summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig3
-rw-r--r--drivers/Makefile9
-rw-r--r--drivers/aiodev/Kconfig15
-rw-r--r--drivers/aiodev/Makefile2
-rw-r--r--drivers/aiodev/am335x_adc.c9
-rw-r--r--drivers/aiodev/core.c24
-rw-r--r--drivers/aiodev/imx7d_adc.c432
-rw-r--r--drivers/aiodev/imx_thermal.c34
-rw-r--r--drivers/aiodev/lm75.c6
-rw-r--r--drivers/aiodev/mc13xxx_adc.c2
-rw-r--r--drivers/aiodev/qoriq_thermal.c11
-rw-r--r--drivers/aiodev/rockchip_saradc.c5
-rw-r--r--drivers/aiodev/st_gyro.c5
-rw-r--r--drivers/aiodev/stm32-adc-core.c9
-rw-r--r--drivers/aiodev/stm32-adc.c37
-rw-r--r--drivers/aiodev/vf610_adc.c620
-rw-r--r--drivers/amba/bus.c8
-rw-r--r--drivers/ata/ahci.c10
-rw-r--r--drivers/ata/ahci.h4
-rw-r--r--drivers/ata/disk_ata_drive.c12
-rw-r--r--drivers/ata/ide-sff.c8
-rw-r--r--drivers/ata/intf_platform_ide.c7
-rw-r--r--drivers/ata/pata-imx.c9
-rw-r--r--drivers/ata/sata-imx.c9
-rw-r--r--drivers/ata/sata_mv.c5
-rw-r--r--drivers/base/Kconfig7
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/bus.c10
-rw-r--r--drivers/base/driver.c298
-rw-r--r--drivers/base/featctrl.c15
-rw-r--r--drivers/base/platform.c13
-rw-r--r--drivers/base/power.c281
-rw-r--r--drivers/base/regmap/Kconfig14
-rw-r--r--drivers/base/regmap/Makefile5
-rw-r--r--drivers/base/regmap/internal.h39
-rw-r--r--drivers/base/regmap/regmap-fmt.c574
-rw-r--r--drivers/base/regmap/regmap-i2c.c52
-rw-r--r--drivers/base/regmap/regmap-mmio.c20
-rw-r--r--drivers/base/regmap/regmap-multi.c104
-rw-r--r--drivers/base/regmap/regmap-spi.c42
-rw-r--r--drivers/base/regmap/regmap.c139
-rw-r--r--drivers/base/resource.c32
-rw-r--r--drivers/base/soc.c123
-rw-r--r--drivers/block/Kconfig2
-rw-r--r--drivers/block/efi-block-io.c51
-rw-r--r--drivers/block/virtio_blk.c10
-rw-r--r--drivers/bus/Kconfig2
-rw-r--r--drivers/bus/acpi.c47
-rw-r--r--drivers/bus/imx-weim.c18
-rw-r--r--drivers/bus/mvebu-mbus.c3
-rw-r--r--drivers/bus/omap-gpmc.c30
-rw-r--r--drivers/bus/ti-sysc.c11
-rw-r--r--drivers/clk/Kconfig46
-rw-r--r--drivers/clk/Makefile7
-rw-r--r--drivers/clk/at91/at91rm9200.c47
-rw-r--r--drivers/clk/at91/at91sam9260.c68
-rw-r--r--drivers/clk/at91/at91sam9g45.c62
-rw-r--r--drivers/clk/at91/at91sam9n12.c71
-rw-r--r--drivers/clk/at91/at91sam9rl.c47
-rw-r--r--drivers/clk/at91/at91sam9x5.c84
-rw-r--r--drivers/clk/at91/clk-audio-pll.c91
-rw-r--r--drivers/clk/at91/clk-generated.c112
-rw-r--r--drivers/clk/at91/clk-h32mx.c34
-rw-r--r--drivers/clk/at91/clk-i2s-mux.c35
-rw-r--r--drivers/clk/at91/clk-main.c237
-rw-r--r--drivers/clk/at91/clk-master.c422
-rw-r--r--drivers/clk/at91/clk-peripheral.c161
-rw-r--r--drivers/clk/at91/clk-pll.c70
-rw-r--r--drivers/clk/at91/clk-plldiv.c52
-rw-r--r--drivers/clk/at91/clk-programmable.c62
-rw-r--r--drivers/clk/at91/clk-sam9x60-pll.c733
-rw-r--r--drivers/clk/at91/clk-slow.c43
-rw-r--r--drivers/clk/at91/clk-smd.c56
-rw-r--r--drivers/clk/at91/clk-system.c64
-rw-r--r--drivers/clk/at91/clk-usb.c143
-rw-r--r--drivers/clk/at91/clk-utmi.c180
-rw-r--r--drivers/clk/at91/pmc.c182
-rw-r--r--drivers/clk/at91/pmc.h166
-rw-r--r--drivers/clk/at91/sam9x60.c147
-rw-r--r--drivers/clk/at91/sama5d2.c138
-rw-r--r--drivers/clk/at91/sama5d3.c80
-rw-r--r--drivers/clk/at91/sama5d4.c86
-rw-r--r--drivers/clk/at91/sama7g5.c1133
-rw-r--r--drivers/clk/at91/sckc.c245
-rw-r--r--drivers/clk/bcm/clk-bcm2835-aux.c9
-rw-r--r--drivers/clk/clk-ar933x.c7
-rw-r--r--drivers/clk/clk-ar9344.c7
-rw-r--r--drivers/clk/clk-bulk.c15
-rw-r--r--drivers/clk/clk-composite.c28
-rw-r--r--drivers/clk/clk-conf.c16
-rw-r--r--drivers/clk/clk-divider.c87
-rw-r--r--drivers/clk/clk-fixed-factor.c16
-rw-r--r--drivers/clk/clk-fixed.c2
-rw-r--r--drivers/clk/clk-gate.c8
-rw-r--r--drivers/clk/clk-gpio.c21
-rw-r--r--drivers/clk/clk-mux.c26
-rw-r--r--drivers/clk/clk-qoric.c94
-rw-r--r--drivers/clk/clk-rpi.c17
-rw-r--r--drivers/clk/clk-scmi.c103
-rw-r--r--drivers/clk/clk-stm32f4.c26
-rw-r--r--drivers/clk/clk-stm32mp1.c53
-rw-r--r--drivers/clk/clk.c263
-rw-r--r--drivers/clk/clkdev.c16
-rw-r--r--drivers/clk/imx/Makefile1
-rw-r--r--drivers/clk/imx/clk-composite-8m.c26
-rw-r--r--drivers/clk/imx/clk-composite-93.c216
-rw-r--r--drivers/clk/imx/clk-fracn-gppll.c297
-rw-r--r--drivers/clk/imx/clk-gate-93.c186
-rw-r--r--drivers/clk/imx/clk-imx1.c7
-rw-r--r--drivers/clk/imx/clk-imx21.c7
-rw-r--r--drivers/clk/imx/clk-imx25.c7
-rw-r--r--drivers/clk/imx/clk-imx27.c14
-rw-r--r--drivers/clk/imx/clk-imx31.c7
-rw-r--r--drivers/clk/imx/clk-imx35.c7
-rw-r--r--drivers/clk/imx/clk-imx5.c43
-rw-r--r--drivers/clk/imx/clk-imx6.c15
-rw-r--r--drivers/clk/imx/clk-imx6sl.c13
-rw-r--r--drivers/clk/imx/clk-imx6sx.c13
-rw-r--r--drivers/clk/imx/clk-imx6ul.c13
-rw-r--r--drivers/clk/imx/clk-imx7.c40
-rw-r--r--drivers/clk/imx/clk-imx8mp.c97
-rw-r--r--drivers/clk/imx/clk-imx93.c331
-rw-r--r--drivers/clk/imx/clk-vf610.c9
-rw-r--r--drivers/clk/imx/clk.h65
-rw-r--r--drivers/clk/loongson/clk-ls1b200.c7
-rw-r--r--drivers/clk/mvebu/common.c14
-rw-r--r--drivers/clk/mvebu/corediv.c7
-rw-r--r--drivers/clk/mxs/clk-imx23.c7
-rw-r--r--drivers/clk/mxs/clk-imx28.c14
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-inverter.c2
-rw-r--r--drivers/clk/rockchip/clk-muxgrf.c2
-rw-r--r--drivers/clk/rockchip/clk-pll.c240
-rw-r--r--drivers/clk/rockchip/clk-rk3399.c7
-rw-r--r--drivers/clk/rockchip/clk-rk3568.c17
-rw-r--r--drivers/clk/rockchip/clk-rk3588.c2533
-rw-r--r--drivers/clk/rockchip/clk.c22
-rw-r--r--drivers/clk/rockchip/clk.h147
-rw-r--r--drivers/clk/rockchip/rst-rk3588.c855
-rw-r--r--drivers/clk/rockchip/softrst.c34
-rw-r--r--drivers/clk/sifive/sifive-prci.c11
-rw-r--r--drivers/clk/socfpga/clk-gate-a10.c6
-rw-r--r--drivers/clk/socfpga/clk.c10
-rw-r--r--drivers/clk/starfive/jh7100-clkgen.c9
-rw-r--r--drivers/clk/stm32/Makefile1
-rw-r--r--drivers/clk/stm32/clk-stm32-core.c680
-rw-r--r--drivers/clk/stm32/clk-stm32-core.h188
-rw-r--r--drivers/clk/stm32/clk-stm32mp13.c1611
-rw-r--r--drivers/clk/stm32/reset-stm32.c122
-rw-r--r--drivers/clk/stm32/reset-stm32.h8
-rw-r--r--drivers/clk/stm32/stm32mp13_rcc.h1748
-rw-r--r--drivers/clk/tegra/clk-pll.c2
-rw-r--r--drivers/clk/tegra/clk-tegra124.c15
-rw-r--r--drivers/clk/tegra/clk-tegra20.c13
-rw-r--r--drivers/clk/tegra/clk-tegra30.c15
-rw-r--r--drivers/clk/tegra/clk.c2
-rw-r--r--drivers/clk/ti-sci-clk.c630
-rw-r--r--drivers/clk/zynq/clkc.c15
-rw-r--r--drivers/clk/zynqmp/clk-divider-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/clk-gate-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/clk-mux-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/clk-pll-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/clkc.c13
-rw-r--r--drivers/clocksource/Kconfig4
-rw-r--r--drivers/clocksource/arm_architected_timer.c5
-rw-r--r--drivers/clocksource/arm_global_timer.c5
-rw-r--r--drivers/clocksource/arm_smp_twd.c5
-rw-r--r--drivers/clocksource/armv7m_systick.c9
-rw-r--r--drivers/clocksource/bcm2835.c7
-rw-r--r--drivers/clocksource/clps711x.c7
-rw-r--r--drivers/clocksource/digic.c5
-rw-r--r--drivers/clocksource/dw_apb_timer.c7
-rw-r--r--drivers/clocksource/efi.c8
-rw-r--r--drivers/clocksource/efi_x86.c4
-rw-r--r--drivers/clocksource/kvx_timer.c5
-rw-r--r--drivers/clocksource/mvebu.c7
-rw-r--r--drivers/clocksource/nomadik.c4
-rw-r--r--drivers/clocksource/orion.c5
-rw-r--r--drivers/clocksource/rk_timer.c19
-rw-r--r--drivers/clocksource/timer-atmel-pit.c9
-rw-r--r--drivers/clocksource/timer-clint.c5
-rw-r--r--drivers/clocksource/timer-imx-gpt.c5
-rw-r--r--drivers/clocksource/timer-riscv.c4
-rw-r--r--drivers/clocksource/timer-stm32.c5
-rw-r--r--drivers/clocksource/timer-ti-32k.c17
-rw-r--r--drivers/clocksource/timer-ti-dm.c9
-rw-r--r--drivers/clocksource/uemd.c5
-rw-r--r--drivers/crypto/Kconfig7
-rw-r--r--drivers/crypto/Makefile2
-rw-r--r--drivers/crypto/caam/Kconfig3
-rw-r--r--drivers/crypto/caam/Makefile1
-rw-r--r--drivers/crypto/caam/caam-blobgen.c36
-rw-r--r--drivers/crypto/caam/caamrng.c19
-rw-r--r--drivers/crypto/caam/ctrl.c25
-rw-r--r--drivers/crypto/caam/desc.h43
-rw-r--r--drivers/crypto/caam/detect.h19
-rw-r--r--drivers/crypto/caam/error.c16
-rw-r--r--drivers/crypto/caam/error.h2
-rw-r--r--drivers/crypto/caam/intern.h14
-rw-r--r--drivers/crypto/caam/jr.c14
-rw-r--r--drivers/crypto/caam/jr.h4
-rw-r--r--drivers/crypto/caam/pbl-init.c491
-rw-r--r--drivers/crypto/caam/regs.h272
-rw-r--r--drivers/crypto/caam/rng_self_test.c14
-rw-r--r--drivers/crypto/caam/rng_self_test.h3
-rw-r--r--drivers/crypto/imx-scc/Kconfig2
-rw-r--r--drivers/crypto/imx-scc/scc-blobgen.c2
-rw-r--r--drivers/crypto/imx-scc/scc.c13
-rw-r--r--drivers/crypto/imx-scc/scc.h2
-rw-r--r--drivers/ddr/Kconfig2
-rw-r--r--drivers/ddr/Makefile2
-rw-r--r--drivers/ddr/fsl/Makefile4
-rw-r--r--drivers/ddr/fsl/arm_ddr_gen3.c7
-rw-r--r--drivers/ddr/fsl/ctrl_regs.c2
-rw-r--r--drivers/ddr/fsl/ddr1_dimm_params.c319
-rw-r--r--drivers/ddr/fsl/ddr2_dimm_params.c320
-rw-r--r--drivers/ddr/fsl/ddr3_dimm_params.c325
-rw-r--r--drivers/ddr/fsl/ddr4_dimm_params.c352
-rw-r--r--drivers/ddr/fsl/fsl_ddr.h13
-rw-r--r--drivers/ddr/fsl/fsl_ddr_gen4.c7
-rw-r--r--drivers/ddr/fsl/main.c24
-rw-r--r--drivers/ddr/imx/Kconfig16
-rw-r--r--drivers/ddr/imx/Makefile8
-rw-r--r--drivers/ddr/imx/ddrphy_csr.c (renamed from drivers/ddr/imx8m/ddrphy_csr.c)2
-rw-r--r--drivers/ddr/imx/ddrphy_train.c (renamed from drivers/ddr/imx8m/ddrphy_train.c)50
-rw-r--r--drivers/ddr/imx/ddrphy_utils.c97
-rw-r--r--drivers/ddr/imx/helper.c (renamed from drivers/ddr/imx8m/helper.c)33
-rw-r--r--drivers/ddr/imx/imx8m_ddr_init.c (renamed from drivers/ddr/imx8m/ddrphy_utils.c)615
-rw-r--r--drivers/ddr/imx/imx9_ddr_init.c698
-rw-r--r--drivers/ddr/imx8m/Kconfig8
-rw-r--r--drivers/ddr/imx8m/Makefile7
-rw-r--r--drivers/ddr/imx8m/ddr_init.c213
-rw-r--r--drivers/dma/Kconfig13
-rw-r--r--drivers/dma/Makefile4
-rw-r--r--drivers/dma/apbh_dma.c519
-rw-r--r--drivers/dma/debug.c206
-rw-r--r--drivers/dma/debug.h56
-rw-r--r--drivers/dma/map.c64
-rw-r--r--drivers/dma/of_fixups.c40
-rw-r--r--drivers/eeprom/at24.c28
-rw-r--r--drivers/eeprom/at25.c13
-rw-r--r--drivers/efi/Makefile3
-rw-r--r--drivers/efi/efi-device.c253
-rw-r--r--drivers/efi/efi-handle.c43
-rw-r--r--drivers/firmware/Kconfig33
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/altera_serial.c23
-rw-r--r--drivers/firmware/arm_scmi/Kconfig86
-rw-r--r--drivers/firmware/arm_scmi/Makefile16
-rw-r--r--drivers/firmware/arm_scmi/base.c89
-rw-r--r--drivers/firmware/arm_scmi/bus.c353
-rw-r--r--drivers/firmware/arm_scmi/clock.c282
-rw-r--r--drivers/firmware/arm_scmi/common.h325
-rw-r--r--drivers/firmware/arm_scmi/driver.c1625
-rw-r--r--drivers/firmware/arm_scmi/msg.c93
-rw-r--r--drivers/firmware/arm_scmi/optee.c614
-rw-r--r--drivers/firmware/arm_scmi/power.c229
-rw-r--r--drivers/firmware/arm_scmi/protocols.h325
-rw-r--r--drivers/firmware/arm_scmi/reset.c67
-rw-r--r--drivers/firmware/arm_scmi/scmi_pm_domain.c135
-rw-r--r--drivers/firmware/arm_scmi/sensors.c936
-rw-r--r--drivers/firmware/arm_scmi/shmem.c53
-rw-r--r--drivers/firmware/arm_scmi/smc.c94
-rw-r--r--drivers/firmware/arm_scmi/voltage.c208
-rw-r--r--drivers/firmware/qemu_fw_cfg.c320
-rw-r--r--drivers/firmware/socfpga.c19
-rw-r--r--drivers/firmware/ti_sci.c2745
-rw-r--r--drivers/firmware/ti_sci.h1533
-rw-r--r--drivers/firmware/zynqmp-fpga.c28
-rw-r--r--drivers/fpga/fpga-bridge.c8
-rw-r--r--drivers/fpga/socfpga-fpga2sdram-bridge.c20
-rw-r--r--drivers/fpga/socfpga-hps2fpga-bridge.c11
-rw-r--r--drivers/gpio/Kconfig26
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/gpio-74164.c10
-rw-r--r--drivers/gpio/gpio-74xx-mmio.c5
-rw-r--r--drivers/gpio/gpio-ath79.c7
-rw-r--r--drivers/gpio/gpio-clps711x.c7
-rw-r--r--drivers/gpio/gpio-davinci.c77
-rw-r--r--drivers/gpio/gpio-digic.c5
-rw-r--r--drivers/gpio/gpio-dw.c11
-rw-r--r--drivers/gpio/gpio-generic.c19
-rw-r--r--drivers/gpio/gpio-imx.c7
-rw-r--r--drivers/gpio/gpio-intel.c198
-rw-r--r--drivers/gpio/gpio-jz4740.c5
-rw-r--r--drivers/gpio/gpio-latch.c196
-rw-r--r--drivers/gpio/gpio-libftdi1.c13
-rw-r--r--drivers/gpio/gpio-malta-fpga-i2c.c5
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c9
-rw-r--r--drivers/gpio/gpio-mxs.c7
-rw-r--r--drivers/gpio/gpio-omap.c7
-rw-r--r--drivers/gpio/gpio-orion.c9
-rw-r--r--drivers/gpio/gpio-pca953x.c46
-rw-r--r--drivers/gpio/gpio-pcf857x.c7
-rw-r--r--drivers/gpio/gpio-raspberrypi-exp.c7
-rw-r--r--drivers/gpio/gpio-rockchip.c211
-rw-r--r--drivers/gpio/gpio-sifive.c7
-rw-r--r--drivers/gpio/gpio-starfive-vic.c5
-rw-r--r--drivers/gpio/gpio-stmpe.c4
-rw-r--r--drivers/gpio/gpio-sx150x.c7
-rw-r--r--drivers/gpio/gpio-tegra.c5
-rw-r--r--drivers/gpio/gpio-vf610.c115
-rw-r--r--drivers/gpio/gpio-zynq.c7
-rw-r--r--drivers/gpio/gpiolib.c766
-rw-r--r--drivers/hab/hab.c215
-rw-r--r--drivers/hab/hab.h10
-rw-r--r--drivers/hab/habv3.c8
-rw-r--r--drivers/hab/habv4.c249
-rw-r--r--drivers/hw_random/Kconfig60
-rw-r--r--drivers/hw_random/Makefile8
-rw-r--r--drivers/hw_random/atmel-rng.c165
-rw-r--r--drivers/hw_random/bcm2835-rng.c199
-rw-r--r--drivers/hw_random/core.c14
-rw-r--r--drivers/hw_random/dev-random.c4
-rw-r--r--drivers/hw_random/efi-rng.c53
-rw-r--r--drivers/hw_random/iproc-rng200.c220
-rw-r--r--drivers/hw_random/mxc-rngc.c7
-rw-r--r--drivers/hw_random/omap-rng.c436
-rw-r--r--drivers/hw_random/optee-rng.c302
-rw-r--r--drivers/hw_random/rockchip-rng.c259
-rw-r--r--drivers/hw_random/starfive-vic-rng.c7
-rw-r--r--drivers/hw_random/stm32-rng.c7
-rw-r--r--drivers/hw_random/timeriomem-rng.c145
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c2
-rw-r--r--drivers/i2c/busses/Kconfig13
-rw-r--r--drivers/i2c/busses/Makefile2
-rw-r--r--drivers/i2c/busses/i2c-at91.c23
-rw-r--r--drivers/i2c/busses/i2c-bcm283x.c13
-rw-r--r--drivers/i2c/busses/i2c-cadence.c11
-rw-r--r--drivers/i2c/busses/i2c-designware.c9
-rw-r--r--drivers/i2c/busses/i2c-efi.c292
-rw-r--r--drivers/i2c/busses/i2c-gpio.c11
-rw-r--r--drivers/i2c/busses/i2c-imx-early.c36
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c555
-rw-r--r--drivers/i2c/busses/i2c-imx.c42
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c13
-rw-r--r--drivers/i2c/busses/i2c-omap.c15
-rw-r--r--drivers/i2c/busses/i2c-rockchip.c25
-rw-r--r--drivers/i2c/busses/i2c-stm32.c455
-rw-r--r--drivers/i2c/busses/i2c-tegra.c13
-rw-r--r--drivers/i2c/busses/i2c-versatile.c7
-rw-r--r--drivers/i2c/i2c-mux.c10
-rw-r--r--drivers/i2c/i2c.c82
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c8
-rw-r--r--drivers/input/Kconfig11
-rw-r--r--drivers/input/gpio_keys.c46
-rw-r--r--drivers/input/imx_keypad.c12
-rw-r--r--drivers/input/input.c11
-rw-r--r--drivers/input/matrix-keymap.c21
-rw-r--r--drivers/input/qt1070.c6
-rw-r--r--drivers/input/twl6030_pwrbtn.c4
-rw-r--r--drivers/input/usb_kbd.c3
-rw-r--r--drivers/input/virtio_input.c3
-rw-r--r--drivers/led/led-gpio.c14
-rw-r--r--drivers/led/led-pca955x.c7
-rw-r--r--drivers/led/led-pwm.c15
-rw-r--r--drivers/mailbox/Kconfig22
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/mailbox.c92
-rw-r--r--drivers/mailbox/ti-msgmgr.c402
-rw-r--r--drivers/mci/Kconfig30
-rw-r--r--drivers/mci/Makefile3
-rw-r--r--drivers/mci/am654-sdhci.c680
-rw-r--r--drivers/mci/arasan-sdhci.c635
-rw-r--r--drivers/mci/atmel-mci-regs.h4
-rw-r--r--drivers/mci/atmel-sdhci-common.c57
-rw-r--r--drivers/mci/atmel-sdhci-pbl.c4
-rw-r--r--drivers/mci/atmel-sdhci.c9
-rw-r--r--drivers/mci/atmel-sdhci.h2
-rw-r--r--drivers/mci/atmel_mci.c14
-rw-r--r--drivers/mci/atmel_mci_common.c24
-rw-r--r--drivers/mci/atmel_mci_pbl.c3
-rw-r--r--drivers/mci/bcm2835-sdhost.c7
-rw-r--r--drivers/mci/dove-sdhci.c52
-rw-r--r--drivers/mci/dw_mmc.c30
-rw-r--r--drivers/mci/dwcmshc-sdhci.c402
-rw-r--r--drivers/mci/imx-esdhc-common.c14
-rw-r--r--drivers/mci/imx-esdhc-pbl.c275
-rw-r--r--drivers/mci/imx-esdhc.c120
-rw-r--r--drivers/mci/imx-esdhc.h29
-rw-r--r--drivers/mci/imx.c7
-rw-r--r--drivers/mci/mci-bcm2835.c31
-rw-r--r--drivers/mci/mci-bcm2835.h1
-rw-r--r--drivers/mci/mci-core.c714
-rw-r--r--drivers/mci/mci_spi.c22
-rw-r--r--drivers/mci/mmci.c8
-rw-r--r--drivers/mci/mxs.c14
-rw-r--r--drivers/mci/omap_hsmmc.c13
-rw-r--r--drivers/mci/pxamci.c16
-rw-r--r--drivers/mci/rockchip-dwcmshc-sdhci.c130
-rw-r--r--drivers/mci/s3c.c761
-rw-r--r--drivers/mci/sdhci.c476
-rw-r--r--drivers/mci/sdhci.h58
-rw-r--r--drivers/mci/stm32_sdmmc2.c69
-rw-r--r--drivers/mci/tegra-sdmmc.c28
-rw-r--r--drivers/memory/Kconfig25
-rw-r--r--drivers/memory/Makefile2
-rw-r--r--drivers/memory/atmel-ebi.c615
-rw-r--r--drivers/memory/mc-tegra124.c5
-rw-r--r--drivers/memory/stm32-fmc2-ebi.c1136
-rw-r--r--drivers/mfd/Kconfig28
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/act8846.c4
-rw-r--r--drivers/mfd/atmel-flexcom.c9
-rw-r--r--drivers/mfd/atmel-smc.c355
-rw-r--r--drivers/mfd/axp20x-i2c.c8
-rw-r--r--drivers/mfd/axp20x.c36
-rw-r--r--drivers/mfd/core.c5
-rw-r--r--drivers/mfd/da9053.c11
-rw-r--r--drivers/mfd/da9063.c15
-rw-r--r--drivers/mfd/lp3972.c4
-rw-r--r--drivers/mfd/mc13xxx.c13
-rw-r--r--drivers/mfd/mc34704.c5
-rw-r--r--drivers/mfd/mc9sdz60.c4
-rw-r--r--drivers/mfd/pca9450.c127
-rw-r--r--drivers/mfd/rave-sp.c25
-rw-r--r--drivers/mfd/rk808.c10
-rw-r--r--drivers/mfd/rn5t568.c38
-rw-r--r--drivers/mfd/rohm-bd718x7.c140
-rw-r--r--drivers/mfd/stm32-timers.c8
-rw-r--r--drivers/mfd/stmpe-i2c.c10
-rw-r--r--drivers/mfd/stpmic1.c9
-rw-r--r--drivers/mfd/superio.c6
-rw-r--r--drivers/mfd/syscon.c24
-rw-r--r--drivers/mfd/twl4030.c4
-rw-r--r--drivers/mfd/twl6030.c4
-rw-r--r--drivers/misc/jtag.c8
-rw-r--r--drivers/misc/mem.c6
-rw-r--r--drivers/misc/sram.c5
-rw-r--r--drivers/misc/starfive-pwrseq.c7
-rw-r--r--drivers/misc/state.c7
-rw-r--r--drivers/misc/storage-by-uuid.c18
-rw-r--r--drivers/misc/ubootvar.c22
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/core.c15
-rw-r--r--drivers/mtd/devices/docg3.c14
-rw-r--r--drivers/mtd/devices/docg3.h2
-rw-r--r--drivers/mtd/devices/m25p80.c28
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c9
-rw-r--r--drivers/mtd/devices/mtdram.c9
-rw-r--r--drivers/mtd/mtdconcat.c4
-rw-r--r--drivers/mtd/nand/Kconfig167
-rw-r--r--drivers/mtd/nand/Makefile29
-rw-r--r--drivers/mtd/nand/core.c133
-rw-r--r--drivers/mtd/nand/ecc-sw-bch.c406
-rw-r--r--drivers/mtd/nand/ecc-sw-hamming.c660
-rw-r--r--drivers/mtd/nand/ecc.c697
-rw-r--r--drivers/mtd/nand/nand_bch.c219
-rw-r--r--drivers/mtd/nand/nand_imx.c1493
-rw-r--r--drivers/mtd/nand/nand_s3c24xx.c649
-rw-r--r--drivers/mtd/nand/raw/Kconfig175
-rw-r--r--drivers/mtd/nand/raw/Makefile23
-rw-r--r--drivers/mtd/nand/raw/atmel/Makefile3
-rw-r--r--drivers/mtd/nand/raw/atmel/atmel_nand_ecc.h (renamed from drivers/mtd/nand/atmel_nand_ecc.h)0
-rw-r--r--drivers/mtd/nand/raw/atmel/legacy.c (renamed from drivers/mtd/nand/atmel_nand.c)42
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c2049
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.c993
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.h70
-rw-r--r--drivers/mtd/nand/raw/denali.h (renamed from drivers/mtd/nand/denali.h)2
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc.h (renamed from drivers/mtd/nand/fsl_ifc.h)0
-rw-r--r--drivers/mtd/nand/raw/internals.h (renamed from drivers/mtd/nand/internals.h)5
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.c1750
-rw-r--r--drivers/mtd/nand/raw/nand_amd.c (renamed from drivers/mtd/nand/nand_amd.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_base.c (renamed from drivers/mtd/nand/nand_base.c)1649
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c (renamed from drivers/mtd/nand/nand_bbt.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_denali.c (renamed from drivers/mtd/nand/nand_denali.c)3
-rw-r--r--drivers/mtd/nand/raw/nand_denali_dt.c (renamed from drivers/mtd/nand/nand_denali_dt.c)78
-rw-r--r--drivers/mtd/nand/raw/nand_ecc.c (renamed from drivers/mtd/nand/nand_ecc.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_esmt.c (renamed from drivers/mtd/nand/nand_esmt.c)17
-rw-r--r--drivers/mtd/nand/raw/nand_fsl_ifc.c (renamed from drivers/mtd/nand/nand_fsl_ifc.c)19
-rw-r--r--drivers/mtd/nand/raw/nand_hynix.c (renamed from drivers/mtd/nand/nand_hynix.c)59
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c (renamed from drivers/mtd/nand/nand_ids.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_jedec.c (renamed from drivers/mtd/nand/nand_jedec.c)4
-rw-r--r--drivers/mtd/nand/raw/nand_legacy.c (renamed from drivers/mtd/nand/nand_legacy.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_macronix.c (renamed from drivers/mtd/nand/nand_macronix.c)42
-rw-r--r--drivers/mtd/nand/raw/nand_micron.c (renamed from drivers/mtd/nand/nand_micron.c)24
-rw-r--r--drivers/mtd/nand/raw/nand_mrvl_nfc.c (renamed from drivers/mtd/nand/nand_mrvl_nfc.c)21
-rw-r--r--drivers/mtd/nand/raw/nand_mxs.c (renamed from drivers/mtd/nand/nand_mxs.c)587
-rw-r--r--drivers/mtd/nand/raw/nand_omap_bch_decoder.c (renamed from drivers/mtd/nand/nand_omap_bch_decoder.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_omap_bch_decoder.h (renamed from drivers/mtd/nand/nand_omap_bch_decoder.h)0
-rw-r--r--drivers/mtd/nand/raw/nand_omap_gpmc.c (renamed from drivers/mtd/nand/nand_omap_gpmc.c)19
-rw-r--r--drivers/mtd/nand/raw/nand_onfi.c (renamed from drivers/mtd/nand/nand_onfi.c)32
-rw-r--r--drivers/mtd/nand/raw/nand_orion.c (renamed from drivers/mtd/nand/nand_orion.c)10
-rw-r--r--drivers/mtd/nand/raw/nand_samsung.c (renamed from drivers/mtd/nand/nand_samsung.c)22
-rw-r--r--drivers/mtd/nand/raw/nand_timings.c (renamed from drivers/mtd/nand/nand_timings.c)370
-rw-r--r--drivers/mtd/nand/raw/nand_toshiba.c (renamed from drivers/mtd/nand/nand_toshiba.c)27
-rw-r--r--drivers/mtd/nand/raw/nomadik_nand.c (renamed from drivers/mtd/nand/nomadik_nand.c)10
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c (renamed from drivers/mtd/nand/omap_elm.c)7
-rw-r--r--drivers/mtd/nand/raw/stm32_fmc2_nand.c1354
-rw-r--r--drivers/mtd/nor/cfi_flash.c18
-rw-r--r--drivers/mtd/nor/cfi_flash.h2
-rw-r--r--drivers/mtd/partition.c4
-rw-r--r--drivers/mtd/peb.c8
-rw-r--r--drivers/mtd/spi-nor/Kconfig6
-rw-r--r--drivers/mtd/spi-nor/Makefile1
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c31
-rw-r--r--drivers/mtd/spi-nor/dw-ospi-nor.c929
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c29
-rw-r--r--drivers/mtd/ubi/ubi.h4
-rw-r--r--drivers/net/Kconfig50
-rw-r--r--drivers/net/Makefile7
-rw-r--r--drivers/net/ag71xx.c23
-rw-r--r--drivers/net/altera_tse.c563
-rw-r--r--drivers/net/altera_tse.h296
-rw-r--r--drivers/net/ar231x.c4
-rw-r--r--drivers/net/arc_emac.c15
-rw-r--r--drivers/net/at91_ether.c23
-rw-r--r--drivers/net/bcmgenet.c56
-rw-r--r--drivers/net/cpsw.c101
-rw-r--r--drivers/net/cs8900.c13
-rw-r--r--drivers/net/davinci_emac.c28
-rw-r--r--drivers/net/designware.c24
-rw-r--r--drivers/net/designware.h4
-rw-r--r--drivers/net/designware_eqos.c171
-rw-r--r--drivers/net/designware_eqos.h15
-rw-r--r--drivers/net/designware_generic.c5
-rw-r--r--drivers/net/designware_imx.c269
-rw-r--r--drivers/net/designware_rockchip.c21
-rw-r--r--drivers/net/designware_socfpga.c17
-rw-r--r--drivers/net/designware_starfive.c9
-rw-r--r--drivers/net/designware_stm32.c16
-rw-r--r--drivers/net/designware_tegra186.c11
-rw-r--r--drivers/net/dm9k.c29
-rw-r--r--drivers/net/dsa.c89
-rw-r--r--drivers/net/e1000/e1000.h6
-rw-r--r--drivers/net/e1000/eeprom.c12
-rw-r--r--drivers/net/e1000/main.c16
-rw-r--r--drivers/net/e1000/mtd.c4
-rw-r--r--drivers/net/efi-snp.c28
-rw-r--r--drivers/net/enc28j60.c13
-rw-r--r--drivers/net/ep93xx.c22
-rw-r--r--drivers/net/ethoc.c15
-rw-r--r--drivers/net/fec_imx.c66
-rw-r--r--drivers/net/fec_imx.h3
-rw-r--r--drivers/net/fec_mpc5200.c9
-rw-r--r--drivers/net/fsl-fman.c80
-rw-r--r--drivers/net/fsl_enetc.c598
-rw-r--r--drivers/net/fsl_enetc.h252
-rw-r--r--drivers/net/fsl_enetc_mdio.c127
-rw-r--r--drivers/net/gianfar.c42
-rw-r--r--drivers/net/gianfar.h8
-rw-r--r--drivers/net/ks8851_mll.c27
-rw-r--r--drivers/net/ksz8864rmn.c4
-rw-r--r--drivers/net/ksz8873.c450
-rw-r--r--drivers/net/ksz9477.c233
-rw-r--r--drivers/net/ksz_common.h154
-rw-r--r--drivers/net/liteeth.c17
-rw-r--r--drivers/net/macb.c170
-rw-r--r--drivers/net/mvneta.c19
-rw-r--r--drivers/net/orion-gbe.c27
-rw-r--r--drivers/net/phy/Kconfig11
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/ar8327.c4
-rw-r--r--drivers/net/phy/at803x.c49
-rw-r--r--drivers/net/phy/dp83867.c566
-rw-r--r--drivers/net/phy/dp83tg720.c103
-rw-r--r--drivers/net/phy/marvell.c4
-rw-r--r--drivers/net/phy/mdio-bitbang.c8
-rw-r--r--drivers/net/phy/mdio-gpio.c25
-rw-r--r--drivers/net/phy/mdio-mux-gpio.c11
-rw-r--r--drivers/net/phy/mdio-mux.c4
-rw-r--r--drivers/net/phy/mdio-mvebu.c9
-rw-r--r--drivers/net/phy/mdio_bus.c144
-rw-r--r--drivers/net/phy/micrel.c264
-rw-r--r--drivers/net/phy/motorcomm.c129
-rw-r--r--drivers/net/phy/mv88e6xxx/chip.c17
-rw-r--r--drivers/net/phy/mv88e6xxx/chip.h2
-rw-r--r--drivers/net/phy/mv88e6xxx/port.c10
-rw-r--r--drivers/net/phy/phy-core.c10
-rw-r--r--drivers/net/phy/phy.c177
-rw-r--r--drivers/net/phy/realtek.c15
-rw-r--r--drivers/net/r8169.h79
-rw-r--r--drivers/net/r8169_firmware.c237
-rw-r--r--drivers/net/r8169_firmware.h36
-rw-r--r--drivers/net/r8169_main.c3215
-rw-r--r--drivers/net/r8169_phy_config.c1156
-rw-r--r--drivers/net/realtek-dsa/Kconfig60
-rw-r--r--drivers/net/realtek-dsa/Makefile7
-rw-r--r--drivers/net/realtek-dsa/dsa_priv.h77
-rw-r--r--drivers/net/realtek-dsa/realtek-mdio.c287
-rw-r--r--drivers/net/realtek-dsa/realtek-smi.c504
-rw-r--r--drivers/net/realtek-dsa/realtek.h104
-rw-r--r--drivers/net/realtek-dsa/rtl8365mb.c1255
-rw-r--r--drivers/net/realtek-dsa/rtl8366rb.c1106
-rw-r--r--drivers/net/realtek-dsa/tag_rtl4_a.c104
-rw-r--r--drivers/net/realtek-dsa/tag_rtl8_4.c206
-rw-r--r--drivers/net/realtek-dsa/tagger.c44
-rw-r--r--drivers/net/rtl8139.c12
-rw-r--r--drivers/net/rtl8169.c561
-rw-r--r--drivers/net/sja1105.c75
-rw-r--r--drivers/net/smc91111.c33
-rw-r--r--drivers/net/smc911x.c23
-rw-r--r--drivers/net/tap.c11
-rw-r--r--drivers/net/usb/Kconfig3
-rw-r--r--drivers/net/usb/asix.c32
-rw-r--r--drivers/net/usb/ax88179_178a.c14
-rw-r--r--drivers/net/usb/r8152.c4
-rw-r--r--drivers/net/usb/r8152_fw.c4
-rw-r--r--drivers/net/usb/smsc95xx.c85
-rw-r--r--drivers/net/usb/usbnet.c4
-rw-r--r--drivers/nvme/host/core.c3
-rw-r--r--drivers/nvme/host/nvme.h4
-rw-r--r--drivers/nvme/host/pci.c2
-rw-r--r--drivers/nvmem/Kconfig20
-rw-r--r--drivers/nvmem/Makefile3
-rw-r--r--drivers/nvmem/bsec.c150
-rw-r--r--drivers/nvmem/core.c94
-rw-r--r--drivers/nvmem/eeprom_93xx46.c18
-rw-r--r--drivers/nvmem/imx-ocotp-ele.c241
-rw-r--r--drivers/nvmem/kvx-otp-nv.c11
-rw-r--r--drivers/nvmem/ocotp.c322
-rw-r--r--drivers/nvmem/partition.c8
-rw-r--r--drivers/nvmem/rave-sp-eeprom.c17
-rw-r--r--drivers/nvmem/regmap.c38
-rw-r--r--drivers/nvmem/rmem.c13
-rw-r--r--drivers/nvmem/rockchip-otp.c448
-rw-r--r--drivers/nvmem/snvs_lpgpr.c23
-rw-r--r--drivers/nvmem/starfive-otp.c23
-rw-r--r--drivers/nvmem/stm32-bsec-optee-ta.c298
-rw-r--r--drivers/nvmem/stm32-bsec-optee-ta.h85
-rw-r--r--drivers/of/Kconfig6
-rw-r--r--drivers/of/address.c16
-rw-r--r--drivers/of/barebox.c13
-rw-r--r--drivers/of/base.c416
-rw-r--r--drivers/of/device.c10
-rw-r--r--drivers/of/fdt.c295
-rw-r--r--drivers/of/of_firmware.c3
-rw-r--r--drivers/of/of_gpio.c73
-rw-r--r--drivers/of/of_mtd.c1
-rw-r--r--drivers/of/of_path.c97
-rw-r--r--drivers/of/overlay.c21
-rw-r--r--drivers/of/partition.c85
-rw-r--r--drivers/of/platform.c177
-rw-r--r--drivers/of/reserved-mem.c4
-rw-r--r--drivers/pci/Kconfig11
-rw-r--r--drivers/pci/Makefile4
-rw-r--r--drivers/pci/bus.c36
-rw-r--r--drivers/pci/host-bridge.c76
-rw-r--r--drivers/pci/of.c102
-rw-r--r--drivers/pci/pci-ecam-generic.c11
-rw-r--r--drivers/pci/pci-efi.c10
-rw-r--r--drivers/pci/pci-imx6.c29
-rw-r--r--drivers/pci/pci-layerscape.c24
-rw-r--r--drivers/pci/pci-mvebu.c16
-rw-r--r--drivers/pci/pci-tegra.c19
-rw-r--r--drivers/pci/pci.c322
-rw-r--r--drivers/pci/pcie-designware-host.c101
-rw-r--r--drivers/pci/pcie-designware.c12
-rw-r--r--drivers/pci/pcie-designware.h11
-rw-r--r--drivers/pci/pcie-dw-rockchip.c302
-rw-r--r--drivers/phy/Kconfig1
-rw-r--r--drivers/phy/freescale/Kconfig8
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c9
-rw-r--r--drivers/phy/phy-core.c59
-rw-r--r--drivers/phy/phy-stm32-usbphyc.c353
-rw-r--r--drivers/phy/rockchip/Kconfig5
-rw-r--r--drivers/phy/rockchip/Makefile1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c35
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c306
-rw-r--r--drivers/phy/rockchip/phy-rockchip-snps-pcie3.c317
-rw-r--r--drivers/phy/usb-nop-xceiv.c11
-rw-r--r--drivers/pinctrl/imx-iomux-v1.c19
-rw-r--r--drivers/pinctrl/imx-iomux-v2.c7
-rw-r--r--drivers/pinctrl/imx-iomux-v3.c20
-rw-r--r--drivers/pinctrl/mvebu/armada-370.c7
-rw-r--r--drivers/pinctrl/mvebu/armada-xp.c7
-rw-r--r--drivers/pinctrl/mvebu/common.c5
-rw-r--r--drivers/pinctrl/mvebu/common.h2
-rw-r--r--drivers/pinctrl/mvebu/dove.c7
-rw-r--r--drivers/pinctrl/mvebu/kirkwood.c7
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c14
-rw-r--r--drivers/pinctrl/pinctrl-at91.c48
-rw-r--r--drivers/pinctrl/pinctrl-bcm2835.c5
-rw-r--r--drivers/pinctrl/pinctrl-mxs.c11
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c3121
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.h452
-rw-r--r--drivers/pinctrl/pinctrl-single.c12
-rw-r--r--drivers/pinctrl/pinctrl-stm32.c47
-rw-r--r--drivers/pinctrl/pinctrl-tegra-xusb.c9
-rw-r--r--drivers/pinctrl/pinctrl-tegra20.c7
-rw-r--r--drivers/pinctrl/pinctrl-tegra30.c7
-rw-r--r--drivers/pinctrl/pinctrl-vf610.c10
-rw-r--r--drivers/pinctrl/pinctrl.c14
-rw-r--r--drivers/pmdomain/Kconfig7
-rw-r--r--drivers/pmdomain/Makefile3
-rw-r--r--drivers/pmdomain/imx/Kconfig16
-rw-r--r--drivers/pmdomain/imx/Makefile3
-rw-r--r--drivers/pmdomain/imx/gpcv2.c (renamed from drivers/soc/imx/gpcv2.c)51
-rw-r--r--drivers/pmdomain/imx/imx8mp-blk-ctrl.c475
-rw-r--r--drivers/pmdomain/ti/Kconfig8
-rw-r--r--drivers/pmdomain/ti/Makefile2
-rw-r--r--drivers/pmdomain/ti/ti_sci_pm_domains.c196
-rw-r--r--drivers/power/reset/gpio-poweroff.c21
-rw-r--r--drivers/power/reset/gpio-restart.c32
-rw-r--r--drivers/power/reset/htif-poweroff.c5
-rw-r--r--drivers/power/reset/nvmem-reboot-mode.c13
-rw-r--r--drivers/power/reset/reboot-mode.c15
-rw-r--r--drivers/power/reset/stm32-reboot.c7
-rw-r--r--drivers/power/reset/syscon-poweroff.c14
-rw-r--r--drivers/power/reset/syscon-reboot-mode.c11
-rw-r--r--drivers/power/reset/syscon-reboot.c17
-rw-r--r--drivers/pwm/Kconfig7
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/core.c60
-rw-r--r--drivers/pwm/pwm-atmel.c30
-rw-r--r--drivers/pwm/pwm-imx.c69
-rw-r--r--drivers/pwm/pwm-mxs.c26
-rw-r--r--drivers/pwm/pwm-rockchip.c348
-rw-r--r--drivers/pwm/pwm-stm32.c25
-rw-r--r--drivers/pwm/pxa_pwm.c31
-rw-r--r--drivers/regulator/Kconfig2
-rw-r--r--drivers/regulator/anatop-regulator.c11
-rw-r--r--drivers/regulator/bcm2835.c8
-rw-r--r--drivers/regulator/core.c421
-rw-r--r--drivers/regulator/fixed.c38
-rw-r--r--drivers/regulator/helpers.c2
-rw-r--r--drivers/regulator/of_regulator.c2
-rw-r--r--drivers/regulator/pfuze.c12
-rw-r--r--drivers/regulator/rk808-regulator.c46
-rw-r--r--drivers/regulator/scmi-regulator.c33
-rw-r--r--drivers/regulator/stm32-pwr.c7
-rw-r--r--drivers/regulator/stm32-vrefbuf.c11
-rw-r--r--drivers/regulator/stpmic1_regulator.c22
-rw-r--r--drivers/remoteproc/Kconfig4
-rw-r--r--drivers/remoteproc/imx_rproc.c30
-rw-r--r--drivers/remoteproc/remoteproc_core.c12
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c2
-rw-r--r--drivers/remoteproc/stm32_rproc.c87
-rw-r--r--drivers/reset/core.c157
-rw-r--r--drivers/reset/reset-imx7.c11
-rw-r--r--drivers/reset/reset-scmi.c6
-rw-r--r--drivers/reset/reset-simple.c7
-rw-r--r--drivers/reset/reset-socfpga.c7
-rw-r--r--drivers/reset/reset-starfive-vic.c7
-rw-r--r--drivers/rtc/Kconfig1
-rw-r--r--drivers/rtc/class.c2
-rw-r--r--drivers/rtc/rtc-abracon.c4
-rw-r--r--drivers/rtc/rtc-ds1307.c10
-rw-r--r--drivers/rtc/rtc-imxdi.c15
-rw-r--r--drivers/rtc/rtc-jz4740.c5
-rw-r--r--drivers/rtc/rtc-pcf85363.c8
-rw-r--r--drivers/serial/Kconfig36
-rw-r--r--drivers/serial/Makefile4
-rw-r--r--drivers/serial/arm_dcc.c6
-rw-r--r--drivers/serial/atmel.c7
-rw-r--r--drivers/serial/efi-stdio.c27
-rw-r--r--drivers/serial/linux_console.c10
-rw-r--r--drivers/serial/serial_altera.c94
-rw-r--r--drivers/serial/serial_altera_jtag.c99
-rw-r--r--drivers/serial/serial_ar933x.c5
-rw-r--r--drivers/serial/serial_auart.c7
-rw-r--r--drivers/serial/serial_cadence.c5
-rw-r--r--drivers/serial/serial_clps711x.c10
-rw-r--r--drivers/serial/serial_digic.c7
-rw-r--r--drivers/serial/serial_imx.c15
-rw-r--r--drivers/serial/serial_litex.c5
-rw-r--r--drivers/serial/serial_lpuart.c9
-rw-r--r--drivers/serial/serial_lpuart32.c188
-rw-r--r--drivers/serial/serial_mpc5xxx.c14
-rw-r--r--drivers/serial/serial_ns16550.c171
-rw-r--r--drivers/serial/serial_ns16550.h7
-rw-r--r--drivers/serial/serial_ns16550_pci.c2
-rw-r--r--drivers/serial/serial_omap4_usbboot.c12
-rw-r--r--drivers/serial/serial_pl010.c4
-rw-r--r--drivers/serial/serial_pxa.c6
-rw-r--r--drivers/serial/serial_s3c.c198
-rw-r--r--drivers/serial/serial_sbi.c4
-rw-r--r--drivers/serial/serial_sifive.c8
-rw-r--r--drivers/serial/serial_stm32.c13
-rw-r--r--drivers/serial/stm-serial.c6
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/imx/Kconfig13
-rw-r--r--drivers/soc/imx/Makefile2
-rw-r--r--drivers/soc/imx/imx8m-featctrl.c50
-rw-r--r--drivers/soc/imx/soc-imx8m.c296
-rw-r--r--drivers/soc/kvx/kvx_socinfo.c42
-rw-r--r--drivers/soc/rockchip/Kconfig17
-rw-r--r--drivers/soc/rockchip/Makefile6
-rw-r--r--drivers/soc/rockchip/io-domain.c222
-rw-r--r--drivers/soc/sifive/sifive_l2_cache.c8
-rw-r--r--drivers/soc/starfive/jh7100_dma.c2
-rw-r--r--drivers/sound/gpio-beeper.c23
-rw-r--r--drivers/sound/pwm-beeper.c38
-rw-r--r--drivers/sound/sdl.c5
-rw-r--r--drivers/spi/Kconfig6
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/altera_spi.c236
-rw-r--r--drivers/spi/ath79_spi.c11
-rw-r--r--drivers/spi/atmel-quadspi.c12
-rw-r--r--drivers/spi/atmel_spi.c17
-rw-r--r--drivers/spi/dspi_spi.c19
-rw-r--r--drivers/spi/gpio_spi.c18
-rw-r--r--drivers/spi/imx_spi.c19
-rw-r--r--drivers/spi/litex_spiflash.c9
-rw-r--r--drivers/spi/mvebu_spi.c7
-rw-r--r--drivers/spi/mxs_spi.c9
-rw-r--r--drivers/spi/omap3_spi.c12
-rw-r--r--drivers/spi/spi-fsl-dspi.c15
-rw-r--r--drivers/spi/spi-fsl-qspi.c11
-rw-r--r--drivers/spi/spi-nxp-fspi.c31
-rw-r--r--drivers/spi/spi-sifive.c11
-rw-r--r--drivers/spi/spi.c62
-rw-r--r--drivers/spi/stm32_spi.c65
-rw-r--r--drivers/spi/zynq_qspi.c9
-rw-r--r--drivers/tee/Kconfig17
-rw-r--r--drivers/tee/Makefile5
-rw-r--r--drivers/tee/optee/Kconfig29
-rw-r--r--drivers/tee/optee/Makefile8
-rw-r--r--drivers/tee/optee/call.c239
-rw-r--r--drivers/tee/optee/core.c68
-rw-r--r--drivers/tee/optee/device.c174
-rw-r--r--drivers/tee/optee/of_fixup.c58
-rw-r--r--drivers/tee/optee/optee_msg.h295
-rw-r--r--drivers/tee/optee/optee_private.h179
-rw-r--r--drivers/tee/optee/optee_smc.h473
-rw-r--r--drivers/tee/optee/rpc.c16
-rw-r--r--drivers/tee/optee/smc_abi.c748
-rw-r--r--drivers/tee/tee_core.c788
-rw-r--r--drivers/tee/tee_private.h50
-rw-r--r--drivers/tee/tee_shm.c338
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/core/common.c2
-rw-r--r--drivers/usb/core/hub.c57
-rw-r--r--drivers/usb/core/of.c4
-rw-r--r--drivers/usb/core/usb.c54
-rw-r--r--drivers/usb/dwc2/core.c29
-rw-r--r--drivers/usb/dwc2/core.h2
-rw-r--r--drivers/usb/dwc2/dwc2.c13
-rw-r--r--drivers/usb/dwc2/dwc2.h7
-rw-r--r--drivers/usb/dwc2/gadget.c46
-rw-r--r--drivers/usb/dwc2/host.c8
-rw-r--r--drivers/usb/dwc3/core.c753
-rw-r--r--drivers/usb/dwc3/core.h513
-rw-r--r--drivers/usb/dwc3/debug.h356
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c17
-rw-r--r--drivers/usb/dwc3/ep0.c483
-rw-r--r--drivers/usb/dwc3/gadget.c2748
-rw-r--r--drivers/usb/dwc3/gadget.h104
-rw-r--r--drivers/usb/dwc3/host.c14
-rw-r--r--drivers/usb/gadget/Kconfig4
-rw-r--r--drivers/usb/gadget/Makefile14
-rw-r--r--drivers/usb/gadget/composite.c1087
-rw-r--r--drivers/usb/gadget/config.c14
-rw-r--r--drivers/usb/gadget/epautoconf.c46
-rw-r--r--drivers/usb/gadget/fsl_udc_pbl.c221
-rw-r--r--drivers/usb/gadget/function/Makefile6
-rw-r--r--drivers/usb/gadget/function/dfu.c (renamed from drivers/usb/gadget/dfu.c)39
-rw-r--r--drivers/usb/gadget/function/f_acm.c (renamed from drivers/usb/gadget/f_acm.c)7
-rw-r--r--drivers/usb/gadget/function/f_fastboot.c (renamed from drivers/usb/gadget/f_fastboot.c)94
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c (renamed from drivers/usb/gadget/f_mass_storage.c)46
-rw-r--r--drivers/usb/gadget/function/f_serial.c (renamed from drivers/usb/gadget/f_serial.c)3
-rw-r--r--drivers/usb/gadget/function/storage_common.c (renamed from drivers/usb/gadget/storage_common.c)42
-rw-r--r--drivers/usb/gadget/function/storage_common.h (renamed from drivers/usb/gadget/storage_common.h)12
-rw-r--r--drivers/usb/gadget/function/u_serial.c (renamed from drivers/usb/gadget/u_serial.c)7
-rw-r--r--drivers/usb/gadget/function/u_serial.h (renamed from drivers/usb/gadget/u_serial.h)4
-rw-r--r--drivers/usb/gadget/functions.c2
-rw-r--r--drivers/usb/gadget/gadget_chips.h56
-rw-r--r--drivers/usb/gadget/legacy/Makefile8
-rw-r--r--drivers/usb/gadget/legacy/multi.c (renamed from drivers/usb/gadget/multi.c)8
-rw-r--r--drivers/usb/gadget/legacy/serial.c (renamed from drivers/usb/gadget/serial.c)28
-rw-r--r--drivers/usb/gadget/u_os_desc.h120
-rw-r--r--drivers/usb/gadget/udc-core.c354
-rw-r--r--drivers/usb/gadget/udc/Makefile7
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c (renamed from drivers/usb/gadget/at91_udc.c)28
-rw-r--r--drivers/usb/gadget/udc/at91_udc.h (renamed from drivers/usb/gadget/at91_udc.h)2
-rw-r--r--drivers/usb/gadget/udc/core.c1517
-rw-r--r--drivers/usb/gadget/udc/fsl_udc.c (renamed from drivers/usb/gadget/fsl_udc.c)29
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c (renamed from drivers/usb/gadget/pxa27x_udc.c)42
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h (renamed from drivers/usb/gadget/pxa27x_udc.h)2
-rw-r--r--drivers/usb/gadget/usbstring.c6
-rw-r--r--drivers/usb/host/ehci-atmel.c17
-rw-r--r--drivers/usb/host/ehci-hcd.c21
-rw-r--r--drivers/usb/host/ehci-omap.c12
-rw-r--r--drivers/usb/host/ohci-at91.c21
-rw-r--r--drivers/usb/host/ohci-hcd.c15
-rw-r--r--drivers/usb/host/xhci-mem.c103
-rw-r--r--drivers/usb/host/xhci-ring.c209
-rw-r--r--drivers/usb/host/xhci.c80
-rw-r--r--drivers/usb/host/xhci.h24
-rw-r--r--drivers/usb/imx/chipidea-imx.c43
-rw-r--r--drivers/usb/imx/imx-usb-misc.c17
-rw-r--r--drivers/usb/imx/imx-usb-phy.c11
-rw-r--r--drivers/usb/misc/Kconfig10
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/onboard_usb_hub.c97
-rw-r--r--drivers/usb/misc/onboard_usb_hub.h55
-rw-r--r--drivers/usb/misc/usb251xb.c17
-rw-r--r--drivers/usb/musb/am35x-phy-control.h2
-rw-r--r--drivers/usb/musb/musb_am335x.c7
-rw-r--r--drivers/usb/musb/musb_barebox.c4
-rw-r--r--drivers/usb/musb/musb_core.c8
-rw-r--r--drivers/usb/musb/musb_core.h7
-rw-r--r--drivers/usb/musb/musb_dsps.c21
-rw-r--r--drivers/usb/musb/musb_gadget.c41
-rw-r--r--drivers/usb/musb/musb_gadget.h2
-rw-r--r--drivers/usb/musb/musb_host.h2
-rw-r--r--drivers/usb/musb/phy-am335x-control.c11
-rw-r--r--drivers/usb/musb/phy-am335x.c7
-rw-r--r--drivers/usb/otg/otgdev.c49
-rw-r--r--drivers/usb/otg/twl4030.c2
-rw-r--r--drivers/usb/otg/ulpi.c2
-rw-r--r--drivers/usb/storage/transport.c80
-rw-r--r--drivers/usb/storage/usb.c64
-rw-r--r--drivers/usb/storage/usb.h2
-rw-r--r--drivers/usb/typec/Kconfig14
-rw-r--r--drivers/usb/typec/Makefile3
-rw-r--r--drivers/usb/typec/class.c179
-rw-r--r--drivers/usb/typec/tusb320.c221
-rw-r--r--drivers/video/Kconfig34
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/atmel_hlcdfb.c10
-rw-r--r--drivers/video/atmel_lcdfb.c7
-rw-r--r--drivers/video/atmel_lcdfb.h4
-rw-r--r--drivers/video/atmel_lcdfb_core.c19
-rw-r--r--drivers/video/backlight-pwm.c15
-rw-r--r--drivers/video/bcm2835.c6
-rw-r--r--drivers/video/bochs/bochs_hw.c8
-rw-r--r--drivers/video/bochs/bochs_hw.h4
-rw-r--r--drivers/video/bochs/bochs_isa.c4
-rw-r--r--drivers/video/edid.c11
-rw-r--r--drivers/video/efi_gop.c2
-rw-r--r--drivers/video/fb.c12
-rw-r--r--drivers/video/fbconsole.c115
-rw-r--r--drivers/video/imx-ipu-fb.c12
-rw-r--r--drivers/video/imx-ipu-v3/imx-hdmi.c13
-rw-r--r--drivers/video/imx-ipu-v3/imx-ldb.c13
-rw-r--r--drivers/video/imx-ipu-v3/imx-pd.c9
-rw-r--r--drivers/video/imx-ipu-v3/ipu-common.c23
-rw-r--r--drivers/video/imx-ipu-v3/ipu-dc.c4
-rw-r--r--drivers/video/imx-ipu-v3/ipu-di.c2
-rw-r--r--drivers/video/imx-ipu-v3/ipu-dmfc.c6
-rw-r--r--drivers/video/imx-ipu-v3/ipu-dp.c4
-rw-r--r--drivers/video/imx-ipu-v3/ipu-prv.h15
-rw-r--r--drivers/video/imx-ipu-v3/ipufb.c17
-rw-r--r--drivers/video/imx-ipu-v3/ipuv3-plane.c2
-rw-r--r--drivers/video/imx.c10
-rw-r--r--drivers/video/mipi_dbi.c291
-rw-r--r--drivers/video/mtl017.c17
-rw-r--r--drivers/video/of_display_timing.c53
-rw-r--r--drivers/video/omap.c6
-rw-r--r--drivers/video/panel-ilitek-ili9341.c34
-rw-r--r--drivers/video/panel-mipi-dbi.c332
-rw-r--r--drivers/video/pxa.c14
-rw-r--r--drivers/video/ramfb.c191
-rw-r--r--drivers/video/rave-sp-backlight.c5
-rw-r--r--drivers/video/s3c24xx.c411
-rw-r--r--drivers/video/sdl.c6
-rw-r--r--drivers/video/simple-panel.c11
-rw-r--r--drivers/video/simplefb-client.c13
-rw-r--r--drivers/video/simplefb-fixup.c10
-rw-r--r--drivers/video/ssd1307fb.c15
-rw-r--r--drivers/video/stm.c13
-rw-r--r--drivers/video/stm32_ltdc.c13
-rw-r--r--drivers/video/tc358767.c20
-rw-r--r--drivers/video/vpl.c15
-rw-r--r--drivers/virtio/Kconfig1
-rw-r--r--drivers/virtio/virtio.c9
-rw-r--r--drivers/virtio/virtio_mmio.c7
-rw-r--r--drivers/virtio/virtio_pci_common.c2
-rw-r--r--drivers/virtio/virtio_pci_modern.c28
-rw-r--r--drivers/virtio/virtio_ring.c6
-rw-r--r--drivers/w1/masters/w1-gpio.c15
-rw-r--r--drivers/w1/w1.c8
-rw-r--r--drivers/w1/w1.h8
-rw-r--r--drivers/watchdog/Kconfig14
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/ar9344_wdt.c7
-rw-r--r--drivers/watchdog/at91sam9_wdt.c7
-rw-r--r--drivers/watchdog/bcm2835_wdt.c7
-rw-r--r--drivers/watchdog/cadence_wdt.c278
-rw-r--r--drivers/watchdog/davinci_wdt.c5
-rw-r--r--drivers/watchdog/dw_wdt.c5
-rw-r--r--drivers/watchdog/efi_wdt.c6
-rw-r--r--drivers/watchdog/f71808e_wdt.c6
-rw-r--r--drivers/watchdog/gpio_wdt.c29
-rw-r--r--drivers/watchdog/im28wd.c9
-rw-r--r--drivers/watchdog/imxulp-wdt.c168
-rw-r--r--drivers/watchdog/imxwd.c42
-rw-r--r--drivers/watchdog/jz4740.c5
-rw-r--r--drivers/watchdog/kvx_wdt.c5
-rw-r--r--drivers/watchdog/omap_wdt.c5
-rw-r--r--drivers/watchdog/orion_wdt.c5
-rw-r--r--drivers/watchdog/rave-sp-wdt.c7
-rw-r--r--drivers/watchdog/rn5t568_wdt.c7
-rw-r--r--drivers/watchdog/starfive_wdt.c5
-rw-r--r--drivers/watchdog/stm32_iwdg.c5
-rw-r--r--drivers/watchdog/stpmic1_wdt.c6
-rw-r--r--drivers/watchdog/wd_core.c16
-rw-r--r--drivers/watchdog/wdat_wdt.c2
991 files changed, 87818 insertions, 22224 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 2636eed1a3..04da623aa5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -42,9 +42,12 @@ source "drivers/phy/Kconfig"
source "drivers/crypto/Kconfig"
source "drivers/memory/Kconfig"
source "drivers/soc/Kconfig"
+source "drivers/pmdomain/Kconfig"
source "drivers/nvme/Kconfig"
source "drivers/ddr/Kconfig"
source "drivers/power/Kconfig"
source "drivers/virtio/Kconfig"
+source "drivers/mailbox/Kconfig"
+source "drivers/tee/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 10ec145be5..7b94a76c56 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -2,7 +2,7 @@
obj-y += base/
obj-y += block/
obj-$(CONFIG_ARM_AMBA) += amba/
-obj-$(CONFIG_EFI_BOOTUP) += efi/
+obj-$(CONFIG_EFI_PAYLOAD) += efi/
obj-y += net/
obj-y += serial/
obj-y += mtd/
@@ -34,16 +34,19 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc/
obj-$(CONFIG_RESET_CONTROLLER) += reset/
obj-$(CONFIG_PCI) += pci/
obj-y += rtc/
-obj-$(CONFIG_FIRMWARE) += firmware/
+obj-y += firmware/
obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_GENERIC_PHY) += phy/
obj-$(CONFIG_HAB) += hab/
-obj-$(CONFIG_CRYPTO_HW) += crypto/
+obj-y += crypto/
obj-$(CONFIG_AIODEV) += aiodev/
obj-y += memory/
obj-y += soc/
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += pmdomain/
obj-y += nvme/
obj-y += ddr/
obj-y += power/
obj-$(CONFIG_SOUND) += sound/
obj-y += virtio/
+obj-y += mailbox/
+obj-y += tee/
diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig
index 6bd697702e..e1edc25320 100644
--- a/drivers/aiodev/Kconfig
+++ b/drivers/aiodev/Kconfig
@@ -65,4 +65,19 @@ config ROCKCHIP_SARADC
help
Support for Successive Approximation Register (SAR) ADC in Rockchip
SoCs.
+
+config IMX7D_ADC
+ tristate "Freescale IMX7D ADC driver"
+ depends on ARCH_IMX7 || COMPILE_TEST
+ depends on OFDEVICE
+ help
+ Say yes here to build support for IMX7D ADC.
+
+config VF610_ADC
+ tristate "Freescale vf610 ADC driver"
+ depends on ARCH_IMX6 || COMPILE_TEST
+ help
+ Say yes here to support for Vybrid board analog-to-digital converter.
+ Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
+
endif
diff --git a/drivers/aiodev/Makefile b/drivers/aiodev/Makefile
index 06a63b0d2d..ce95d5be2f 100644
--- a/drivers/aiodev/Makefile
+++ b/drivers/aiodev/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_AM335X_ADC) += am335x_adc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
+obj-$(CONFIG_VF610_ADC) += vf610_adc.o
diff --git a/drivers/aiodev/am335x_adc.c b/drivers/aiodev/am335x_adc.c
index 0d6cc426eb..512f0a86fa 100644
--- a/drivers/aiodev/am335x_adc.c
+++ b/drivers/aiodev/am335x_adc.c
@@ -19,7 +19,7 @@
#include <io.h>
#include <linux/log2.h>
#include <aiodev.h>
-#include <mach/am33xx-clock.h>
+#include <mach/omap/am33xx-clock.h>
#include "ti_am335x_tscadc.h"
struct am335x_adc_data {
@@ -72,7 +72,7 @@ static int am335x_adc_read(struct aiochannel *chan, int *val)
return 0;
}
-static int am335x_adc_probe(struct device_d *dev)
+static int am335x_adc_probe(struct device *dev)
{
struct device_node *node;
struct am335x_adc_data *data;
@@ -87,7 +87,7 @@ static int am335x_adc_probe(struct device_d *dev)
goto fail_data;
}
- node = of_find_compatible_node(dev->device_node, NULL, "ti,am3359-adc");
+ node = of_find_compatible_node(dev->of_node, NULL, "ti,am3359-adc");
if (!node) {
ret = -EINVAL;
goto fail_data;
@@ -174,8 +174,9 @@ static const struct of_device_id of_am335x_adc_match[] = {
{ .compatible = "ti,am3359-tscadc", },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_am335x_adc_match);
-static struct driver_d am335x_adc_driver = {
+static struct driver am335x_adc_driver = {
.name = "am335x_adc",
.probe = am335x_adc_probe,
.of_compatible = DRV_OF_COMPAT(of_am335x_adc_match),
diff --git a/drivers/aiodev/core.c b/drivers/aiodev/core.c
index 9f5c422500..5bdc4d83d4 100644
--- a/drivers/aiodev/core.c
+++ b/drivers/aiodev/core.c
@@ -31,16 +31,16 @@ struct aiochannel *aiochannel_by_name(const char *name)
}
EXPORT_SYMBOL(aiochannel_by_name);
-struct aiochannel *aiochannel_get(struct device_d *dev, int index)
+struct aiochannel *aiochannel_get(struct device *dev, int index)
{
struct of_phandle_args spec;
struct aiodevice *aiodev;
int ret, chnum = 0;
- if (!dev->device_node)
+ if (!dev->of_node)
return ERR_PTR(-EINVAL);
- ret = of_parse_phandle_with_args(dev->device_node,
+ ret = of_parse_phandle_with_args(dev->of_node,
"io-channels",
"#io-channel-cells",
index, &spec);
@@ -48,7 +48,7 @@ struct aiochannel *aiochannel_get(struct device_d *dev, int index)
return ERR_PTR(ret);
list_for_each_entry(aiodev, &aiodevices, list) {
- if (aiodev->hwdev->device_node == spec.np)
+ if (aiodev->hwdev->of_node == spec.np)
goto found;
}
@@ -73,6 +73,18 @@ int aiochannel_get_value(struct aiochannel *aiochan, int *value)
}
EXPORT_SYMBOL(aiochannel_get_value);
+int aiochannel_name_get_value(const char *chname, int *value)
+{
+ struct aiochannel *aio;
+
+ aio = aiochannel_by_name(chname);
+ if (IS_ERR(aio))
+ return PTR_ERR(aio);
+
+ return aiochannel_get_value(aio, value);
+}
+EXPORT_SYMBOL(aiochannel_name_get_value);
+
int aiochannel_get_index(struct aiochannel *aiochan)
{
return aiochan->index;
@@ -91,10 +103,10 @@ int aiodevice_register(struct aiodevice *aiodev)
int i, ret;
if (!aiodev->name && aiodev->hwdev &&
- aiodev->hwdev->device_node) {
+ aiodev->hwdev->of_node) {
aiodev->dev.id = DEVICE_ID_SINGLE;
- aiodev->name = of_alias_get(aiodev->hwdev->device_node);
+ aiodev->name = of_alias_get(aiodev->hwdev->of_node);
}
if (!aiodev->name) {
diff --git a/drivers/aiodev/imx7d_adc.c b/drivers/aiodev/imx7d_adc.c
new file mode 100644
index 0000000000..4d1f902031
--- /dev/null
+++ b/drivers/aiodev/imx7d_adc.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale i.MX7D ADC driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+#include <linux/printk.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <regulator.h>
+#include <linux/barebox-wrapper.h>
+
+#include <linux/iopoll.h>
+#include <aiodev.h>
+
+/* ADC register */
+#define IMX7D_REG_ADC_TIMER_UNIT 0x90
+#define IMX7D_REG_ADC_INT_STATUS 0xe0
+#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
+#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
+#define IMX7D_REG_ADC_ADC_CFG 0x130
+
+#define IMX7D_REG_ADC_CFG1(ch) ((ch) * 0x20)
+#define IMX7D_REG_ADC_CFG2(ch) ((ch) * 0x20 + 0x10)
+
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
+
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
+
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
+
+#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
+
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
+
+#define IMX7D_ADC_TIMEOUT_NSEC (100 * NSEC_PER_MSEC)
+#define IMX7D_ADC_INPUT_CLK 24000000
+
+enum imx7d_adc_clk_pre_div {
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
+};
+
+enum imx7d_adc_average_num {
+ IMX7D_ADC_AVERAGE_NUM_4,
+ IMX7D_ADC_AVERAGE_NUM_8,
+ IMX7D_ADC_AVERAGE_NUM_16,
+ IMX7D_ADC_AVERAGE_NUM_32,
+};
+
+struct imx7d_adc_feature {
+ enum imx7d_adc_clk_pre_div clk_pre_div;
+ enum imx7d_adc_average_num avg_num;
+
+ u32 core_time_unit; /* impact the sample rate */
+};
+
+struct imx7d_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct aiodevice aiodev;
+ void (*aiodev_info)(struct device *);
+
+ u32 vref_uv;
+ u32 pre_div_num;
+
+ struct regulator *vref;
+ struct imx7d_adc_feature adc_feature;
+
+ struct aiochannel aiochan[16];
+};
+
+struct imx7d_adc_analogue_core_clk {
+ u32 pre_div;
+ u32 reg_config;
+};
+
+#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
+ .pre_div = (_pre_div), \
+ .reg_config = (_reg_conf), \
+}
+
+static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
+};
+
+static const u32 imx7d_adc_average_num[] = {
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
+};
+
+static void imx7d_adc_feature_config(struct imx7d_adc *info)
+{
+ info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
+ info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
+ info->adc_feature.core_time_unit = 1;
+}
+
+static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
+{
+ struct imx7d_adc_feature *adc_feature = &info->adc_feature;
+ struct imx7d_adc_analogue_core_clk adc_analogue_clk;
+ unsigned i;
+ u32 tmp_cfg1;
+ u32 sample_rate = 0;
+
+ /*
+ * Before sample set, disable channel A,B,C,D. Here we
+ * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
+ */
+ for (i = 0; i < 4; i++) {
+ tmp_cfg1 = readl(info->regs + IMX7D_REG_ADC_CFG1(i));
+ tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
+ writel(tmp_cfg1, info->regs + IMX7D_REG_ADC_CFG1(i));
+ }
+
+ adc_analogue_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
+ sample_rate |= adc_analogue_clk.reg_config;
+ info->pre_div_num = adc_analogue_clk.pre_div;
+
+ sample_rate |= adc_feature->core_time_unit;
+ writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
+}
+
+static void imx7d_adc_hw_init(struct imx7d_adc *info)
+{
+ u32 cfg;
+
+ /* power up and enable adc analogue core */
+ cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
+ cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+
+ imx7d_adc_sample_rate_set(info);
+}
+
+static void imx7d_adc_channel_set(struct imx7d_adc *info, u32 channel)
+{
+ u32 cfg1 = 0;
+ u32 cfg2;
+
+ /* the channel choose single conversion, and enable average mode */
+ cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
+
+ /*
+ * physical channel 0 chose logical channel A
+ * physical channel 1 chose logical channel B
+ * physical channel 2 chose logical channel C
+ * physical channel 3 chose logical channel D
+ */
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
+
+ /*
+ * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
+ * channel chosen
+ */
+ cfg2 = readl(info->regs + IMX7D_REG_ADC_CFG2(channel));
+
+ cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
+
+ /*
+ * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
+ * the channel chosen
+ */
+ writel(cfg2, info->regs + IMX7D_REG_ADC_CFG2(channel));
+ writel(cfg1, info->regs + IMX7D_REG_ADC_CFG1(channel));
+}
+
+static u16 __imx7d_adc_read_data(struct imx7d_adc *info, u32 channel)
+{
+ u32 value;
+
+ /*
+ * channel A and B conversion result share one register,
+ * bit[27~16] is the channel B conversion result,
+ * bit[11~0] is the channel A conversion result.
+ * channel C and D is the same.
+ */
+ if (channel < 2)
+ value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
+ else
+ value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
+ if (channel & 0x1) /* channel B or D */
+ value = (value >> 16) & 0xFFF;
+ else /* channel A or C */
+ value &= 0xFFF;
+
+ return value;
+}
+
+static int imx7d_adc_read_data(struct imx7d_adc *info, u32 channel)
+{
+ int ret = -EAGAIN;
+ int status;
+
+ status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
+ ret = __imx7d_adc_read_data(info, channel);
+
+ /*
+ * The register IMX7D_REG_ADC_INT_STATUS can't clear
+ * itself after read operation, need software to write
+ * 0 to the related bit. Here we clear the channel A/B/C/D
+ * conversion finished flag.
+ */
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ /*
+ * If the channel A/B/C/D conversion timeout, report it and clear these
+ * timeout flags.
+ */
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
+ dev_err(info->dev,
+ "ADC got conversion time out interrupt: 0x%08x\n",
+ status);
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ ret = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+
+static int imx7d_adc_read_raw(struct aiochannel *chan, int *data)
+{
+ struct imx7d_adc *info = container_of(chan->aiodev, struct imx7d_adc, aiodev);
+ u64 raw64, start;
+ u32 channel;
+ int ret;
+
+ channel = chan->index & 0x03;
+ imx7d_adc_channel_set(info, channel);
+
+ start = get_time_ns();
+ do {
+ if (is_timeout(start, IMX7D_ADC_TIMEOUT_NSEC)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ ret = imx7d_adc_read_data(info, channel);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ return ret;
+
+ raw64 = ret;
+ raw64 *= info->vref_uv;
+ raw64 = div_u64(raw64, 1000);
+ *data = div_u64(raw64, (1 << 12));
+
+ return 0;
+}
+
+static const struct of_device_id imx7d_adc_match[] = {
+ { .compatible = "fsl,imx7d-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx7d_adc_match);
+
+static void imx7d_adc_power_down(struct imx7d_adc *info)
+{
+ u32 adc_cfg;
+
+ adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
+ adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+}
+
+static int imx7d_adc_enable(struct imx7d_adc *info)
+{
+ struct device *dev = info->dev;
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Can't enable adc reference top voltage\n");
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ regulator_disable(info->vref);
+ return dev_err_probe(dev, ret, "Could not enable clock.\n");
+ }
+
+ imx7d_adc_hw_init(info);
+
+ ret = regulator_get_voltage(info->vref);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "can't get vref-supply value\n");
+
+ info->vref_uv = ret;
+ return 0;
+}
+
+static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
+{
+ u32 analogue_core_clk;
+ u32 core_time_unit = info->adc_feature.core_time_unit;
+ u32 tmp;
+
+ analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
+ tmp = (core_time_unit + 1) * 6;
+
+ return analogue_core_clk / tmp;
+}
+
+static void imx7d_adc_devinfo(struct device *dev)
+{
+ struct imx7d_adc *info = dev->parent->priv;
+
+ if (info->aiodev_info)
+ info->aiodev_info(dev);
+
+ printf("Sample Rate: %u\n", imx7d_adc_get_sample_rate(info));
+}
+
+static int imx7d_adc_probe(struct device *dev)
+{
+ struct aiodevice *aiodev;
+ struct imx7d_adc *info;
+ int ret, i;
+
+ info = xzalloc(sizeof(*info));
+
+ info->dev = dev;
+
+ info->clk = clk_get(dev, "adc");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n");
+
+ info->vref = regulator_get(dev, "vref");
+ if (IS_ERR(info->vref))
+ return dev_err_probe(dev, PTR_ERR(info->vref),
+ "Failed getting reference voltage\n");
+
+ info->regs = dev_request_mem_region(dev, 0);
+ if (IS_ERR(info->regs))
+ return dev_err_probe(dev, PTR_ERR(info->regs),
+ "Failed to get memory region\n");
+
+ dev->priv = info;
+ aiodev = &info->aiodev;
+
+ aiodev->num_channels = 4;
+ aiodev->hwdev = dev;
+ aiodev->read = imx7d_adc_read_raw;
+ aiodev->channels = xzalloc(aiodev->num_channels * sizeof(aiodev->channels[0]));
+
+ for (i = 0; i < aiodev->num_channels; i++) {
+ aiodev->channels[i] = &info->aiochan[i];
+ info->aiochan[i].unit = "mV";
+ }
+
+ imx7d_adc_feature_config(info);
+
+ ret = imx7d_adc_enable(info);
+ if (ret)
+ return ret;
+
+ ret = aiodevice_register(aiodev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to register aiodev\n");
+
+ info->aiodev_info = aiodev->dev.info;
+ aiodev->dev.info = imx7d_adc_devinfo;
+
+ return 0;
+}
+
+static void imx7d_adc_disable(struct device *dev)
+{
+ struct imx7d_adc *info = dev->priv;
+
+ imx7d_adc_power_down(info);
+
+ clk_disable(info->clk);
+ regulator_disable(info->vref);
+}
+
+static struct driver imx7d_adc_driver = {
+ .probe = imx7d_adc_probe,
+ .name = "imx7d_adc",
+ .of_compatible = imx7d_adc_match,
+ .remove = imx7d_adc_disable,
+};
+device_platform_driver(imx7d_adc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freescale IMX7D ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/aiodev/imx_thermal.c b/drivers/aiodev/imx_thermal.c
index 0bc84ee160..2693ad05e0 100644
--- a/drivers/aiodev/imx_thermal.c
+++ b/drivers/aiodev/imx_thermal.c
@@ -22,14 +22,14 @@
#include <linux/math64.h>
#include <linux/log2.h>
#include <linux/clk.h>
-#include <mach/imx6-anadig.h>
+#include <linux/nvmem-consumer.h>
+#include <mach/imx/imx6-anadig.h>
#include <io.h>
#include <aiodev.h>
#include <mfd/syscon.h>
#define FACTOR0 10000000
#define MEASURE_FREQ 327
-#define OCOTP_ANA1_OFFSET (0xE * sizeof(uint32_t))
struct imx_thermal_data {
int c1, c2;
@@ -106,36 +106,19 @@ static int imx_thermal_read(struct aiochannel *chan, int *val)
return 0;
}
-static int imx_thermal_probe(struct device_d *dev)
+static int imx_thermal_probe(struct device *dev)
{
uint32_t ocotp_ana1;
- struct device_node *node;
struct imx_thermal_data *imx_thermal;
- struct cdev *ocotp;
int t1, n1, t2, n2;
int ret;
- node = of_parse_phandle(dev->device_node, "fsl,tempmon-data", 0);
- if (!node) {
- dev_err(dev, "No calibration data source\n");
- return -ENODEV;
- }
-
- ocotp = cdev_by_device_node(node);
- if (!ocotp) {
- dev_err(dev, "No OCOTP character device\n");
- return -ENODEV;
- }
-
- ret = cdev_read(ocotp, &ocotp_ana1, sizeof(ocotp_ana1),
- OCOTP_ANA1_OFFSET, 0);
- if (ret != sizeof(ocotp_ana1)) {
- dev_err(dev, "Failed to read calibration data\n");
- return ret < 0 ? ret : -EIO;
- }
+ ret = nvmem_cell_read_variable_le_u32(dev, "calib", &ocotp_ana1);
+ if (ret)
+ return ret;
imx_thermal = xzalloc(sizeof(*imx_thermal));
- imx_thermal->base = syscon_base_lookup_by_phandle(dev->device_node,
+ imx_thermal->base = syscon_base_lookup_by_phandle(dev->of_node,
"fsl,tempmon");
if (IS_ERR(imx_thermal->base)) {
dev_err(dev, "Could not get ANATOP address\n");
@@ -192,9 +175,10 @@ static const struct of_device_id of_imx_thermal_match[] = {
{ .compatible = "fsl,imx6sx-tempmon", },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
-static struct driver_d imx_thermal_driver = {
+static struct driver imx_thermal_driver = {
.name = "imx_thermal",
.probe = imx_thermal_probe,
.of_compatible = DRV_OF_COMPAT(of_imx_thermal_match),
diff --git a/drivers/aiodev/lm75.c b/drivers/aiodev/lm75.c
index 1900636555..13b7ac4710 100644
--- a/drivers/aiodev/lm75.c
+++ b/drivers/aiodev/lm75.c
@@ -57,7 +57,7 @@ static const u8 LM75_REG_TEMP[3] = {
/* Each client has this additional data */
struct lm75_data {
struct i2c_client *client;
- struct device_d dev;
+ struct device dev;
u8 resolution; /* In bits, between 9 and 12 */
struct aiochannel aiochan;
struct aiodevice aiodev;
@@ -102,7 +102,7 @@ static int lm75_get_temp(struct aiochannel *chan, int *val)
return 0;
}
-static int lm75_probe(struct device_d *dev)
+static int lm75_probe(struct device *dev)
{
struct lm75_data *data;
int status;
@@ -237,7 +237,7 @@ static const struct platform_device_id lm75_ids[] = {
{ /* LIST END */ }
};
-static struct driver_d lm75_driver = {
+static struct driver lm75_driver = {
.name = "lm75",
.probe = lm75_probe,
.id_table = lm75_ids,
diff --git a/drivers/aiodev/mc13xxx_adc.c b/drivers/aiodev/mc13xxx_adc.c
index 13436e8936..21eea1f525 100644
--- a/drivers/aiodev/mc13xxx_adc.c
+++ b/drivers/aiodev/mc13xxx_adc.c
@@ -178,7 +178,7 @@ err:
return ret;
}
-int mc13xxx_adc_probe(struct device_d *dev, struct mc13xxx *mc_dev)
+int mc13xxx_adc_probe(struct device *dev, struct mc13xxx *mc_dev)
{
int i;
int ret;
diff --git a/drivers/aiodev/qoriq_thermal.c b/drivers/aiodev/qoriq_thermal.c
index 1acb06a9de..20998a0f97 100644
--- a/drivers/aiodev/qoriq_thermal.c
+++ b/drivers/aiodev/qoriq_thermal.c
@@ -67,7 +67,7 @@ struct qoriq_tmu_regs {
* Thermal zone data
*/
struct qoriq_tmu_data {
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
struct qoriq_tmu_regs __iomem *regs;
int sensor_id;
@@ -146,7 +146,7 @@ static int qoriq_tmu_calibration(struct qoriq_tmu_data *data)
int i, val, len;
u32 range[4];
const u32 *calibration;
- struct device_node *np = data->dev->device_node;
+ struct device_node *np = data->dev->of_node;
if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
dev_err(data->dev, "missing calibration range.\n");
@@ -187,9 +187,9 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
}
-static int qoriq_tmu_probe(struct device_d *dev)
+static int qoriq_tmu_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct qoriq_tmu_data *data;
u32 site;
int ret;
@@ -245,8 +245,9 @@ static const struct of_device_id qoriq_tmu_match[] = {
{ .compatible = "fsl,imx8mq-tmu",},
{},
};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
-static struct driver_d imx_thermal_driver = {
+static struct driver imx_thermal_driver = {
.name = "qoriq_thermal",
.probe = qoriq_tmu_probe,
.of_compatible = DRV_OF_COMPAT(qoriq_tmu_match),
diff --git a/drivers/aiodev/rockchip_saradc.c b/drivers/aiodev/rockchip_saradc.c
index 707df71950..3c5c0e94da 100644
--- a/drivers/aiodev/rockchip_saradc.c
+++ b/drivers/aiodev/rockchip_saradc.c
@@ -85,7 +85,7 @@ static int rockchip_saradc_read(struct aiochannel *chan, int *val)
return 0;
}
-static int rockchip_saradc_probe(struct device_d *dev)
+static int rockchip_saradc_probe(struct device *dev)
{
struct rockchip_saradc_data *data;
int i, ret;
@@ -188,8 +188,9 @@ static const struct of_device_id of_rockchip_saradc_match[] = {
{ .compatible = "rockchip,rk3568-saradc", .data = &rk3568_saradc_cfg },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_rockchip_saradc_match);
-static struct driver_d rockchip_saradc_driver = {
+static struct driver rockchip_saradc_driver = {
.name = "rockchip_saradc",
.probe = rockchip_saradc_probe,
.of_compatible = DRV_OF_COMPAT(of_rockchip_saradc_match),
diff --git a/drivers/aiodev/st_gyro.c b/drivers/aiodev/st_gyro.c
index 3938d8239e..3a8b6a7761 100644
--- a/drivers/aiodev/st_gyro.c
+++ b/drivers/aiodev/st_gyro.c
@@ -66,7 +66,7 @@ static int st_gyro_read(struct aiochannel *chan, int *val)
return 0;
}
-static int st_gyro_probe(struct device_d *dev)
+static int st_gyro_probe(struct device *dev)
{
u8 tx[2], rx[2];
struct st_gyro *gyro;
@@ -113,8 +113,9 @@ static const struct of_device_id st_gyro_match[] = {
{ .compatible = "st,l3gd20-gyro" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, st_gyro_match);
-static struct driver_d st_gyro_driver = {
+static struct driver st_gyro_driver = {
.name = "st_gyro",
.probe = st_gyro_probe,
.of_compatible = st_gyro_match,
diff --git a/drivers/aiodev/stm32-adc-core.c b/drivers/aiodev/stm32-adc-core.c
index 43abed8386..f4e22d47af 100644
--- a/drivers/aiodev/stm32-adc-core.c
+++ b/drivers/aiodev/stm32-adc-core.c
@@ -56,7 +56,7 @@ static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
{ 3, 0, 4 },
};
-static int stm32h7_adc_clk_sel(struct device_d *dev,
+static int stm32h7_adc_clk_sel(struct device *dev,
struct stm32_adc_common *common)
{
u32 ckmode, presc;
@@ -137,7 +137,7 @@ out:
return 0;
}
-static int stm32_adc_core_probe(struct device_d *dev)
+static int stm32_adc_core_probe(struct device *dev)
{
struct stm32_adc_common *common;
int ret;
@@ -186,7 +186,7 @@ static int stm32_adc_core_probe(struct device_d *dev)
goto err_bclk_disable;
dev->priv = common;
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
err_bclk_disable:
clk_disable(common->bclk);
@@ -202,8 +202,9 @@ static const struct of_device_id stm32_adc_core_ids[] = {
{ .compatible = "st,stm32mp1-adc-core" },
{}
};
+MODULE_DEVICE_TABLE(of, stm32_adc_core_ids);
-static struct driver_d stm32_adc_core_driver = {
+static struct driver stm32_adc_core_driver = {
.name = "stm32-adc-core",
.probe = stm32_adc_core_probe,
.of_compatible = DRV_OF_COMPAT(stm32_adc_core_ids),
diff --git a/drivers/aiodev/stm32-adc.c b/drivers/aiodev/stm32-adc.c
index 930708d4df..a1998da62c 100644
--- a/drivers/aiodev/stm32-adc.c
+++ b/drivers/aiodev/stm32-adc.c
@@ -91,7 +91,7 @@ static void stm32_adc_stop(struct stm32_adc *adc)
static int stm32_adc_start_channel(struct stm32_adc *adc, int channel)
{
- struct device_d *dev = adc->aiodev.hwdev;
+ struct device *dev = adc->aiodev.hwdev;
struct stm32_adc_common *common = adc->common;
int ret;
u32 val;
@@ -153,7 +153,7 @@ static int stm32_adc_start_channel(struct stm32_adc *adc, int channel)
static int stm32_adc_channel_data(struct stm32_adc *adc, int channel,
int *data)
{
- struct device_d *dev = &adc->aiodev.dev;
+ struct device *dev = &adc->aiodev.dev;
int ret;
u32 val;
@@ -211,7 +211,7 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift);
}
-static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
+static int stm32_adc_chan_of_init(struct device *dev, struct stm32_adc *adc)
{
unsigned int i;
int num_channels = 0, num_times = 0;
@@ -219,7 +219,7 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
int ret;
/* Retrieve single ended channels listed in device tree */
- of_get_property(dev->device_node, "st,adc-channels", &num_channels);
+ of_get_property(dev->of_node, "st,adc-channels", &num_channels);
num_channels /= sizeof(__be32);
if (num_channels > adc->cfg->max_channels) {
@@ -228,7 +228,7 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
}
/* Optional sample time is provided either for each, or all channels */
- of_get_property(dev->device_node, "st,min-sample-time-nsecs", &num_times);
+ of_get_property(dev->of_node, "st,min-sample-time-nsecs", &num_times);
num_times /= sizeof(__be32);
if (num_times > 1 && num_times != num_channels) {
dev_err(dev, "Invalid st,min-sample-time-nsecs\n");
@@ -252,7 +252,8 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
for (i = 0; i < num_channels; i++) {
u32 chan;
- ret = of_property_read_u32_index(dev->device_node, "st,adc-channels", i, &chan);
+ ret = of_property_read_u32_index(dev->of_node,
+ "st,adc-channels", i, &chan);
if (ret)
return ret;
@@ -273,7 +274,8 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
* get either no value, 1 shared value for all indexes, or one
* value per channel.
*/
- of_property_read_u32_index(dev->device_node, "st,min-sample-time-nsecs",
+ of_property_read_u32_index(dev->of_node,
+ "st,min-sample-time-nsecs",
i, &smp);
/* Prepare sampling time settings */
stm32_adc_smpr_init(adc, chan, smp);
@@ -288,14 +290,14 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
return ret;
}
-static int stm32_adc_probe(struct device_d *dev)
+static int stm32_adc_probe(struct device *dev)
{
struct stm32_adc_common *common = dev->parent->priv;
struct stm32_adc *adc;
u32 offset;
int ret;
- ret = of_property_read_u32(dev->device_node, "reg", &offset);
+ ret = of_property_read_u32(dev->of_node, "reg", &offset);
if (ret) {
dev_err(dev, "Can't read reg property\n");
return ret;
@@ -360,13 +362,28 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
.has_vregready = true,
};
+/* STM32MP13 programmable sampling time (ADC clock cycles, rounded down) */
+static const unsigned int stm32mp13_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+ 2, 6, 12, 24, 47, 92, 247, 640,
+};
+
+static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
+ .num_bits = 16,
+ .max_channels = 19,
+ .smp_bits = stm32h7_smp_bits,
+ .smp_cycles = stm32mp13_adc_smp_cycles,
+ .has_vregready = false,
+};
+
static const struct of_device_id stm32_adc_match[] = {
{ .compatible = "st,stm32h7-adc", .data = &stm32h7_adc_cfg },
{ .compatible = "st,stm32mp1-adc", .data = &stm32mp1_adc_cfg },
+ { .compatible = "st,stm32mp13-adc", .data = &stm32mp13_adc_cfg },
{}
};
+MODULE_DEVICE_TABLE(of, stm32_adc_match);
-static struct driver_d stm32_adc_driver = {
+static struct driver stm32_adc_driver = {
.name = "stm32-adc",
.probe = stm32_adc_probe,
.of_compatible = DRV_OF_COMPAT(stm32_adc_match),
diff --git a/drivers/aiodev/vf610_adc.c b/drivers/aiodev/vf610_adc.c
new file mode 100644
index 0000000000..cb062e0846
--- /dev/null
+++ b/drivers/aiodev/vf610_adc.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale Vybrid vf610 ADC driver
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ */
+
+#include <clock.h>
+#include <io.h>
+#include <linux/printk.h>
+#include <driver.h>
+#include <init.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <regulator.h>
+#include <linux/err.h>
+
+#include <aiodev.h>
+
+/* This will be the driver name the kernel reports */
+#define DRIVER_NAME "vf610-adc"
+
+/* Vybrid/IMX ADC registers */
+#define VF610_REG_ADC_HC0 0x00
+#define VF610_REG_ADC_HS 0x08
+#define VF610_REG_ADC_R0 0x0c
+#define VF610_REG_ADC_CFG 0x14
+#define VF610_REG_ADC_GC 0x18
+#define VF610_REG_ADC_GS 0x1c
+
+/* Configuration register field define */
+#define VF610_ADC_MODE_BIT8 0x00
+#define VF610_ADC_MODE_BIT10 0x04
+#define VF610_ADC_MODE_BIT12 0x08
+#define VF610_ADC_MODE_MASK 0x0c
+#define VF610_ADC_BUSCLK2_SEL 0x01
+#define VF610_ADC_ALTCLK_SEL 0x02
+#define VF610_ADC_ADACK_SEL 0x03
+#define VF610_ADC_ADCCLK_MASK 0x03
+#define VF610_ADC_CLK_DIV2 0x20
+#define VF610_ADC_CLK_DIV4 0x40
+#define VF610_ADC_CLK_DIV8 0x60
+#define VF610_ADC_CLK_MASK 0x60
+#define VF610_ADC_ADLSMP_LONG 0x10
+#define VF610_ADC_ADSTS_SHORT 0x100
+#define VF610_ADC_ADSTS_NORMAL 0x200
+#define VF610_ADC_ADSTS_LONG 0x300
+#define VF610_ADC_ADSTS_MASK 0x300
+#define VF610_ADC_ADLPC_EN 0x80
+#define VF610_ADC_ADHSC_EN 0x400
+#define VF610_ADC_REFSEL_VALT 0x800
+#define VF610_ADC_REFSEL_VBG 0x1000
+#define VF610_ADC_AVGS_8 0x4000
+#define VF610_ADC_AVGS_16 0x8000
+#define VF610_ADC_AVGS_32 0xC000
+#define VF610_ADC_AVGS_MASK 0xC000
+#define VF610_ADC_OVWREN 0x10000
+
+/* General control register field define */
+#define VF610_ADC_AVGEN 0x20
+#define VF610_ADC_CAL 0x80
+
+/* Other field define */
+#define VF610_ADC_ADCHC(x) ((x) & 0x1F)
+#define VF610_ADC_CONV_DISABLE 0x1F
+#define VF610_ADC_HS_COCO0 0x1
+#define VF610_ADC_CALF 0x2
+#define VF610_ADC_TIMEOUT_NSEC (100 * NSEC_PER_MSEC)
+
+#define DEFAULT_SAMPLE_TIME 1000
+
+enum clk_sel {
+ VF610_ADCIOC_BUSCLK_SET,
+ VF610_ADCIOC_ALTCLK_SET,
+ VF610_ADCIOC_ADACK_SET,
+};
+
+enum vol_ref {
+ VF610_ADCIOC_VR_VREF_SET,
+ VF610_ADCIOC_VR_VALT_SET,
+ VF610_ADCIOC_VR_VBG_SET,
+};
+
+enum average_sel {
+ VF610_ADC_SAMPLE_1,
+ VF610_ADC_SAMPLE_4,
+ VF610_ADC_SAMPLE_8,
+ VF610_ADC_SAMPLE_16,
+ VF610_ADC_SAMPLE_32,
+};
+
+enum conversion_mode_sel {
+ VF610_ADC_CONV_NORMAL,
+ VF610_ADC_CONV_HIGH_SPEED,
+ VF610_ADC_CONV_LOW_POWER,
+};
+
+enum lst_adder_sel {
+ VF610_ADCK_CYCLES_3,
+ VF610_ADCK_CYCLES_5,
+ VF610_ADCK_CYCLES_7,
+ VF610_ADCK_CYCLES_9,
+ VF610_ADCK_CYCLES_13,
+ VF610_ADCK_CYCLES_17,
+ VF610_ADCK_CYCLES_21,
+ VF610_ADCK_CYCLES_25,
+};
+
+struct vf610_adc_feature {
+ enum clk_sel clk_sel;
+ enum vol_ref vol_ref;
+ enum conversion_mode_sel conv_mode;
+
+ int clk_div;
+ int sample_rate;
+ int res_mode;
+ u32 lst_adder_index;
+ u32 default_sample_time;
+
+ bool calibration;
+ bool ovwren;
+};
+
+struct vf610_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct aiodevice aiodev;
+ void (*aiodev_info)(struct device *);
+
+ u32 vref_uv;
+ u32 value;
+ struct regulator *vref;
+
+ u32 max_adck_rate[3];
+ struct vf610_adc_feature adc_feature;
+
+ u32 sample_freq_avail[5];
+
+ struct aiochannel aiochan[16];
+};
+
+static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
+static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 };
+
+static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+ unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
+ u32 adck_period, lst_addr_min;
+ int divisor, i;
+
+ adck_rate = info->max_adck_rate[adc_feature->conv_mode];
+
+ if (adck_rate) {
+ /* calculate clk divider which is within specification */
+ divisor = ipg_rate / adck_rate;
+ adc_feature->clk_div = 1 << fls(divisor + 1);
+ } else {
+ /* fall-back value using a safe divisor */
+ adc_feature->clk_div = 8;
+ }
+
+ adck_rate = ipg_rate / adc_feature->clk_div;
+
+ /*
+ * Determine the long sample time adder value to be used based
+ * on the default minimum sample time provided.
+ */
+ adck_period = NSEC_PER_SEC / adck_rate;
+ lst_addr_min = adc_feature->default_sample_time / adck_period;
+ for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) {
+ if (vf610_lst_adder[i] > lst_addr_min) {
+ adc_feature->lst_adder_index = i;
+ break;
+ }
+ }
+
+ /*
+ * Calculate ADC sample frequencies
+ * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
+ * which is the same as bus clock.
+ *
+ * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
+ * SFCAdder: fixed to 6 ADCK cycles
+ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
+ * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
+ * LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles
+ */
+ for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
+ info->sample_freq_avail[i] =
+ adck_rate / (6 + vf610_hw_avgs[i] *
+ (25 + vf610_lst_adder[adc_feature->lst_adder_index]));
+}
+
+static inline void vf610_adc_cfg_init(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+
+ /* set default Configuration for ADC controller */
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
+
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
+
+ adc_feature->res_mode = 12;
+ adc_feature->sample_rate = 1;
+
+ adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
+
+ vf610_adc_calculate_rates(info);
+}
+
+static void vf610_adc_cfg_post_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+ int cfg_data = 0;
+ int gc_data = 0;
+
+ switch (adc_feature->clk_sel) {
+ case VF610_ADCIOC_ALTCLK_SET:
+ cfg_data |= VF610_ADC_ALTCLK_SEL;
+ break;
+ case VF610_ADCIOC_ADACK_SET:
+ cfg_data |= VF610_ADC_ADACK_SEL;
+ break;
+ default:
+ break;
+ }
+
+ /* low power set for calibration */
+ cfg_data |= VF610_ADC_ADLPC_EN;
+
+ /* enable high speed for calibration */
+ cfg_data |= VF610_ADC_ADHSC_EN;
+
+ /* voltage reference */
+ switch (adc_feature->vol_ref) {
+ case VF610_ADCIOC_VR_VREF_SET:
+ break;
+ case VF610_ADCIOC_VR_VALT_SET:
+ cfg_data |= VF610_ADC_REFSEL_VALT;
+ break;
+ case VF610_ADCIOC_VR_VBG_SET:
+ cfg_data |= VF610_ADC_REFSEL_VBG;
+ break;
+ default:
+ dev_err(info->dev, "error voltage reference\n");
+ }
+
+ /* data overwrite enable */
+ if (adc_feature->ovwren)
+ cfg_data |= VF610_ADC_OVWREN;
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+ writel(gc_data, info->regs + VF610_REG_ADC_GC);
+}
+
+static void vf610_adc_calibration(struct vf610_adc *info)
+{
+ int adc_gc, hc_cfg;
+ u64 start;
+ int coco;
+
+ if (!info->adc_feature.calibration)
+ return;
+
+ hc_cfg = VF610_ADC_CONV_DISABLE;
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ adc_gc = readl(info->regs + VF610_REG_ADC_GC);
+ writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
+
+ start = get_time_ns();
+ do {
+ if (is_timeout(start, VF610_ADC_TIMEOUT_NSEC)) {
+ dev_err(info->dev, "Timeout for adc calibration\n");
+ break;
+ }
+
+ coco = readl(info->regs + VF610_REG_ADC_HS);
+ } while (!(coco & VF610_ADC_HS_COCO0));
+
+ adc_gc = readl(info->regs + VF610_REG_ADC_GS);
+ if (adc_gc & VF610_ADC_CALF)
+ dev_err(info->dev, "ADC calibration failed\n");
+
+ info->adc_feature.calibration = false;
+}
+
+static void vf610_adc_cfg_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &(info->adc_feature);
+ int cfg_data;
+
+ cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
+
+ cfg_data &= ~VF610_ADC_ADLPC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
+ cfg_data |= VF610_ADC_ADLPC_EN;
+
+ cfg_data &= ~VF610_ADC_ADHSC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
+ cfg_data |= VF610_ADC_ADHSC_EN;
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+}
+
+static void vf610_adc_sample_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &(info->adc_feature);
+ int cfg_data, gc_data;
+
+ cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
+ gc_data = readl(info->regs + VF610_REG_ADC_GC);
+
+ /* resolution mode */
+ cfg_data &= ~VF610_ADC_MODE_MASK;
+ switch (adc_feature->res_mode) {
+ case 8:
+ cfg_data |= VF610_ADC_MODE_BIT8;
+ break;
+ case 10:
+ cfg_data |= VF610_ADC_MODE_BIT10;
+ break;
+ case 12:
+ cfg_data |= VF610_ADC_MODE_BIT12;
+ break;
+ default:
+ dev_err(info->dev, "error resolution mode\n");
+ break;
+ }
+
+ /* clock select and clock divider */
+ cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
+ switch (adc_feature->clk_div) {
+ case 1:
+ break;
+ case 2:
+ cfg_data |= VF610_ADC_CLK_DIV2;
+ break;
+ case 4:
+ cfg_data |= VF610_ADC_CLK_DIV4;
+ break;
+ case 8:
+ cfg_data |= VF610_ADC_CLK_DIV8;
+ break;
+ case 16:
+ switch (adc_feature->clk_sel) {
+ case VF610_ADCIOC_BUSCLK_SET:
+ cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
+ break;
+ default:
+ dev_err(info->dev, "error clk divider\n");
+ break;
+ }
+ break;
+ }
+
+ /*
+ * Set ADLSMP and ADSTS based on the Long Sample Time Adder value
+ * determined.
+ */
+ switch (adc_feature->lst_adder_index) {
+ case VF610_ADCK_CYCLES_3:
+ break;
+ case VF610_ADCK_CYCLES_5:
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_7:
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_9:
+ cfg_data |= VF610_ADC_ADSTS_LONG;
+ break;
+ case VF610_ADCK_CYCLES_13:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ break;
+ case VF610_ADCK_CYCLES_17:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_21:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_25:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ default:
+ dev_err(info->dev, "error in sample time select\n");
+ }
+
+ /* update hardware average selection */
+ cfg_data &= ~VF610_ADC_AVGS_MASK;
+ gc_data &= ~VF610_ADC_AVGEN;
+ switch (adc_feature->sample_rate) {
+ case VF610_ADC_SAMPLE_1:
+ break;
+ case VF610_ADC_SAMPLE_4:
+ gc_data |= VF610_ADC_AVGEN;
+ break;
+ case VF610_ADC_SAMPLE_8:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_8;
+ break;
+ case VF610_ADC_SAMPLE_16:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_16;
+ break;
+ case VF610_ADC_SAMPLE_32:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_32;
+ break;
+ default:
+ dev_err(info->dev,
+ "error hardware sample average select\n");
+ }
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+ writel(gc_data, info->regs + VF610_REG_ADC_GC);
+}
+
+static void vf610_adc_hw_init(struct vf610_adc *info)
+{
+ /* CFG: Feature set */
+ vf610_adc_cfg_post_set(info);
+ vf610_adc_sample_set(info);
+
+ /* adc calibration */
+ vf610_adc_calibration(info);
+
+ /* CFG: power and speed set */
+ vf610_adc_cfg_set(info);
+}
+
+static int __vf610_adc_read_data(struct vf610_adc *info)
+{
+ int result;
+
+ result = readl(info->regs + VF610_REG_ADC_R0);
+
+ switch (info->adc_feature.res_mode) {
+ case 8:
+ result &= 0xFF;
+ break;
+ case 10:
+ result &= 0x3FF;
+ break;
+ case 12:
+ result &= 0xFFF;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static int vf610_adc_read_data(struct vf610_adc *info)
+{
+ int coco;
+ int ret = -EAGAIN;
+
+ coco = readl(info->regs + VF610_REG_ADC_HS);
+ if (coco & VF610_ADC_HS_COCO0) {
+ ret = __vf610_adc_read_data(info);
+ }
+
+ return ret;
+}
+
+static int vf610_read_sample(struct aiochannel *chan, int *val)
+{
+ struct vf610_adc *info = container_of(chan->aiodev, struct vf610_adc, aiodev);
+ unsigned int hc_cfg;
+ u64 raw64, start;
+ int ret;
+
+ hc_cfg = VF610_ADC_ADCHC(chan->index & 0x0f);
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ start = get_time_ns();
+ do {
+ if (is_timeout(start, VF610_ADC_TIMEOUT_NSEC)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ ret = vf610_adc_read_data(info);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ return ret;
+
+ raw64 = ret;
+ raw64 *= info->vref_uv;
+ raw64 = div_u64(raw64, 1000);
+ *val = div_u64(raw64, (1 << 12));
+
+ return 0;
+}
+
+
+static const struct of_device_id vf610_adc_match[] = {
+ { .compatible = "fsl,vf610-adc", },
+ { /* sentinel */ }
+};
+
+static void vf610_adc_devinfo(struct device *dev)
+{
+ struct vf610_adc *info = dev->parent->priv;
+
+ if (info->aiodev_info)
+ info->aiodev_info(dev);
+
+ pr_info("Sample Rate: %u\n", info->sample_freq_avail[info->adc_feature.sample_rate]);
+}
+
+static int vf610_adc_probe(struct device *dev)
+{
+ struct aiodevice *aiodev;
+ struct vf610_adc *info;
+ int ret, i;
+
+ info = xzalloc(sizeof(*info));
+ info->dev = dev;
+
+ info->regs = dev_request_mem_region(dev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->clk = clk_get(dev, "adc");
+ if (IS_ERR(info->clk)) {
+ dev_err(dev, "failed getting clock, err = %ld\n",
+ PTR_ERR(info->clk));
+ return PTR_ERR(info->clk);
+ }
+
+ info->vref = regulator_get(dev, "vref");
+ if (IS_ERR(info->vref))
+ return PTR_ERR(info->vref);
+
+ of_property_read_u32_array(dev->device_node, "fsl,adck-max-frequency", info->max_adck_rate, 3);
+
+ info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME;
+ of_property_read_u32(dev->device_node, "min-sample-time", &info->adc_feature.default_sample_time);
+
+ dev->priv = info;
+ aiodev = &info->aiodev;
+
+ aiodev->num_channels = 16;
+ aiodev->hwdev = dev;
+ aiodev->read = vf610_read_sample;
+ aiodev->channels = xzalloc(aiodev->num_channels * sizeof(aiodev->channels[0]));
+
+ for (i = 0; i < aiodev->num_channels; i++) {
+ aiodev->channels[i] = &info->aiochan[i];
+ info->aiochan[i].unit = "mV";
+ }
+
+ ret = regulator_enable(info->vref);
+ if (ret)
+ return ret;
+
+ info->vref_uv = regulator_get_voltage(info->vref);
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ dev_err(dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_adc_clk_enable;
+ }
+
+ vf610_adc_cfg_init(info);
+ vf610_adc_hw_init(info);
+
+ ret = aiodevice_register(aiodev);
+ if (ret < 0) {
+ dev_err(dev, "Couldn't register the device.\n");
+ goto error_adc_buffer_init;
+ }
+
+ info->aiodev_info = aiodev->dev.info;
+ aiodev->dev.info = vf610_adc_devinfo;
+
+ return 0;
+
+error_adc_buffer_init:
+ clk_disable(info->clk);
+error_adc_clk_enable:
+ regulator_disable(info->vref);
+
+ return ret;
+}
+
+static void vf610_adc_remove(struct device *dev)
+{
+ struct vf610_adc *info = dev->priv;
+
+ regulator_disable(info->vref);
+ clk_disable(info->clk);
+}
+
+static struct driver vf610_adc_driver = {
+ .probe = vf610_adc_probe,
+ .remove = vf610_adc_remove,
+ .name = DRIVER_NAME,
+ .of_compatible = vf610_adc_match,
+};
+
+device_platform_driver(vf610_adc_driver);
+
+MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
+MODULE_DESCRIPTION("Freescale VF610 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 571aa0e8bf..cf43aaa76e 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -28,7 +28,7 @@ amba_lookup(const struct amba_id *table, struct amba_device *dev)
return ret ? table : NULL;
}
-static int amba_match(struct device_d *dev, struct driver_d *drv)
+static int amba_match(struct device *dev, struct driver *drv)
{
struct amba_device *pcdev = to_amba_device(dev);
@@ -55,7 +55,7 @@ static int amba_get_enable_pclk(struct amba_device *pcdev)
return ret;
}
-static int amba_probe(struct device_d *dev)
+static int amba_probe(struct device *dev)
{
struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *pcdrv = to_amba_driver(dev->driver);
@@ -64,7 +64,7 @@ static int amba_probe(struct device_d *dev)
return pcdrv->probe(pcdev, id);
}
-static void amba_remove(struct device_d *dev)
+static void amba_remove(struct device *dev)
{
struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *drv = to_amba_driver(dev->driver);
@@ -163,7 +163,7 @@ int amba_device_add(struct amba_device *dev)
}
struct amba_device *
-amba_aphb_device_add(struct device_d *parent, const char *name, int id,
+amba_aphb_device_add(struct device *parent, const char *name, int id,
resource_size_t base, size_t size,
void *pdata, unsigned int periphid)
{
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index f4b93183ce..de67482881 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -541,14 +541,14 @@ void ahci_print_info(struct ahci_device *ahci)
cap2 & HOST_CAP2_BOH ? "boh " : "");
}
-void ahci_info(struct device_d *dev)
+void ahci_info(struct device *dev)
{
struct ahci_device *ahci = dev->priv;
ahci_print_info(ahci);
}
-static int ahci_detect(struct device_d *dev)
+static int ahci_detect(struct device *dev)
{
struct ahci_device *ahci = dev->priv;
int n_ports = max_t(int, ahci->n_ports, fls(ahci->port_map));
@@ -624,6 +624,7 @@ int ahci_add_host(struct ahci_device *ahci)
ahci_port->ata.dev = ahci->dev;
ahci_port->port_mmio = ahci_port_base(ahci->mmio_base, i);
ahci_port->ata.ops = &ahci_ops;
+ ahci_port->ata.ahci = true;
ata_port_register(&ahci_port->ata);
}
@@ -636,7 +637,7 @@ int ahci_add_host(struct ahci_device *ahci)
return 0;
}
-static int ahci_probe(struct device_d *dev)
+static int ahci_probe(struct device *dev)
{
struct resource *iores;
struct ahci_device *ahci;
@@ -669,8 +670,9 @@ static __maybe_unused struct of_device_id ahci_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ahci_dt_ids);
-static struct driver_d ahci_driver = {
+static struct driver ahci_driver = {
.name = "ahci",
.probe = ahci_probe,
.of_compatible = DRV_OF_COMPAT(ahci_dt_ids),
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 77196592ed..196bde73c2 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -184,7 +184,7 @@ struct ahci_port {
};
struct ahci_device {
- struct device_d *dev;
+ struct device *dev;
struct ahci_port ports[AHCI_MAX_PORTS];
u32 n_ports;
void __iomem *mmio_base;
@@ -197,6 +197,6 @@ struct ahci_device {
int ahci_add_host(struct ahci_device *ahci);
void ahci_print_info(struct ahci_device *ahci);
-void ahci_info(struct device_d *dev);
+void ahci_info(struct device *dev);
#endif
diff --git a/drivers/ata/disk_ata_drive.c b/drivers/ata/disk_ata_drive.c
index 7df0879b19..a49acc1641 100644
--- a/drivers/ata/disk_ata_drive.c
+++ b/drivers/ata/disk_ata_drive.c
@@ -199,7 +199,7 @@ static int ata_port_init(struct ata_port *port)
{
int rc;
struct ata_port_operations *ops = port->ops;
- struct device_d *dev = &port->class_dev;
+ struct device *dev = &port->class_dev;
if (ops->init) {
rc = ops->init(port);
@@ -245,6 +245,7 @@ static int ata_port_init(struct ata_port *port)
port->blk.num_blocks = ata_id_n_sectors(port->id);
port->blk.blockbits = SECTOR_SHIFT;
+ port->blk.type = port->ahci ? BLK_TYPE_AHCI : BLK_TYPE_IDE;
rc = blockdevice_register(&port->blk);
if (rc != 0) {
@@ -254,11 +255,6 @@ static int ata_port_init(struct ata_port *port)
dev_info(dev, "registered /dev/%s\n", port->blk.cdev.name);
- /* create partitions on demand */
- rc = parse_partition_table(&port->blk);
- if (rc != 0)
- dev_warn(dev, "No partition table found\n");
-
return 0;
on_error:
@@ -293,14 +289,14 @@ static int ata_set_probe(struct param_d *param, void *priv)
return ata_port_detect(port);
}
-static int ata_detect(struct device_d *dev)
+static int ata_detect(struct device *dev)
{
struct ata_port *port = container_of(dev, struct ata_port, class_dev);
return ata_port_detect(port);
}
-static void ata_info(struct device_d *dev)
+static void ata_info(struct device *dev)
{
struct ata_port *port = container_of(dev, struct ata_port, class_dev);
diff --git a/drivers/ata/ide-sff.c b/drivers/ata/ide-sff.c
index 69055e0585..f25dfeae43 100644
--- a/drivers/ata/ide-sff.c
+++ b/drivers/ata/ide-sff.c
@@ -26,7 +26,7 @@ static inline uint8_t ata_rd_byte(struct ide_port *ide, void __iomem *addr)
if (ide->io.mmio)
return readb(addr);
else
- return (uint8_t) inb((int) addr);
+ return (uint8_t) inb((ulong)addr);
}
/**
@@ -42,7 +42,7 @@ static inline void ata_wr_byte(struct ide_port *ide, uint8_t value,
if (ide->io.mmio)
writeb(value, addr);
else
- outb(value, (int) addr);
+ outb(value, (ulong)addr);
}
/**
@@ -57,7 +57,7 @@ static inline uint16_t ata_rd_word(struct ide_port *ide,
if (ide->io.mmio)
return readw(addr);
else
- return (uint16_t) inw((int) addr);
+ return (uint16_t) inw((ulong)addr);
}
/**
@@ -73,7 +73,7 @@ static inline void ata_wr_word(struct ide_port *ide, uint16_t value,
if (ide->io.mmio)
writew(value, addr);
else
- outw(value, (int) addr);
+ outw(value, (ulong)addr);
}
/**
diff --git a/drivers/ata/intf_platform_ide.c b/drivers/ata/intf_platform_ide.c
index 96ea3717b6..0d69b4b0c3 100644
--- a/drivers/ata/intf_platform_ide.c
+++ b/drivers/ata/intf_platform_ide.c
@@ -65,7 +65,7 @@ static void platform_ide_setup_port(void *reg_base, void *alt_base,
}
}
-static int platform_ide_probe(struct device_d *dev)
+static int platform_ide_probe(struct device *dev)
{
struct resource *iores;
int rc;
@@ -74,7 +74,7 @@ static int platform_ide_probe(struct device_d *dev)
void *reg_base, *alt_base = NULL;
struct resource *reg, *alt;
int mmio = 0;
- struct device_node *dn = dev->device_node;
+ struct device_node *dn = dev->of_node;
u32 ioport_shift = 0;
int dataif_be = 0;
void (*reset)(int) = NULL;
@@ -144,8 +144,9 @@ static __maybe_unused struct of_device_id platform_ide_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, platform_ide_dt_ids);
-static struct driver_d platform_ide_driver = {
+static struct driver platform_ide_driver = {
.name = "ide_intf",
.probe = platform_ide_probe,
.of_compatible = DRV_OF_COMPAT(platform_ide_dt_ids),
diff --git a/drivers/ata/pata-imx.c b/drivers/ata/pata-imx.c
index 383f5f3bc1..e10babd1bb 100644
--- a/drivers/ata/pata-imx.c
+++ b/drivers/ata/pata-imx.c
@@ -132,14 +132,14 @@ static void imx_pata_setup_port(void *reg_base, void *alt_base,
}
}
-static int pata_imx_detect(struct device_d *dev)
+static int pata_imx_detect(struct device *dev)
{
struct ide_port *ide = dev->priv;
return ata_port_detect(&ide->port);
}
-static int imx_pata_probe(struct device_d *dev)
+static int imx_pata_probe(struct device *dev)
{
struct resource *iores;
struct ide_port *ide;
@@ -171,7 +171,7 @@ static int imx_pata_probe(struct device_d *dev)
ide->port.dev = dev;
- ide->port.devname = xstrdup(of_alias_get(dev->device_node));
+ ide->port.devname = xstrdup(of_alias_get(dev->of_node));
dev->priv = ide;
dev->detect = pata_imx_detect;
@@ -201,8 +201,9 @@ static __maybe_unused struct of_device_id imx_pata_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, imx_pata_dt_ids);
-static struct driver_d imx_pata_driver = {
+static struct driver imx_pata_driver = {
.name = "imx-pata",
.probe = imx_pata_probe,
.of_compatible = DRV_OF_COMPAT(imx_pata_dt_ids),
diff --git a/drivers/ata/sata-imx.c b/drivers/ata/sata-imx.c
index 9415e34055..5bcbfca5b5 100644
--- a/drivers/ata/sata-imx.c
+++ b/drivers/ata/sata-imx.c
@@ -10,8 +10,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <malloc.h>
-#include <mach/imx53-regs.h>
-#include <mach/imx6-regs.h>
+#include <mach/imx/imx53-regs.h>
+#include <mach/imx/imx6-regs.h>
#include <mfd/imx6q-iomuxc-gpr.h>
#include "ahci.h"
@@ -83,7 +83,7 @@ static int imx_sata_init_1ms(struct imx_ahci *imx_ahci)
return 0;
}
-static int imx_sata_probe(struct device_d *dev)
+static int imx_sata_probe(struct device *dev)
{
struct resource *iores;
struct imx_ahci *imx_ahci;
@@ -158,8 +158,9 @@ static __maybe_unused struct of_device_id imx_sata_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_sata_dt_ids);
-static struct driver_d imx_sata_driver = {
+static struct driver imx_sata_driver = {
.name = "imx-sata",
.probe = imx_sata_probe,
.id_table = imx_sata_ids,
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 05b27f1008..f92d311c4a 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -90,7 +90,7 @@ static void mv_soc_65n_phy_errata(void __iomem *base)
writel(reg, base + PHY_MODE9_GEN1);
}
-static int mv_sata_probe(struct device_d *dev)
+static int mv_sata_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -215,8 +215,9 @@ static const struct of_device_id mv_sata_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mv_sata_dt_ids);
-static struct driver_d mv_sata_driver = {
+static struct driver mv_sata_driver = {
.name = "mv_sata",
.probe = mv_sata_probe,
.of_compatible = mv_sata_dt_ids,
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index eebb60ce91..21a4793cfa 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -4,4 +4,9 @@ config PM_GENERIC_DOMAINS
bool
config FEATURE_CONTROLLER
- bool "Feature controller support" if COMPILE_TEST
+ bool "Feature controller support" if COMPILE_TEST || SANDBOX
+
+config SOC_BUS
+ bool
+
+source "drivers/base/regmap/Kconfig"
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index e8e354cdaa..acc53763da 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -7,3 +7,4 @@ obj-y += regmap/
obj-$(CONFIG_PM_GENERIC_DOMAINS) += power.o
obj-$(CONFIG_FEATURE_CONTROLLER) += featctrl.o
+obj-$(CONFIG_SOC_BUS) += soc.o
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 3c2bab937a..a5c9c930da 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -47,9 +47,9 @@ int bus_register(struct bus_type *bus)
return 0;
}
-int device_match(struct device_d *dev, struct driver_d *drv)
+int device_match(struct device *dev, struct driver *drv)
{
- if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node &&
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node &&
drv->of_compatible)
return of_match(dev, drv);
@@ -70,7 +70,7 @@ int device_match(struct device_d *dev, struct driver_d *drv)
return -1;
}
-int device_match_of_modalias(struct device_d *dev, struct driver_d *drv)
+int device_match_of_modalias(struct device *dev, struct driver *drv)
{
const struct platform_device_id *id = drv->id_table;
const char *of_modalias = NULL, *p;
@@ -80,10 +80,10 @@ int device_match_of_modalias(struct device_d *dev, struct driver_d *drv)
if (!device_match(dev, drv))
return 0;
- if (!id || !IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
+ if (!id || !IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node)
return -1;
- of_property_for_each_string(dev->device_node, "compatible", prop, compat) {
+ of_property_for_each_string(dev->of_node, "compatible", prop, compat) {
p = strchr(compat, ',');
of_modalias = p ? p + 1 : compat;
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index cbe1c974f4..fbc5cbebe0 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -24,6 +24,7 @@
#include <fs.h>
#include <of.h>
#include <linux/list.h>
+#include <linux/overflow.h>
#include <linux/err.h>
#include <complete.h>
#include <pinctrl.h>
@@ -46,9 +47,11 @@ LIST_HEAD(active_device_list);
EXPORT_SYMBOL(active_device_list);
static LIST_HEAD(deferred);
-struct device_d *find_device(const char *str)
+static LIST_HEAD(device_alias_list);
+
+struct device *find_device(const char *str)
{
- struct device_d *dev;
+ struct device *dev;
struct device_node *np;
dev = get_device_by_name(str);
@@ -62,21 +65,27 @@ struct device_d *find_device(const char *str)
return NULL;
}
-struct device_d *get_device_by_name(const char *name)
+struct device *get_device_by_name(const char *name)
{
- struct device_d *dev;
+ struct device *dev;
+ struct device_alias *alias;
for_each_device(dev) {
if(!strcmp(dev_name(dev), name))
return dev;
}
+ list_for_each_entry(alias, &device_alias_list, list) {
+ if(!strcmp(alias->name, name))
+ return alias->dev;
+ }
+
return NULL;
}
-static struct device_d *get_device_by_name_id(const char *name, int id)
+static struct device *get_device_by_name_id(const char *name, int id)
{
- struct device_d *dev;
+ struct device *dev;
for_each_device(dev) {
if(!strcmp(dev->name, name) && id == dev->id)
@@ -97,12 +106,21 @@ int get_free_deviceid(const char *name_template)
};
}
-int device_probe(struct device_d *dev)
+static void dev_report_permanent_probe_deferral(struct device *dev)
+{
+ if (dev->deferred_probe_reason)
+ dev_err(dev, "probe permanently deferred (%s)\n",
+ dev->deferred_probe_reason);
+ else
+ dev_err(dev, "probe permanently deferred\n");
+}
+
+int device_probe(struct device *dev)
{
static int depth = 0;
int ret;
- ret = of_feature_controller_check(dev->device_node);
+ ret = of_feature_controller_check(dev->of_node);
if (ret < 0)
return ret;
if (ret == FEATCTRL_GATED) {
@@ -115,43 +133,51 @@ int device_probe(struct device_d *dev)
pr_report_probe("%*sprobe-> %s\n", depth * 4, "", dev_name(dev));
pinctrl_select_state_default(dev);
- of_clk_set_defaults(dev->device_node, false);
+ of_clk_set_defaults(dev->of_node, false);
list_add(&dev->active, &active_device_list);
- ret = dev->bus->probe(dev);
- if (ret == 0)
- goto out;
+ if (dev->bus->probe)
+ ret = dev->bus->probe(dev);
+ else if (dev->driver->probe)
+ ret = dev->driver->probe(dev);
+ else
+ ret = 0;
- if (ret == -EPROBE_DEFER) {
- list_del(&dev->active);
- list_add(&dev->active, &deferred);
+ depth--;
+ switch (ret) {
+ case 0:
+ return 0;
+ case -EPROBE_DEFER:
/*
* -EPROBE_DEFER should never appear on a deep-probe machine so
* inform the user immediately.
*/
- if (deep_probe_is_supported())
- dev_err(dev, "probe deferred\n");
- else
- dev_dbg(dev, "probe deferred\n");
- goto out;
+ if (deep_probe_is_supported()) {
+ dev_report_permanent_probe_deferral(dev);
+ break;
+ }
+
+ list_move(&dev->active, &deferred);
+
+ dev_dbg(dev, "probe deferred\n");
+ return -EPROBE_DEFER;
+ case -ENODEV:
+ case -ENXIO:
+ dev_dbg(dev, "probe failed: %pe\n", ERR_PTR(ret));
+ break;
+ default:
+ dev_err(dev, "probe failed: %pe\n", ERR_PTR(ret));
+ break;
}
- list_del(&dev->active);
- INIT_LIST_HEAD(&dev->active);
+ list_del_init(&dev->active);
- if (ret == -ENODEV || ret == -ENXIO)
- dev_dbg(dev, "probe failed: %s\n", strerror(-ret));
- else
- dev_err(dev, "probe failed: %s\n", strerror(-ret));
-
-out:
- depth--;
return ret;
}
-int device_detect(struct device_d *dev)
+int device_detect(struct device *dev)
{
if (!dev->detect)
return -ENOSYS;
@@ -162,7 +188,7 @@ int device_detect_by_name(const char *__devname)
{
char *devname = xstrdup(__devname);
char *str = devname;
- struct device_d *dev;
+ struct device *dev;
int ret = -ENODEV;
while (1) {
@@ -185,13 +211,13 @@ int device_detect_by_name(const char *__devname)
void device_detect_all(void)
{
- struct device_d *dev;
+ struct device *dev;
for_each_device(dev)
device_detect(dev);
}
-static int match(struct driver_d *drv, struct device_d *dev)
+static int match(struct driver *drv, struct device *dev)
{
int ret;
@@ -200,7 +226,7 @@ static int match(struct driver_d *drv, struct device_d *dev)
dev->driver = drv;
- if (dev->bus->match(dev, drv))
+ if (dev->bus->match && dev->bus->match(dev, drv))
goto err_out;
ret = device_probe(dev);
if (ret)
@@ -212,9 +238,9 @@ err_out:
return -1;
}
-int register_device(struct device_d *new_device)
+int register_device(struct device *new_device)
{
- struct driver_d *drv;
+ struct driver *drv;
if (new_device->id == DEVICE_ID_DYNAMIC) {
new_device->id = get_free_deviceid(new_device->name);
@@ -259,17 +285,24 @@ int register_device(struct device_d *new_device)
}
EXPORT_SYMBOL(register_device);
-int unregister_device(struct device_d *old_dev)
+int unregister_device(struct device *old_dev)
{
+ struct device_alias *alias, *at;
struct cdev *cdev, *ct;
- struct device_d *child, *dt;
+ struct device *child, *dt;
+ struct device_node *np;
dev_dbg(old_dev, "unregister\n");
dev_remove_parameters(old_dev);
if (old_dev->driver)
- old_dev->bus->remove(old_dev);
+ device_remove(old_dev);
+
+ list_for_each_entry_safe(alias, at, &device_alias_list, list) {
+ if(alias->dev == old_dev)
+ list_del(&alias->list);
+ }
list_for_each_entry_safe(child, dt, &old_dev->children, sibling) {
dev_dbg(old_dev, "unregister child %s\n", dev_name(child));
@@ -277,9 +310,9 @@ int unregister_device(struct device_d *old_dev)
}
list_for_each_entry_safe(cdev, ct, &old_dev->cdevs, devices_list) {
- if (cdev->master) {
+ if (cdev_is_partition(cdev)) {
dev_dbg(old_dev, "unregister part %s\n", cdev->name);
- devfs_del_partition(cdev->name);
+ cdevfs_del_partition(cdev);
}
}
@@ -290,8 +323,10 @@ int unregister_device(struct device_d *old_dev)
/* remove device from parents child list */
if (old_dev->parent)
list_del(&old_dev->sibling);
- if (dev_of_node(old_dev))
- old_dev->device_node->dev = NULL;
+
+ np = dev_of_node(old_dev);
+ if (np && np->dev == old_dev)
+ np->dev = NULL;
return 0;
}
@@ -304,7 +339,7 @@ EXPORT_SYMBOL(unregister_device);
* This frees dynamically allocated resources allocated during device
* lifetime, but not the device itself.
*/
-void free_device_res(struct device_d *dev)
+void free_device_res(struct device *dev)
{
free(dev->name);
dev->name = NULL;
@@ -321,7 +356,7 @@ EXPORT_SYMBOL(free_device_res);
* This frees dynamically allocated resources allocated during device
* lifetime and finally the device itself.
*/
-void free_device(struct device_d *dev)
+void free_device(struct device *dev)
{
free_device_res(dev);
free(dev);
@@ -337,8 +372,8 @@ EXPORT_SYMBOL(free_device);
*/
static int device_probe_deferred(void)
{
- struct device_d *dev, *tmp;
- struct driver_d *drv;
+ struct device *dev, *tmp;
+ struct driver *drv;
bool success;
do {
@@ -361,20 +396,16 @@ static int device_probe_deferred(void)
}
} while (success);
- list_for_each_entry(dev, &deferred, active) {
- if (dev->deferred_probe_reason)
- dev_err(dev, "probe permanently deferred (%s)\n",
- dev->deferred_probe_reason);
- else
- dev_err(dev, "probe permanently deferred\n");
- }
+ list_for_each_entry(dev, &deferred, active)
+ dev_report_permanent_probe_deferral(dev);
+
return 0;
}
late_initcall(device_probe_deferred);
-struct driver_d *get_driver_by_name(const char *name)
+struct driver *get_driver_by_name(const char *name)
{
- struct driver_d *drv;
+ struct driver *drv;
for_each_driver(drv) {
if(!strcmp(name, drv->name))
@@ -384,9 +415,9 @@ struct driver_d *get_driver_by_name(const char *name)
return NULL;
}
-int register_driver(struct driver_d *drv)
+int register_driver(struct driver *drv)
{
- struct device_d *dev = NULL;
+ struct device *dev = NULL;
if (!drv->name)
return -EINVAL;
@@ -405,7 +436,24 @@ int register_driver(struct driver_d *drv)
}
EXPORT_SYMBOL(register_driver);
-struct resource *dev_get_resource(struct device_d *dev, unsigned long type,
+void unregister_driver(struct driver *drv)
+{
+ struct device *dev;
+
+ list_del(&drv->list);
+ list_del(&drv->bus_list);
+
+ bus_for_each_device(drv->bus, dev) {
+ if (dev->driver == drv) {
+ device_remove(dev);
+ dev->driver = NULL;
+ list_del(&dev->active);
+ INIT_LIST_HEAD(&dev->active);
+ }
+ }
+}
+
+struct resource *dev_get_resource(struct device *dev, unsigned long type,
int num)
{
int i, n = 0;
@@ -422,7 +470,7 @@ struct resource *dev_get_resource(struct device_d *dev, unsigned long type,
return ERR_PTR(-ENOENT);
}
-void *dev_get_mem_region(struct device_d *dev, int num)
+void *dev_get_mem_region(struct device *dev, int num)
{
struct resource *res;
@@ -434,7 +482,7 @@ void *dev_get_mem_region(struct device_d *dev, int num)
}
EXPORT_SYMBOL(dev_get_mem_region);
-struct resource *dev_get_resource_by_name(struct device_d *dev,
+struct resource *dev_get_resource_by_name(struct device *dev,
unsigned long type,
const char *name)
{
@@ -453,7 +501,19 @@ struct resource *dev_get_resource_by_name(struct device_d *dev,
return ERR_PTR(-ENOENT);
}
-struct resource *dev_request_mem_resource_by_name(struct device_d *dev, const char *name)
+static struct resource *dev_request_iomem_resource(struct device *dev,
+ const struct resource *res)
+{
+ return request_iomem_region(dev_name(dev), res->start, res->end);
+}
+
+int dev_request_resource(struct device *dev, const struct resource *res)
+{
+ return PTR_ERR_OR_ZERO(dev_request_iomem_resource(dev, res));
+}
+EXPORT_SYMBOL(dev_request_resource);
+
+struct resource *dev_request_mem_resource_by_name(struct device *dev, const char *name)
{
struct resource *res;
@@ -461,11 +521,12 @@ struct resource *dev_request_mem_resource_by_name(struct device_d *dev, const ch
if (IS_ERR(res))
return ERR_CAST(res);
- return request_iomem_region(dev_name(dev), res->start, res->end);
+ return dev_request_iomem_resource(dev, res);
}
EXPORT_SYMBOL(dev_request_mem_resource_by_name);
-void __iomem *dev_request_mem_region_by_name(struct device_d *dev, const char *name)
+void __iomem *dev_request_mem_region_by_name(struct device *dev,
+ const char *name)
{
struct resource *res;
@@ -477,7 +538,26 @@ void __iomem *dev_request_mem_region_by_name(struct device_d *dev, const char *n
}
EXPORT_SYMBOL(dev_request_mem_region_by_name);
-struct resource *dev_request_mem_resource(struct device_d *dev, int num)
+void __iomem *dev_platform_get_and_ioremap_resource(struct device *dev,
+ int num,
+ struct resource **out_res)
+{
+ struct resource *res;
+
+ res = dev_request_mem_resource(dev, num);
+ if (IS_ERR(res))
+ return IOMEM_ERR_PTR(PTR_ERR(res));
+ else if (WARN_ON(IS_ERR_VALUE(res->start)))
+ return IOMEM_ERR_PTR(-EINVAL);
+
+ if (out_res)
+ *out_res = res;
+
+ return IOMEM(res->start);
+}
+EXPORT_SYMBOL(dev_platform_get_and_ioremap_resource);
+
+struct resource *dev_request_mem_resource(struct device *dev, int num)
{
struct resource *res;
@@ -485,22 +565,22 @@ struct resource *dev_request_mem_resource(struct device_d *dev, int num)
if (IS_ERR(res))
return ERR_CAST(res);
- return request_iomem_region(dev_name(dev), res->start, res->end);
+ return dev_request_iomem_resource(dev, res);
}
-void __iomem *dev_request_mem_region_err_null(struct device_d *dev, int num)
+void __iomem *dev_request_mem_region_err_null(struct device *dev, int num)
{
struct resource *res;
res = dev_request_mem_resource(dev, num);
- if (IS_ERR(res))
+ if (IS_ERR(res) || WARN_ON(!res->start))
return NULL;
return IOMEM(res->start);
}
EXPORT_SYMBOL(dev_request_mem_region_err_null);
-void __iomem *dev_request_mem_region(struct device_d *dev, int num)
+void __iomem *dev_request_mem_region(struct device *dev, int num)
{
struct resource *res;
@@ -537,9 +617,9 @@ int generic_memmap_ro(struct cdev *cdev, void **map, int flags)
*
* NOTE: This function expects dev->name to be free()-able, so extra
* precautions needs to be taken when mixing its usage with manual
- * assignement of device_d.name.
+ * assignement of device.name.
*/
-int dev_set_name(struct device_d *dev, const char *fmt, ...)
+int dev_set_name(struct device *dev, const char *fmt, ...)
{
va_list vargs;
int err;
@@ -562,24 +642,63 @@ int dev_set_name(struct device_d *dev, const char *fmt, ...)
}
EXPORT_SYMBOL_GPL(dev_set_name);
+/**
+ * dev_add_alias - add alias for device
+ * @dev: device
+ * @fmt: format string for the device's alias
+ */
+int dev_add_alias(struct device *dev, const char *fmt, ...)
+{
+ va_list va, va_copy;
+ unsigned int len;
+ struct device_alias *alias;
+
+ va_start(va, fmt);
+ va_copy(va_copy, va);
+ len = vsnprintf(NULL, 0, fmt, va_copy);
+ va_end(va_copy);
+
+ alias = malloc(struct_size(alias, name, len + 1));
+ if (!alias)
+ return -ENOMEM;
+
+ vsnprintf(alias->name, len + 1, fmt, va);
+
+ va_end(va);
+
+ alias->dev = dev;
+ list_add_tail(&alias->list, &device_alias_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dev_add_alias);
+
+bool device_remove(struct device *dev)
+{
+ if (dev->bus && dev->bus->remove)
+ dev->bus->remove(dev);
+ else if (dev->driver->remove)
+ dev->driver->remove(dev);
+ else
+ return false; /* nothing to do */
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(device_remove);
+
static void devices_shutdown(void)
{
- struct device_d *dev;
- int depth = 0;
+ struct device *dev;
list_for_each_entry(dev, &active_device_list, active) {
- if (dev->bus->remove) {
- depth++;
- pr_report_probe("%*sremove-> %s\n", depth * 4, "", dev_name(dev));
- dev->bus->remove(dev);
- dev->driver = NULL;
- depth--;
- }
+ if (device_remove(dev))
+ pr_report_probe("%*sremove-> %s\n", 1 * 4, "", dev_name(dev));
+ dev->driver = NULL;
}
}
devshutdown_exitcall(devices_shutdown);
-int dev_get_drvdata(struct device_d *dev, const void **data)
+int dev_get_drvdata(struct device *dev, const void **data)
{
if (dev->of_id_entry) {
*data = dev->of_id_entry->data;
@@ -594,7 +713,7 @@ int dev_get_drvdata(struct device_d *dev, const void **data)
return -ENODEV;
}
-const void *device_get_match_data(struct device_d *dev)
+const void *device_get_match_data(struct device *dev)
{
if (dev->of_id_entry)
return dev->of_id_entry->data;
@@ -605,7 +724,8 @@ const void *device_get_match_data(struct device_d *dev)
return NULL;
}
-static void device_set_deferred_probe_reason(struct device_d *dev, const struct va_format *vaf)
+static void device_set_deferred_probe_reason(struct device *dev,
+ const struct va_format *vaf)
{
char *reason;
char *last_char;
@@ -655,8 +775,8 @@ static void device_set_deferred_probe_reason(struct device_d *dev, const struct
* Returns @err.
*
*/
-int dev_err_probe(struct device_d *dev, int err, const char *fmt, ...);
-int dev_err_probe(struct device_d *dev, int err, const char *fmt, ...)
+int dev_err_probe(struct device *dev, int err, const char *fmt, ...);
+int dev_err_probe(struct device *dev, int err, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
@@ -679,7 +799,7 @@ EXPORT_SYMBOL_GPL(dev_err_probe);
/*
* device_find_child - device iterator for locating a particular device.
- * @parent: parent struct device_d
+ * @parent: parent struct device
* @match: Callback function to check device
* @data: Data to pass to match function
*
@@ -688,10 +808,10 @@ EXPORT_SYMBOL_GPL(dev_err_probe);
* current device can be obtained, this function will return to the caller
* and not iterate over any more devices.
*/
-struct device_d *device_find_child(struct device_d *parent, void *data,
- int (*match)(struct device_d *dev, void *data))
+struct device *device_find_child(struct device *parent, void *data,
+ int (*match)(struct device *dev, void *data))
{
- struct device_d *child;
+ struct device *child;
if (!parent)
return NULL;
diff --git a/drivers/base/featctrl.c b/drivers/base/featctrl.c
index abe21698ed..153720e5ee 100644
--- a/drivers/base/featctrl.c
+++ b/drivers/base/featctrl.c
@@ -46,17 +46,17 @@ static struct feature_controller *featctrl_get_from_provider(struct of_phandle_a
struct feature_controller *featctrl;
int ret;
- if (!spec)
- return ERR_PTR(-EINVAL);
-
ret = of_device_ensure_probed(spec->np);
if (ret < 0)
return ERR_PTR(ret);
+ if (spec->args_count > 1)
+ return ERR_PTR(-EINVAL);
+
/* Check if we have such a controller in our array */
list_for_each_entry(featctrl, &of_feature_controllers, list) {
if (dev_of_node(featctrl->dev) == spec->np) {
- *gateid = spec->args[0];
+ *gateid = spec->args_count ? spec->args[0] : 0;
return featctrl;
}
}
@@ -107,7 +107,7 @@ int of_feature_controller_check(struct device_node *np)
ret = featctrl->check(featctrl, gateid);
- dev_dbg(featctrl->dev, "checking %s: %d\n", np->full_name, ret);
+ dev_dbg(featctrl->dev, "checking %pOF: %d\n", np, ret);
if (ret == FEATCTRL_OKAY)
return FEATCTRL_OKAY;
@@ -135,12 +135,11 @@ static int of_featctrl_fixup(struct device_node *root, void *context)
dstnp = of_get_node_by_reproducible_name(root, srcnp);
if (!dstnp) {
- pr_debug("gated node %s not in fixup DT\n",
- srcnp->full_name);
+ pr_debug("gated node %pOF not in fixup DT\n", srcnp);
continue;
}
- pr_debug("fixing up gating of node %s\n", dstnp->full_name);
+ pr_debug("fixing up gating of node %pOF\n", dstnp);
/* Convention is deleting non-existing CPUs, not disable them. */
if (of_property_match_string(srcnp, "device_type", "cpu") >= 0)
of_delete_node(dstnp);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index c0ea2746b3..ac7c473c8c 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -11,7 +11,7 @@
#include <of.h>
#include <pm_domain.h>
-static int platform_probe(struct device_d *dev)
+static int platform_probe(struct device *dev)
{
int ret;
@@ -22,20 +22,14 @@ static int platform_probe(struct device_d *dev)
return dev->driver->probe(dev);
}
-static void platform_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
-int platform_driver_register(struct driver_d *drv)
+int platform_driver_register(struct driver *drv)
{
drv->bus = &platform_bus;
return register_driver(drv);
}
-int platform_device_register(struct device_d *new_device)
+int platform_device_register(struct device *new_device)
{
new_device->bus = &platform_bus;
@@ -46,7 +40,6 @@ struct bus_type platform_bus = {
.name = "platform",
.match = device_match,
.probe = platform_probe,
- .remove = platform_remove,
};
static int platform_init(void)
diff --git a/drivers/base/power.c b/drivers/base/power.c
index 3eabf3c897..f7629f554a 100644
--- a/drivers/base/power.c
+++ b/drivers/base/power.c
@@ -2,6 +2,7 @@
#include <common.h>
#include <driver.h>
#include <errno.h>
+#include <linux/device.h>
#include <of.h>
#include <pm_domain.h>
@@ -10,6 +11,14 @@
static LIST_HEAD(gpd_list);
+static inline struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+ if (IS_ERR_OR_NULL(dev->pm_domain))
+ return ERR_PTR(-EINVAL);
+
+ return dev->pm_domain;
+}
+
/**
* pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize.
@@ -31,6 +40,19 @@ int pm_genpd_init(struct generic_pm_domain *genpd, void *gov, bool is_off)
}
EXPORT_SYMBOL_GPL(pm_genpd_init);
+int pm_genpd_remove(struct generic_pm_domain *genpd)
+{
+ if (IS_ERR_OR_NULL(genpd))
+ return -EINVAL;
+
+ list_del(&genpd->gpd_list_node);
+
+ pr_debug("%s: removed %s\n", __func__, genpd->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm_genpd_remove);
+
/**
* struct of_genpd_provider - PM domain provider registration structure
* @link: Entry in global list of PM domain providers
@@ -80,6 +102,37 @@ static struct generic_pm_domain *genpd_xlate_simple(
}
/**
+ * genpd_xlate_onecell() - Xlate function using a single index.
+ * @genpdspec: OF phandle args to map into a PM domain
+ * @data: xlate function private data - pointer to struct genpd_onecell_data
+ *
+ * This is a generic xlate function that can be used to model simple PM domain
+ * controllers that have one device tree node and provide multiple PM domains.
+ * A single cell is used as an index into an array of PM domains specified in
+ * the genpd_onecell_data struct when registering the provider.
+ */
+static struct generic_pm_domain *genpd_xlate_onecell(
+ struct of_phandle_args *genpdspec,
+ void *data)
+{
+ struct genpd_onecell_data *genpd_data = data;
+ unsigned int idx = genpdspec->args[0];
+
+ if (genpdspec->args_count != 1)
+ return ERR_PTR(-EINVAL);
+
+ if (idx >= genpd_data->num_domains) {
+ pr_err("%s: invalid domain index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!genpd_data->domains[idx])
+ return ERR_PTR(-ENOENT);
+
+ return genpd_data->domains[idx];
+}
+
+/**
* genpd_add_provider() - Register a PM domain provider for a node
* @np: Device node pointer associated with the PM domain provider.
* @xlate: Callback for decoding PM domain from phandle arguments.
@@ -125,6 +178,69 @@ int of_genpd_add_provider_simple(struct device_node *np,
EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
/**
+ * of_genpd_add_provider_onecell() - Register a onecell PM domain provider
+ * @np: Device node pointer associated with the PM domain provider.
+ * @data: Pointer to the data associated with the PM domain provider.
+ */
+int of_genpd_add_provider_onecell(struct device_node *np,
+ struct genpd_onecell_data *data)
+{
+ struct generic_pm_domain *genpd;
+ unsigned int i;
+ int ret = -EINVAL;
+
+ if (!np || !data)
+ return -EINVAL;
+
+ if (!data->xlate)
+ data->xlate = genpd_xlate_onecell;
+
+ for (i = 0; i < data->num_domains; i++) {
+ genpd = data->domains[i];
+
+ if (!genpd)
+ continue;
+ if (!genpd_present(genpd))
+ goto error;
+ }
+
+ ret = genpd_add_provider(np, data->xlate, data);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ while (i--) {
+ genpd = data->domains[i];
+
+ if (!genpd)
+ continue;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell);
+
+/**
+ * of_genpd_del_provider() - Remove a previously registered PM domain provider
+ * @np: Device node pointer associated with the PM domain provider
+ */
+void of_genpd_del_provider(struct device_node *np)
+{
+ struct of_genpd_provider *cp, *tmp;
+
+ list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) {
+ if (cp->node == np) {
+ list_del(&cp->link);
+ kfree(cp);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(of_genpd_del_provider);
+
+/**
* genpd_get_from_provider() - Look-up PM domain
* @genpdspec: OF phandle args to use for look-up
*
@@ -173,7 +289,7 @@ static struct generic_pm_domain *genpd_get_from_provider(
return genpd;
}
-static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
+static int _genpd_power_on(struct generic_pm_domain *genpd)
{
if (!genpd->power_on)
return 0;
@@ -184,19 +300,18 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
/**
* genpd_power_on - Restore power to a given PM domain and its masters.
* @genpd: PM domain to power up.
- * @depth: nesting count for lockdep.
*
* Restore power to @genpd and all of its masters so that it is possible to
* resume a device belonging to it.
*/
-static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
+static int genpd_power_on(struct generic_pm_domain *genpd)
{
int ret;
if (!genpd || genpd_status_on(genpd))
return 0;
- ret = _genpd_power_on(genpd, true);
+ ret = _genpd_power_on(genpd);
if (ret)
return ret;
@@ -205,14 +320,56 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
return 0;
}
-static int __genpd_dev_pm_attach(struct device_d *dev, struct device_node *np,
+int pm_runtime_resume_and_get_genpd(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return PTR_ERR(genpd);
+
+ return genpd_power_on(genpd);
+}
+
+static void genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+ dev->pm_domain = genpd;
+}
+
+static void genpd_remove_device(struct generic_pm_domain *genpd,
+ struct device *dev)
+{
+ dev->pm_domain = NULL;
+}
+
+static bool have_genpd_providers;
+
+void genpd_activate(void)
+{
+ have_genpd_providers = true;
+}
+
+static struct bus_type genpd_bus_type = {
+ .name = "genpd",
+};
+
+static int __init genpd_bus_init(void)
+{
+ return bus_register(&genpd_bus_type);
+}
+core_initcall(genpd_bus_init);
+
+static int __genpd_dev_pm_attach(struct device *dev,
unsigned int index, bool power_on)
{
struct of_phandle_args pd_args;
struct generic_pm_domain *pd;
int ret;
- ret = of_parse_phandle_with_args(np, "power-domains",
+ if (!have_genpd_providers)
+ return 0;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", index, &pd_args);
if (ret < 0)
return ret;
@@ -231,8 +388,13 @@ static int __genpd_dev_pm_attach(struct device_d *dev, struct device_node *np,
dev_dbg(dev, "adding to PM domain %s\n", pd ? pd->name : "dummy");
- if (power_on)
- ret = genpd_power_on(pd, 0);
+ genpd_add_device(pd, dev);
+
+ if (power_on) {
+ ret = genpd_power_on(pd);
+ if (ret < 0)
+ genpd_remove_device(pd, dev);
+ }
return ret ?: 1;
}
@@ -250,23 +412,118 @@ static int __genpd_dev_pm_attach(struct device_d *dev, struct device_node *np,
* found or turned on, then return -EPROBE_DEFER to ensure that the device is
* not probed and to re-try again later.
*/
-int genpd_dev_pm_attach(struct device_d *dev)
+int genpd_dev_pm_attach(struct device *dev)
{
- if (!dev->device_node)
+ if (!dev->of_node)
return 0;
/*
* Devices with multiple PM domains must be attached separately, as we
* can only attach one PM domain per device.
*/
- if (of_count_phandle_with_args(dev->device_node, "power-domains",
+ if (of_count_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells") != 1)
return 0;
- return __genpd_dev_pm_attach(dev, dev->device_node, 0, true);
+ return __genpd_dev_pm_attach(dev, 0, true);
}
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
+/**
+ * dev_pm_domain_attach_by_id - Associate a device with one of its PM domains.
+ * @dev: The device used to lookup the PM domain.
+ * @index: The index of the PM domain.
+ *
+ * As @dev may only be attached to a single PM domain, the backend PM domain
+ * provider creates a virtual device to attach instead. If attachment succeeds,
+ * the ->detach() callback in the struct dev_pm_domain are assigned by the
+ * corresponding backend attach function, as to deal with detaching of the
+ * created virtual device.
+ *
+ * This function should typically be invoked by a driver during the probe phase,
+ * in case its device requires power management through multiple PM domains. The
+ * driver may benefit from using the received device, to configure device-links
+ * towards its original device. Depending on the use-case and if needed, the
+ * links may be dynamically changed by the driver, which allows it to control
+ * the power to the PM domains independently from each other.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns the virtual created device when successfully attached to its PM
+ * domain, NULL in case @dev don't need a PM domain, else an ERR_PTR().
+ * Note that, to detach the returned virtual device, the driver shall call
+ * dev_pm_domain_detach() on it, typically during the remove phase.
+ */
+struct device *genpd_dev_pm_attach_by_id(struct device *dev,
+ unsigned int index)
+{
+ struct device *virt_dev;
+ int num_domains;
+ int ret;
+
+ if (!dev->of_node)
+ return NULL;
+
+ /* Verify that the index is within a valid range. */
+ num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
+ "#power-domain-cells");
+ if (index >= num_domains)
+ return NULL;
+
+ /* Allocate and register device on the genpd bus. */
+ virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
+ if (!virt_dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev_set_name(virt_dev, "genpd");
+ virt_dev->bus = &genpd_bus_type;
+ virt_dev->parent = dev;
+ virt_dev->of_node = dev->of_node;
+ virt_dev->id = index;
+
+ ret = device_register(virt_dev);
+ if (ret) {
+ kfree(dev);
+ return ERR_PTR(ret);
+ }
+
+ /* Try to attach the device to the PM domain at the specified index. */
+ ret = __genpd_dev_pm_attach(virt_dev, index, false);
+ if (ret < 1) {
+ device_unregister(virt_dev);
+ return ret ? ERR_PTR(ret) : NULL;
+ }
+
+ return virt_dev;
+}
+EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
+
+/**
+ * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains.
+ * @dev: The device used to lookup the PM domain.
+ * @name: The name of the PM domain.
+ *
+ * Parse device's OF node to find a PM domain specifier using the
+ * power-domain-names DT property. For further description see
+ * genpd_dev_pm_attach_by_id().
+ */
+struct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name)
+{
+ int index;
+
+ if (!dev->of_node)
+ return NULL;
+
+ index = of_property_match_string(dev->of_node, "power-domain-names",
+ name);
+ if (index < 0)
+ return NULL;
+
+ return genpd_dev_pm_attach_by_id(dev, index);
+}
+EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_name);
+
void pm_genpd_print(void)
{
struct generic_pm_domain *genpd;
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
new file mode 100644
index 0000000000..c76908952a
--- /dev/null
+++ b/drivers/base/regmap/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config REGMAP_FORMATTED
+ bool
+
+config REGMAP_I2C
+ bool "I2C regmaps" if COMPILE_TEST
+ depends on I2C
+ select REGMAP_FORMATTED
+
+config REGMAP_SPI
+ bool "SPI regmaps" if COMPILE_TEST
+ depends on SPI
+ select REGMAP_FORMATTED
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index b136a72409..6911e07f0e 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,4 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += regmap.o
+obj-y += regmap-multi.o
obj-y += regmap-mmio.o
-obj-$(CONFIG_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_FORMATTED) += regmap-fmt.o
+obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 05f8f8d622..ac3f0d3c0f 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -7,24 +7,51 @@
struct regmap_bus;
+struct regmap_format {
+ size_t buf_size;
+ size_t reg_bytes;
+ size_t pad_bytes;
+ size_t val_bytes;
+ void (*format_write)(struct regmap *map,
+ unsigned int reg, unsigned int val);
+ void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
+ void (*format_val)(void *buf, unsigned int val, unsigned int shift);
+ unsigned int (*parse_val)(const void *buf);
+};
+
struct regmap {
- struct device_d *dev;
+ struct device *dev;
const struct regmap_bus *bus;
const char *name;
void *bus_context;
struct list_head list;
- int reg_bits;
int reg_stride;
- int pad_bits;
- int val_bits;
- int val_bytes;
+ void *work_buf; /* Scratch buffer used to format I/O */
+ struct regmap_format format;
+ unsigned int read_flag_mask;
+ unsigned int write_flag_mask;
+ int reg_shift;
unsigned int max_register;
struct cdev cdev;
+
+ int (*reg_read)(void *context, unsigned int reg,
+ unsigned int *val);
+ int (*reg_write)(void *context, unsigned int reg,
+ unsigned int val);
};
-enum regmap_endian regmap_get_val_endian(struct device_d *dev,
+enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config);
+#ifdef CONFIG_REGMAP_FORMATTED
+int regmap_formatted_init(struct regmap *map, const struct regmap_config *);
+#else
+static inline int regmap_formatted_init(struct regmap *map, const struct regmap_config *cfg)
+{
+ return -ENOSYS;
+}
+#endif
+
#endif
diff --git a/drivers/base/regmap/regmap-fmt.c b/drivers/base/regmap/regmap-fmt.c
new file mode 100644
index 0000000000..e7f6a8da80
--- /dev/null
+++ b/drivers/base/regmap/regmap-fmt.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Formatted register map access API
+ *
+ * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ *
+ * based on Kernel code:
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
+#include <common.h>
+#include <linux/regmap.h>
+#include <linux/log2.h>
+#include <asm/unaligned.h>
+
+#include "internal.h"
+
+static void regmap_format_12_20_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ out[0] = reg >> 4;
+ out[1] = (reg << 4) | (val >> 16);
+ out[2] = val >> 8;
+ out[3] = val;
+}
+
+
+static void regmap_format_2_6_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ *out = (reg << 6) | val;
+}
+
+static void regmap_format_4_12_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ __be16 *out = map->work_buf;
+ *out = cpu_to_be16((reg << 12) | val);
+}
+
+static void regmap_format_7_9_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ __be16 *out = map->work_buf;
+ *out = cpu_to_be16((reg << 9) | val);
+}
+
+static void regmap_format_7_17_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ out[2] = val;
+ out[1] = val >> 8;
+ out[0] = (val >> 16) | (reg << 1);
+}
+
+static void regmap_format_10_14_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ out[2] = val;
+ out[1] = (val >> 8) | (reg << 6);
+ out[0] = reg >> 2;
+}
+
+static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
+{
+ u8 *b = buf;
+
+ b[0] = val << shift;
+}
+
+static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_be16(val << shift, buf);
+}
+
+static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_le16(val << shift, buf);
+}
+
+static void regmap_format_16_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ u16 v = val << shift;
+
+ memcpy(buf, &v, sizeof(v));
+}
+
+static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
+{
+ u8 *b = buf;
+
+ val <<= shift;
+
+ b[0] = val >> 16;
+ b[1] = val >> 8;
+ b[2] = val;
+}
+
+static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_be32(val << shift, buf);
+}
+
+static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_le32(val << shift, buf);
+}
+
+static void regmap_format_32_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ u32 v = val << shift;
+
+ memcpy(buf, &v, sizeof(v));
+}
+
+#ifdef CONFIG_64BIT
+static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_be64((u64) val << shift, buf);
+}
+
+static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_le64((u64) val << shift, buf);
+}
+
+static void regmap_format_64_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ u64 v = (u64) val << shift;
+
+ memcpy(buf, &v, sizeof(v));
+}
+#endif
+
+static unsigned int regmap_parse_8(const void *buf)
+{
+ const u8 *b = buf;
+
+ return b[0];
+}
+
+static unsigned int regmap_parse_16_be(const void *buf)
+{
+ return get_unaligned_be16(buf);
+}
+
+static unsigned int regmap_parse_16_le(const void *buf)
+{
+ return get_unaligned_le16(buf);
+}
+
+static unsigned int regmap_parse_16_native(const void *buf)
+{
+ u16 v;
+
+ memcpy(&v, buf, sizeof(v));
+ return v;
+}
+
+static unsigned int regmap_parse_24(const void *buf)
+{
+ const u8 *b = buf;
+ unsigned int ret = b[2];
+ ret |= ((unsigned int)b[1]) << 8;
+ ret |= ((unsigned int)b[0]) << 16;
+
+ return ret;
+}
+
+static unsigned int regmap_parse_32_be(const void *buf)
+{
+ return get_unaligned_be32(buf);
+}
+
+static unsigned int regmap_parse_32_le(const void *buf)
+{
+ return get_unaligned_le32(buf);
+}
+
+static unsigned int regmap_parse_32_native(const void *buf)
+{
+ u32 v;
+
+ memcpy(&v, buf, sizeof(v));
+ return v;
+}
+
+#ifdef CONFIG_64BIT
+static unsigned int regmap_parse_64_be(const void *buf)
+{
+ return get_unaligned_be64(buf);
+}
+
+static unsigned int regmap_parse_64_le(const void *buf)
+{
+ return get_unaligned_le64(buf);
+}
+
+static unsigned int regmap_parse_64_native(const void *buf)
+{
+ u64 v;
+
+ memcpy(&v, buf, sizeof(v));
+ return v;
+}
+#endif
+
+
+static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ enum regmap_endian endian;
+
+ /* Retrieve the endianness specification from the regmap config */
+ endian = config->reg_format_endian;
+
+ /* If the regmap config specified a non-default value, use that */
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
+
+ /* Retrieve the endianness specification from the bus config */
+ if (bus && bus->reg_format_endian_default)
+ endian = bus->reg_format_endian_default;
+
+ /* If the bus specified a non-default value, use that */
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
+
+ /* Use this if no other value was found */
+ return REGMAP_ENDIAN_BIG;
+}
+
+static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
+ unsigned long mask)
+{
+ u8 *buf;
+ int i;
+
+ if (!mask || !map->work_buf)
+ return;
+
+ buf = map->work_buf;
+
+ for (i = 0; i < max_bytes; i++)
+ buf[i] |= (mask >> (8 * i)) & 0xff;
+}
+
+static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
+ unsigned int val_len, bool noinc)
+{
+ const struct regmap_bus *bus = map->bus;
+
+ if (!bus->read)
+ return -EINVAL;
+
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->read_flag_mask);
+
+ return bus->read(map->bus_context, map->work_buf,
+ map->format.reg_bytes + map->format.pad_bytes,
+ val, val_len);
+
+}
+
+static int _regmap_bus_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ int ret;
+ struct regmap *map = context;
+ void *work_val = map->work_buf + map->format.reg_bytes +
+ map->format.pad_bytes;
+
+ if (!map->format.parse_val)
+ return -EINVAL;
+
+ ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes, false);
+ if (ret == 0)
+ *val = map->format.parse_val(work_val);
+
+ return ret;
+}
+
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct regmap *map = context;
+
+ map->format.format_write(map, reg, val);
+
+ return map->bus->write(map->bus_context, map->work_buf,
+ map->format.buf_size);
+}
+
+static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len, bool noinc)
+{
+ void *work_val = map->work_buf + map->format.reg_bytes +
+ map->format.pad_bytes;
+
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->write_flag_mask);
+
+ /*
+ * Essentially all I/O mechanisms will be faster with a single
+ * buffer to write. Since register syncs often generate raw
+ * writes of single registers optimise that case.
+ */
+ if (val != work_val && val_len == map->format.val_bytes) {
+ memcpy(work_val, val, map->format.val_bytes);
+ val = work_val;
+ }
+
+ /* If we're doing a single register write we can probably just
+ * send the work_buf directly, otherwise try to do a gather
+ * write.
+ */
+ return map->bus->write(map->bus_context, map->work_buf,
+ map->format.reg_bytes +
+ map->format.pad_bytes +
+ val_len);
+
+}
+
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct regmap *map = context;
+
+ WARN_ON(!map->format.format_val);
+
+ map->format.format_val(map->work_buf + map->format.reg_bytes
+ + map->format.pad_bytes, val, 0);
+ return _regmap_raw_write_impl(map, reg,
+ map->work_buf +
+ map->format.reg_bytes +
+ map->format.pad_bytes,
+ map->format.val_bytes,
+ false);
+}
+
+int regmap_formatted_init(struct regmap *map, const struct regmap_config *config)
+{
+ enum regmap_endian reg_endian, val_endian;
+ const struct regmap_bus *bus = map->bus;
+
+ map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
+ config->val_bits + config->pad_bits, 8);
+
+ map->work_buf = xzalloc(map->format.buf_size);
+
+ if (config->read_flag_mask || config->write_flag_mask) {
+ map->read_flag_mask = config->read_flag_mask;
+ map->write_flag_mask = config->write_flag_mask;
+ } else {
+ map->read_flag_mask = bus->read_flag_mask;
+ }
+
+ map->reg_read = _regmap_bus_read;
+
+ reg_endian = regmap_get_reg_endian(bus, config);
+ val_endian = regmap_get_val_endian(map->dev, bus, config);
+
+ switch (config->reg_bits + config->pad_bits % 8) {
+ case 2:
+ switch (config->val_bits) {
+ case 6:
+ map->format.format_write = regmap_format_2_6_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 4:
+ switch (config->val_bits) {
+ case 12:
+ map->format.format_write = regmap_format_4_12_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 7:
+ switch (config->val_bits) {
+ case 9:
+ map->format.format_write = regmap_format_7_9_write;
+ break;
+ case 17:
+ map->format.format_write = regmap_format_7_17_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 10:
+ switch (config->val_bits) {
+ case 14:
+ map->format.format_write = regmap_format_10_14_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 12:
+ switch (config->val_bits) {
+ case 20:
+ map->format.format_write = regmap_format_12_20_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 8:
+ map->format.format_reg = regmap_format_8;
+ break;
+
+ case 16:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_16_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_reg = regmap_format_16_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_16_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 24:
+ if (reg_endian != REGMAP_ENDIAN_BIG)
+ return -EINVAL;
+ map->format.format_reg = regmap_format_24;
+ break;
+
+ case 32:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_32_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_reg = regmap_format_32_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_32_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+#ifdef CONFIG_64BIT
+ case 64:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_64_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_reg = regmap_format_64_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_64_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+#endif
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (config->val_bits) {
+ case 8:
+ map->format.format_val = regmap_format_8;
+ map->format.parse_val = regmap_parse_8;
+ break;
+ case 16:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_16_be;
+ map->format.parse_val = regmap_parse_16_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_16_le;
+ map->format.parse_val = regmap_parse_16_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_16_native;
+ map->format.parse_val = regmap_parse_16_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 24:
+ if (val_endian != REGMAP_ENDIAN_BIG)
+ return -EINVAL;
+ map->format.format_val = regmap_format_24;
+ map->format.parse_val = regmap_parse_24;
+ break;
+ case 32:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_32_be;
+ map->format.parse_val = regmap_parse_32_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_32_le;
+ map->format.parse_val = regmap_parse_32_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_32_native;
+ map->format.parse_val = regmap_parse_32_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+#ifdef CONFIG_64BIT
+ case 64:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_64_be;
+ map->format.parse_val = regmap_parse_64_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_64_le;
+ map->format.parse_val = regmap_parse_64_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_64_native;
+ map->format.parse_val = regmap_parse_64_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+#endif
+ }
+
+ if (map->format.format_write)
+ map->reg_write = _regmap_bus_formatted_write;
+ else if (map->format.format_val)
+ map->reg_write = _regmap_bus_raw_write;
+ else
+ return -EOPNOTSUPP;
+
+ return 0;
+}
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 756bc224cc..13ba5866c0 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -4,37 +4,57 @@
*/
#include <i2c/i2c.h>
-#include <regmap.h>
+#include <linux/regmap.h>
-static int regmap_i2c_reg_read(void *client, unsigned int reg, unsigned int *val)
+static int regmap_i2c_read(void *context,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
{
- u8 buf[1];
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct i2c_msg xfer[2];
int ret;
- ret = i2c_read_reg(client, reg, buf, 1);
- if (ret != 1)
- return ret;
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reg_size;
+ xfer[0].buf = (void *)reg;
- *val = buf[0];
- return 0;
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = val_size;
+ xfer[1].buf = val;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
}
-static int regmap_i2c_reg_write(void *client, unsigned int reg, unsigned int val)
+static int regmap_i2c_write(void *context, const void *data, size_t count)
{
- u8 buf[] = { val & 0xff };
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
int ret;
- ret = i2c_write_reg(client, reg, buf, 1);
- if (ret != 1)
+ ret = i2c_master_send(i2c, data, count);
+ if (ret == count)
+ return 0;
+ else if (ret < 0)
return ret;
-
- return 0;
+ else
+ return -EIO;
}
static const struct regmap_bus regmap_regmap_i2c_bus = {
- .reg_write = regmap_i2c_reg_write,
- .reg_read = regmap_i2c_reg_read,
+ .write = regmap_i2c_write,
+ .read = regmap_i2c_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
};
struct regmap *regmap_init_i2c(struct i2c_client *client,
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
index 7b1501df93..01b0a99631 100644
--- a/drivers/base/regmap/regmap-mmio.c
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <io.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "internal.h"
@@ -185,9 +185,9 @@ static const struct regmap_bus regmap_mmio = {
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
-static struct regmap_mmio_context *regmap_mmio_gen_context(struct device_d *dev,
- void __iomem *regs,
- const struct regmap_config *config)
+static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
+ void __iomem *regs,
+ const struct regmap_config *config)
{
struct regmap_mmio_context *ctx;
int min_stride;
@@ -277,7 +277,7 @@ err_free:
return ERR_PTR(ret);
}
-struct regmap *regmap_init_mmio_clk(struct device_d *dev,
+struct regmap *regmap_init_mmio_clk(struct device *dev,
const char *clk_id,
void __iomem *regs,
const struct regmap_config *config)
@@ -289,11 +289,15 @@ struct regmap *regmap_init_mmio_clk(struct device_d *dev,
return ERR_CAST(ctx);
if (clk_id) {
- ctx->clk = clk_get(dev, clk_id);
- if (IS_ERR(ctx->clk)) {
+ struct clk *clk;
+
+ clk = clk_get(dev, clk_id);
+ if (IS_ERR(clk)) {
kfree(ctx);
- return ERR_CAST(ctx->clk);
+ return ERR_CAST(clk);
}
+
+ ctx->clk = clk;
}
return regmap_init(dev, &regmap_mmio, ctx, config);
diff --git a/drivers/base/regmap/regmap-multi.c b/drivers/base/regmap/regmap-multi.c
new file mode 100644
index 0000000000..74f3648eb4
--- /dev/null
+++ b/drivers/base/regmap/regmap-multi.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <fcntl.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+#include <linux/export.h>
+
+#include "internal.h"
+
+enum { MULTI_MAP_8, MULTI_MAP_16, MULTI_MAP_32, MULTI_MAP_64, MULTI_MAP_COUNT };
+struct regmap_multi {
+ struct cdev cdev;
+ struct regmap *map[MULTI_MAP_COUNT];
+};
+
+static struct regmap *regmap_multi_cdev_get_map(struct cdev *cdev, unsigned rwsize)
+{
+ struct regmap_multi *multi = container_of(cdev, struct regmap_multi, cdev);
+
+ switch (rwsize) {
+ case 8:
+ return multi->map[MULTI_MAP_64];
+ case 4:
+ return multi->map[MULTI_MAP_32];
+ case 2:
+ return multi->map[MULTI_MAP_16];
+ case 1:
+ return multi->map[MULTI_MAP_8];
+ }
+
+ return NULL;
+}
+
+static ssize_t regmap_multi_cdev_read(struct cdev *cdev, void *buf, size_t count,
+ loff_t offset, unsigned long flags)
+{
+ unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags);
+ struct regmap *map;
+
+ map = regmap_multi_cdev_get_map(cdev, rwsize);
+ if (!map)
+ return -EINVAL;
+
+ count = ALIGN_DOWN(count, rwsize);
+ return regmap_bulk_read(map, offset, buf, count / rwsize) ?: count;
+}
+
+static ssize_t regmap_multi_cdev_write(struct cdev *cdev, const void *buf, size_t count,
+ loff_t offset, unsigned long flags)
+{
+ unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags);
+ struct regmap *map;
+
+ map = regmap_multi_cdev_get_map(cdev, rwsize);
+ if (!map)
+ return -EINVAL;
+
+ count = ALIGN_DOWN(count, rwsize);
+ return regmap_bulk_write(map, offset, buf, count / rwsize) ?: count;
+}
+
+static struct cdev_operations regmap_multi_fops = {
+ .read = regmap_multi_cdev_read,
+ .write = regmap_multi_cdev_write,
+};
+
+int regmap_multi_register_cdev(struct regmap *map8,
+ struct regmap *map16,
+ struct regmap *map32,
+ struct regmap *map64)
+{
+ struct regmap *maps[MULTI_MAP_COUNT] = { map8, map16, map32, map64 };
+ struct regmap_multi *multi;
+ struct cdev *cdev;
+ int i;
+
+ multi = xzalloc(sizeof(*multi));
+ cdev = &multi->cdev;
+
+ cdev->ops = &regmap_multi_fops;
+ cdev->size = LLONG_MAX;
+
+ for (i = 0; i < MULTI_MAP_COUNT; i++) {
+ if (!maps[i])
+ continue;
+
+ multi->map[i] = maps[i];
+ cdev->size = min_t(loff_t, regmap_size_bytes(maps[i]), cdev->size);
+ cdev->dev = cdev->dev ?: maps[i]->dev;
+ }
+
+ if (!cdev->dev) {
+ free(multi);
+ return -EINVAL;
+ }
+
+ cdev->name = xstrdup(dev_name(cdev->dev));
+
+ return devfs_create(cdev);
+}
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
new file mode 100644
index 0000000000..d15d59f635
--- /dev/null
+++ b/drivers/base/regmap/regmap-spi.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - SPI support
+//
+// Copyright 2011 Wolfson Microelectronics plc
+//
+// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+
+#include <linux/regmap.h>
+#include <spi/spi.h>
+
+static int regmap_spi_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_write(spi, data, count);
+}
+
+static int regmap_spi_read(void *context,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
+}
+
+static const struct regmap_bus regmap_spi = {
+ .write = regmap_spi_write,
+ .read = regmap_spi_read,
+ .read_flag_mask = 0x80,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+struct regmap *regmap_init_spi(struct spi_device *spi,
+ const struct regmap_config *config)
+{
+ return regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
+}
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 35c462f962..7ad527954c 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -12,7 +12,7 @@
*/
#include <common.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <malloc.h>
#include <linux/log2.h>
@@ -20,7 +20,7 @@
static LIST_HEAD(regmaps);
-enum regmap_endian regmap_get_val_endian(struct device_d *dev,
+enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config)
{
@@ -35,8 +35,8 @@ enum regmap_endian regmap_get_val_endian(struct device_d *dev,
return endian;
/* If the dev and dev->device_node exist try to get endianness from DT */
- if (dev && dev->device_node) {
- np = dev->device_node;
+ if (dev && dev->of_node) {
+ np = dev->of_node;
/* Parse the device's DT node for an endianness specification */
if (of_property_read_bool(np, "big-endian"))
@@ -64,6 +64,23 @@ enum regmap_endian regmap_get_val_endian(struct device_d *dev,
}
EXPORT_SYMBOL_GPL(regmap_get_val_endian);
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct regmap *map = context;
+
+ return map->bus->reg_read(map->bus_context, reg, val);
+}
+
+
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct regmap *map = context;
+
+ return map->bus->reg_write(map->bus_context, reg, val);
+}
+
/*
* regmap_init - initialize and register a regmap
*
@@ -74,27 +91,39 @@ EXPORT_SYMBOL_GPL(regmap_get_val_endian);
*
* Returns a pointer to the new map or a ERR_PTR value on failure
*/
-struct regmap *regmap_init(struct device_d *dev,
+struct regmap *regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
const struct regmap_config *config)
{
struct regmap *map;
+ int ret;
map = xzalloc(sizeof(*map));
map->dev = dev;
map->bus = bus;
map->name = config->name;
map->bus_context = bus_context;
- map->reg_bits = config->reg_bits;
+ map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->reg_stride = config->reg_stride;
if (!map->reg_stride)
map->reg_stride = 1;
- map->pad_bits = config->pad_bits;
- map->val_bits = config->val_bits;
- map->val_bytes = DIV_ROUND_UP(config->val_bits, 8);
+ map->format.pad_bytes = config->pad_bits / 8;
+ map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
+ map->reg_shift = config->pad_bits % 8;
map->max_register = config->max_register;
+ if (!bus->read || !bus->write) {
+ map->reg_read = _regmap_bus_reg_read;
+ map->reg_write = _regmap_bus_reg_write;
+ } else {
+ ret = regmap_formatted_init(map, config);
+ if (ret) {
+ free(map);
+ return ERR_PTR(ret);
+ }
+ }
+
list_add_tail(&map->list, &regmaps);
return map;
@@ -108,7 +137,7 @@ struct regmap *regmap_init(struct device_d *dev,
*
* Returns a pointer to the regmap or a ERR_PTR value on failure
*/
-struct regmap *dev_get_regmap(struct device_d *dev, const char *name)
+struct regmap *dev_get_regmap(struct device *dev, const char *name)
{
struct regmap *map;
@@ -124,7 +153,7 @@ struct regmap *dev_get_regmap(struct device_d *dev, const char *name)
return ERR_PTR(-ENOENT);
}
-struct device_d *regmap_get_device(struct regmap *map)
+struct device *regmap_get_device(struct regmap *map)
{
return map->dev;
}
@@ -140,7 +169,7 @@ struct device_d *regmap_get_device(struct regmap *map)
*/
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
- return map->bus->reg_write(map->bus_context, reg, val);
+ return map->reg_write(map, reg, val);
}
/*
@@ -154,7 +183,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
*/
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
- return map->bus->reg_read(map->bus_context, reg, val);
+ return map->reg_read(map, reg, val);
}
/**
@@ -219,27 +248,27 @@ int regmap_write_bits(struct regmap *map, unsigned int reg,
* @map: Register map to read from
* @reg: First register to be read from
* @val: Pointer to store read value
- * @val_len: Size of data to read
+ * @val_count: Number of registers to read
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
- size_t val_len)
+ size_t val_count)
{
- size_t val_bytes = map->val_bytes;
- size_t val_count = val_len / val_bytes;
unsigned int v;
int ret, i;
- if (val_len % val_bytes)
- return -EINVAL;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (val_count == 0)
return -EINVAL;
for (i = 0; i < val_count; i++) {
+
+#ifdef CONFIG_64BIT
+ u64 *u64 = val;
+#endif
u32 *u32 = val;
u16 *u16 = val;
u8 *u8 = val;
@@ -248,7 +277,12 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
if (ret != 0)
goto out;
- switch (map->val_bytes) {
+ switch (map->format.val_bytes) {
+#ifdef CONFIG_64BIT
+ case 8:
+ u64[i] = v;
+ break;
+#endif
case 4:
u32[i] = v;
break;
@@ -274,20 +308,17 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
* @reg: Initial register to write to
* @val: Block of data to be written, laid out for direct transmission to the
* device
- * @val_len: Length of data pointed to by val.
+ * @val_len: Number of registers to write
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_bulk_write(struct regmap *map, unsigned int reg,
- const void *val, size_t val_len)
+ const void *val, size_t val_count)
{
- size_t val_bytes = map->val_bytes;
- size_t val_count = val_len / val_bytes;
+ size_t val_bytes = map->format.val_bytes;
int ret, i;
- if (val_len % val_bytes)
- return -EINVAL;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (val_count == 0)
@@ -306,6 +337,11 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg,
case 4:
ival = *(u32 *)(val + (i * val_bytes));
break;
+#ifdef CONFIG_64BIT
+ case 8:
+ ival = *(u64 *)(val + (i * val_bytes));
+ break;
+#endif
default:
ret = -EINVAL;
goto out;
@@ -323,7 +359,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg,
int regmap_get_val_bytes(struct regmap *map)
{
- return map->val_bytes;
+ return map->format.val_bytes;
}
int regmap_get_max_register(struct regmap *map)
@@ -338,22 +374,18 @@ int regmap_get_reg_stride(struct regmap *map)
static int regmap_round_val_bytes(struct regmap *map)
{
- int val_bytes;
-
- val_bytes = roundup_pow_of_two(map->val_bits) >> 3;
- if (!val_bytes)
- val_bytes = 1;
-
- return val_bytes;
+ return map->format.val_bytes ?: 1;
}
static ssize_t regmap_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset,
unsigned long flags)
{
struct regmap *map = container_of(cdev, struct regmap, cdev);
+ size_t val_bytes = map->format.val_bytes;
int ret;
- ret = regmap_bulk_read(map, offset, buf, count);
+ count = ALIGN_DOWN(count, val_bytes);
+ ret = regmap_bulk_read(map, offset, buf, count / val_bytes);
if (ret)
return ret;
@@ -364,9 +396,11 @@ static ssize_t regmap_cdev_write(struct cdev *cdev, const void *buf, size_t coun
unsigned long flags)
{
struct regmap *map = container_of(cdev, struct regmap, cdev);
+ size_t val_bytes = map->format.val_bytes;
int ret;
- ret = regmap_bulk_write(map, offset, buf, count);
+ count = ALIGN_DOWN(count, val_bytes);
+ ret = regmap_bulk_write(map, offset, buf, count / val_bytes);
if (ret)
return ret;
@@ -379,6 +413,36 @@ static struct cdev_operations regmap_fops = {
};
/*
+ * regmap_count_registers - returns the total number of registers
+ *
+ * @map: The map
+ *
+ * Returns the total number of registers in a regmap
+ */
+static size_t regmap_count_registers(struct regmap *map)
+{
+ /*
+ * max_register is in units of reg_stride, so we need to divide
+ * by the register stride before adding one to arrive at the
+ * total number of registers.
+ */
+ return (map->max_register / map->reg_stride) + 1;
+}
+
+/*
+ * regmap_size_bytes - computes the size of the regmap in bytes
+ *
+ * @map: The map
+ *
+ * Returns the number of bytes needed to hold all values in the
+ * regmap.
+ */
+size_t regmap_size_bytes(struct regmap *map)
+{
+ return regmap_round_val_bytes(map) * regmap_count_registers(map);
+}
+
+/*
* regmap_register_cdev - register a devfs file for a regmap
*
* @map: The map
@@ -405,8 +469,7 @@ int regmap_register_cdev(struct regmap *map, const char *name)
map->cdev.name = xstrdup(dev_name(map->dev));
}
- map->cdev.size = regmap_round_val_bytes(map) * (map->max_register + 1) /
- map->reg_stride;
+ map->cdev.size = regmap_size_bytes(map);
map->cdev.dev = map->dev;
map->cdev.ops = &regmap_fops;
diff --git a/drivers/base/resource.c b/drivers/base/resource.c
index 0134456ffa..0d6f200a9d 100644
--- a/drivers/base/resource.c
+++ b/drivers/base/resource.c
@@ -9,9 +9,9 @@
#include <xfuncs.h>
#include <malloc.h>
-struct device_d *device_alloc(const char *devname, int id)
+struct device *device_alloc(const char *devname, int id)
{
- struct device_d *dev;
+ struct device *dev;
dev = xzalloc(sizeof(*dev));
dev_set_name(dev, devname);
@@ -20,7 +20,7 @@ struct device_d *device_alloc(const char *devname, int id)
return dev;
}
-int device_add_data(struct device_d *dev, const void *data, size_t size)
+int device_add_data(struct device *dev, const void *data, size_t size)
{
free(dev->platform_data);
@@ -32,7 +32,8 @@ int device_add_data(struct device_d *dev, const void *data, size_t size)
return 0;
}
-int device_add_resources(struct device_d *dev, const struct resource *res, int num)
+int device_add_resources(struct device *dev, const struct resource *res,
+ int num)
{
dev->resource = xmemdup(res, sizeof(*res) * num);
dev->num_resources = num;
@@ -40,8 +41,9 @@ int device_add_resources(struct device_d *dev, const struct resource *res, int n
return 0;
}
-int device_add_resource(struct device_d *dev, const char *resname,
- resource_size_t start, resource_size_t size, unsigned int flags)
+int device_add_resource(struct device *dev, const char *resname,
+ resource_size_t start, resource_size_t size,
+ unsigned int flags)
{
struct resource res = {
.start = start,
@@ -55,13 +57,15 @@ int device_add_resource(struct device_d *dev, const char *resname,
return device_add_resources(dev, &res, 1);
}
-struct device_d *add_generic_device(const char* devname, int id, const char *resname,
+struct device *add_child_device(struct device *parent,
+ const char* devname, int id, const char *resname,
resource_size_t start, resource_size_t size, unsigned int flags,
void *pdata)
{
- struct device_d *dev;
+ struct device *dev;
dev = device_alloc(devname, id);
+ dev->parent = parent;
dev->platform_data = pdata;
device_add_resource(dev, resname, start, size, flags);
@@ -69,12 +73,12 @@ struct device_d *add_generic_device(const char* devname, int id, const char *res
return dev;
}
-EXPORT_SYMBOL(add_generic_device);
+EXPORT_SYMBOL(add_child_device);
-struct device_d *add_generic_device_res(const char* devname, int id,
+struct device *add_generic_device_res(const char* devname, int id,
struct resource *res, int nb, void *pdata)
{
- struct device_d *dev;
+ struct device *dev;
dev = device_alloc(devname, id);
dev->platform_data = pdata;
@@ -87,7 +91,7 @@ struct device_d *add_generic_device_res(const char* devname, int id,
EXPORT_SYMBOL(add_generic_device_res);
#ifdef CONFIG_DRIVER_NET_DM9K
-struct device_d *add_dm9000_device(int id, resource_size_t base,
+struct device *add_dm9000_device(int id, resource_size_t base,
resource_size_t data, int flags, void *pdata)
{
struct resource *res;
@@ -123,7 +127,7 @@ EXPORT_SYMBOL(add_dm9000_device);
#endif
#ifdef CONFIG_USB_EHCI
-struct device_d *add_usb_ehci_device(int id, resource_size_t hccr,
+struct device *add_usb_ehci_device(int id, resource_size_t hccr,
resource_size_t hcor, void *pdata)
{
struct resource *res;
@@ -142,7 +146,7 @@ EXPORT_SYMBOL(add_usb_ehci_device);
#endif
#ifdef CONFIG_DRIVER_NET_KS8851_MLL
-struct device_d *add_ks8851_device(int id, resource_size_t addr,
+struct device *add_ks8851_device(int id, resource_size_t addr,
resource_size_t addr_cmd, int flags, void *pdata)
{
struct resource *res;
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
new file mode 100644
index 0000000000..a481f8987b
--- /dev/null
+++ b/drivers/base/soc.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2024 Marco Felsch, Pengutronix
+/*
+ * Based on Linux drivers/base/soc.c:
+ * Copyright (C) ST-Ericsson SA 2011
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of.h>
+
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/err.h>
+
+struct soc_device {
+ struct device dev;
+ struct soc_device_attribute *attr;
+};
+
+static struct bus_type soc_bus_type = {
+ .name = "soc",
+};
+static bool soc_bus_registered;
+
+static void soc_device_add_params(struct soc_device *soc_dev)
+{
+ struct soc_device_attribute *attr = soc_dev->attr;
+ struct device *dev = &soc_dev->dev;
+
+ if (attr->machine)
+ dev_add_param_string_fixed(dev, "machine", attr->machine);
+ if (attr->family)
+ dev_add_param_string_fixed(dev, "family", attr->family);
+ if (attr->revision)
+ dev_add_param_string_fixed(dev, "revision", attr->revision);
+ if (attr->serial_number)
+ dev_add_param_string_fixed(dev, "serial_number", attr->serial_number);
+ if (attr->soc_id)
+ dev_add_param_string_fixed(dev, "soc_id", attr->soc_id);
+}
+
+static void soc_device_get_machine(struct soc_device_attribute *soc_dev_attr)
+{
+ struct device_node *np;
+
+ if (soc_dev_attr->machine)
+ return;
+
+ np = of_find_node_by_path("/");
+ of_property_read_string(np, "model", &soc_dev_attr->machine);
+ of_node_put(np);
+}
+
+static struct soc_device_attribute *early_soc_dev_attr;
+
+struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
+{
+ struct soc_device *soc_dev;
+ int ret;
+
+ soc_device_get_machine(soc_dev_attr);
+
+ if (!soc_bus_registered) {
+ if (early_soc_dev_attr)
+ return ERR_PTR(-EBUSY);
+ early_soc_dev_attr = soc_dev_attr;
+ return NULL;
+ }
+
+ soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
+ if (!soc_dev) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ soc_dev->attr = soc_dev_attr;
+ soc_dev->dev.bus = &soc_bus_type;
+ soc_dev->dev.id = DEVICE_ID_DYNAMIC;
+
+ dev_set_name(&soc_dev->dev, "soc");
+
+ ret = device_register(&soc_dev->dev);
+ if (ret) {
+ put_device(&soc_dev->dev);
+ goto out2;
+ }
+
+ soc_device_add_params(soc_dev);
+
+ return soc_dev;
+
+out2:
+ kfree(soc_dev);
+out1:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(soc_device_register);
+
+/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */
+void soc_device_unregister(struct soc_device *soc_dev)
+{
+ device_unregister(&soc_dev->dev);
+ kfree(soc_dev);
+ early_soc_dev_attr = NULL;
+}
+EXPORT_SYMBOL_GPL(soc_device_unregister);
+
+static int __init soc_bus_register(void)
+{
+ int ret;
+
+ ret = bus_register(&soc_bus_type);
+ if (ret)
+ return ret;
+ soc_bus_registered = true;
+
+ if (early_soc_dev_attr)
+ return PTR_ERR(soc_device_register(early_soc_dev_attr));
+
+ return 0;
+}
+core_initcall(soc_bus_register);
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 625e81a4ec..bf8dfdbd5c 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -9,7 +9,7 @@ config VIRTIO_BLK
config EFI_BLK
bool "EFI block I/O driver"
default y
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
config EFI_BLK_SEPARATE_USBDISK
bool "rename USB devices to /dev/usbdiskX"
diff --git a/drivers/block/efi-block-io.c b/drivers/block/efi-block-io.c
index b78117d445..876b46c2d7 100644
--- a/drivers/block/efi-block-io.c
+++ b/drivers/block/efi-block-io.c
@@ -12,7 +12,6 @@
#include <fcntl.h>
#include <efi.h>
#include <block.h>
-#include <disks.h>
#include <efi/efi-payload.h>
#include <efi/efi-device.h>
#include <bootsource.h>
@@ -20,39 +19,12 @@
#define EFI_BLOCK_IO_PROTOCOL_REVISION2 0x00020001
#define EFI_BLOCK_IO_PROTOCOL_REVISION3 ((2<<16) | (31))
-struct efi_block_io_media{
- u32 media_id;
- bool removable_media;
- bool media_present;
- bool logical_partition;
- bool read_only;
- bool write_caching;
- u32 block_size;
- u32 io_align;
- sector_t last_block;
- u64 lowest_aligned_lba; /* added in Revision 2 */
- u32 logical_blocks_per_physical_block; /* added in Revision 2 */
- u32 optimal_transfer_length_granularity; /* added in Revision 3 */
-};
-
-struct efi_block_io_protocol {
- u64 revision;
- struct efi_block_io_media *media;
- efi_status_t(EFIAPI *reset)(struct efi_block_io_protocol *this,
- bool ExtendedVerification);
- efi_status_t(EFIAPI *read)(struct efi_block_io_protocol *this, u32 media_id,
- u64 lba, unsigned long buffer_size, void *buf);
- efi_status_t(EFIAPI *write)(struct efi_block_io_protocol *this, u32 media_id,
- u64 lba, unsigned long buffer_size, void *buf);
- efi_status_t(EFIAPI *flush)(struct efi_block_io_protocol *this);
-};
-
struct efi_bio_priv {
struct efi_block_io_protocol *protocol;
- struct device_d *dev;
+ struct device *dev;
struct block_device blk;
u32 media_id;
- void (*efi_info)(struct device_d *);
+ void (*efi_info)(struct device *);
};
static int efi_bio_read(struct block_device *blk, void *buffer, sector_t block,
@@ -102,7 +74,7 @@ static struct block_device_ops efi_bio_ops = {
.flush = efi_bio_flush,
};
-static void efi_bio_print_info(struct device_d *dev)
+static void efi_bio_print_info(struct device *dev)
{
struct efi_bio_priv *priv = dev->priv;
struct efi_block_io_media *media = priv->protocol->media;
@@ -121,7 +93,7 @@ static void efi_bio_print_info(struct device_d *dev)
printf(" last_block: 0x%016llx\n", media->last_block);
if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION2)
- return;
+ goto out;
printf(" lowest_aligned_lba: 0x%08llx\n",
media->lowest_aligned_lba);
@@ -129,11 +101,12 @@ static void efi_bio_print_info(struct device_d *dev)
media->logical_blocks_per_physical_block);
if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION3)
- return;
+ goto out;
printf(" optimal_transfer_length_granularity: 0x%08x\n",
media->optimal_transfer_length_granularity);
+out:
if (priv->efi_info)
priv->efi_info(dev);
}
@@ -146,11 +119,10 @@ static bool is_bio_usbdev(struct efi_device *efidev)
static int efi_bio_probe(struct efi_device *efidev)
{
- int ret;
int instance;
struct efi_bio_priv *priv;
struct efi_block_io_media *media;
- struct device_d *dev = &efidev->dev;
+ struct device *dev = &efidev->dev;
priv = xzalloc(sizeof(*priv));
@@ -180,19 +152,14 @@ static int efi_bio_probe(struct efi_device *efidev)
priv->blk.num_blocks = media->last_block + 1;
priv->blk.ops = &efi_bio_ops;
priv->blk.dev = &efidev->dev;
+ priv->blk.type = BLK_TYPE_VIRTUAL;
priv->media_id = media->media_id;
- ret = blockdevice_register(&priv->blk);
- if (ret)
- return ret;
-
if (efi_get_bootsource() == efidev)
bootsource_set_raw_instance(instance);
- parse_partition_table(&priv->blk);
-
- return 0;
+ return blockdevice_register(&priv->blk);
}
static struct efi_driver efi_bio_driver = {
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 660f3a7b6b..cbef500d59 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -99,19 +99,15 @@ static int virtio_blk_probe(struct virtio_device *vdev)
devnum = cdev_find_free_index("virtioblk");
priv->blk.cdev.name = xasprintf("virtioblk%d", devnum);
+ cdev_set_of_node(&priv->blk.cdev, vdev->dev.device_node);
priv->blk.dev = &vdev->dev;
priv->blk.blockbits = SECTOR_SHIFT;
virtio_cread(vdev, struct virtio_blk_config, capacity, &cap);
priv->blk.num_blocks = cap;
priv->blk.ops = &virtio_blk_ops;
+ priv->blk.type = BLK_TYPE_VIRTUAL;
- ret = blockdevice_register(&priv->blk);
- if (ret)
- return ret;
-
- parse_partition_table(&priv->blk);
-
- return 0;
+ return blockdevice_register(&priv->blk);
}
static void virtio_blk_remove(struct virtio_device *vdev)
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index e622ed239c..b480cf8bff 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -29,7 +29,7 @@ config MVEBU_MBUS
config ACPI
bool "Advanced Configuration and Power Interface (ACPI)"
default y
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
help
Driver needed for supporting drivers probed from ACPI tables.
The root SDT is found via UEFI.
diff --git a/drivers/bus/acpi.c b/drivers/bus/acpi.c
index 63d3f618b4..5936170733 100644
--- a/drivers/bus/acpi.c
+++ b/drivers/bus/acpi.c
@@ -84,7 +84,7 @@ static struct sig_desc {
{ /* sentinel */ }
};
-static struct acpi_sdt *acpi_get_dev_sdt(struct device_d *dev)
+static struct acpi_sdt *acpi_get_dev_sdt(struct device *dev)
{
int i;
@@ -96,7 +96,7 @@ static struct acpi_sdt *acpi_get_dev_sdt(struct device_d *dev)
return NULL;
}
-static void acpi_devinfo(struct device_d *dev)
+static void acpi_devinfo(struct device *dev)
{
struct acpi_sdt *sdt = acpi_get_dev_sdt(dev);
struct sig_desc *sig_desc;
@@ -120,26 +120,20 @@ static void acpi_devinfo(struct device_d *dev)
printf("CreatorRevision: %u\n", sdt->creator_revision);
}
-static int acpi_register_device(struct device_d *dev, struct acpi_sdt *sdt)
+static int acpi_register_device(struct device *dev, struct acpi_sdt *sdt)
{
- int ret;
-
- ret = register_device(dev);
- if (ret)
- return ret;
-
device_add_resource(dev, "SDT", (resource_size_t)sdt, sdt->len,
IORESOURCE_MEM | IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY);
- dev_dbg(dev, "registered as ACPI device\n");
+ dev_dbg(dev, "registering as ACPI device\n");
- return 0;
+ return register_device(dev);
}
-static struct device_d *acpi_add_device(struct bus_type *bus,
+static struct device *acpi_add_device(struct bus_type *bus,
acpi_sig_t signature)
{
- struct device_d *dev;
+ struct device *dev;
dev = xzalloc(sizeof(*dev));
@@ -155,7 +149,7 @@ static struct device_d *acpi_add_device(struct bus_type *bus,
static int acpi_register_devices(struct bus_type *bus)
{
- efi_config_table_t *table = bus->dev->priv;
+ struct efi_config_table *table = bus->dev->priv;
struct acpi_rsdp *rsdp;
struct acpi_rsdt *root;
size_t entry_count;
@@ -192,7 +186,7 @@ static int acpi_register_devices(struct bus_type *bus)
return -EIO;
}
- dev_info(bus->dev, "Found %s (OEM: %.8s) with %lu entries\n",
+ dev_info(bus->dev, "Found %s (OEM: %.8s) with %zu entries\n",
sig, root->sdt.oem_id, entry_count);
for (i = 0; i < entry_count; i++) {
@@ -203,7 +197,7 @@ static int acpi_register_devices(struct bus_type *bus)
return 0;
}
-static int acpi_bus_match(struct device_d *dev, struct driver_d *drv)
+static int acpi_bus_match(struct device *dev, struct driver *drv)
{
struct acpi_driver *acpidrv = to_acpi_driver(drv);
struct acpi_sdt *sdt = acpi_get_dev_sdt(dev);
@@ -211,31 +205,16 @@ static int acpi_bus_match(struct device_d *dev, struct driver_d *drv)
return acpi_sigcmp(acpidrv->signature, sdt->signature);
}
-static int acpi_bus_probe(struct device_d *dev)
-{
- return dev->driver->probe(dev);
-}
-
-static void acpi_bus_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
struct bus_type acpi_bus = {
.name = "acpi",
.match = acpi_bus_match,
- .probe = acpi_bus_probe,
- .remove = acpi_bus_remove,
};
static int efi_acpi_probe(void)
{
- efi_config_table_t *table = NULL;
- int i;
+ struct efi_config_table *ect, *table = NULL;
- for (i = 0; i < efi_sys_table->nr_tables; i++) {
- efi_config_table_t *ect = &efi_sys_table->tables[i];
+ for_each_efi_config_table(ect) {
/* take ACPI < 2 table only if no ACPI 2.0 is available */
if (!efi_guidcmp(ect->guid, EFI_ACPI_20_TABLE_GUID)) {
acpi_bus.name = "acpi2";
@@ -254,4 +233,4 @@ static int efi_acpi_probe(void)
acpi_bus.dev->priv = table;
return acpi_register_devices(&acpi_bus);
}
-postcore_initcall(efi_acpi_probe);
+postcore_efi_initcall(efi_acpi_probe);
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 01a35cfc9f..4907577164 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -64,9 +64,10 @@ static struct of_device_id weim_id_table[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, weim_id_table);
struct imx_weim {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct imx_weim_devtype *devtype;
};
@@ -106,26 +107,25 @@ static int weim_parse_dt(struct imx_weim *weim)
struct device_node *child;
int ret;
- for_each_child_of_node(weim->dev->device_node, child) {
+ for_each_child_of_node(weim->dev->of_node, child) {
if (!child->name)
continue;
ret = weim_timing_setup(weim, child);
if (ret) {
- dev_err(weim->dev, "%s set timing failed.\n",
- child->full_name);
+ dev_err(weim->dev, "%pOF set timing failed.\n", child);
return ret;
}
}
- ret = of_platform_populate(weim->dev->device_node, NULL, weim->dev);
+ ret = of_platform_populate(weim->dev->of_node, NULL, weim->dev);
if (ret)
- dev_err(weim->dev, "%s fail to create devices.\n",
- weim->dev->device_node->full_name);
+ dev_err(weim->dev, "%pOF failed to create devices.\n",
+ weim->dev->of_node);
return ret;
}
-static int weim_probe(struct device_d *dev)
+static int weim_probe(struct device *dev)
{
struct resource *iores;
struct imx_weim_devtype *devtype;
@@ -163,7 +163,7 @@ weim_err:
return ret;
}
-static struct driver_d weim_driver = {
+static struct driver weim_driver = {
.name = "imx-weim",
.of_compatible = DRV_OF_COMPAT(weim_id_table),
.probe = weim_probe,
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 46fe5dd398..3e3d3ae2b6 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -60,7 +60,7 @@
#include <of.h>
#include <of_address.h>
#include <linux/mbus.h>
-#include <mach/common.h>
+#include <mach/mvebu/common.h>
/* DDR target is the same on all platforms */
#define TARGET_DDR 0
@@ -483,6 +483,7 @@ static struct of_device_id mvebu_mbus_dt_ids[] = {
#endif
{ },
};
+MODULE_DEVICE_TABLE(of, mvebu_mbus_dt_ids);
/*
* Public API of the driver
diff --git a/drivers/bus/omap-gpmc.c b/drivers/bus/omap-gpmc.c
index 5f65d59911..f720933a0a 100644
--- a/drivers/bus/omap-gpmc.c
+++ b/drivers/bus/omap-gpmc.c
@@ -16,8 +16,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mtd/rawnand.h>
-#include <mach/gpmc_nand.h>
-#include <mach/gpmc.h>
+#include <mach/omap/gpmc_nand.h>
+#include <mach/omap/gpmc.h>
#define GPMC_CS_NUM 8
#define GPMC_NR_WAITPINS 4
@@ -141,7 +141,7 @@ struct gpmc_settings {
};
struct imx_gpmc {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct imx_gpmc_devtype *devtype;
};
@@ -452,7 +452,7 @@ static struct dt_eccmode modes[] = {
},
};
-static int gpmc_probe_nand_child(struct device_d *dev,
+static int gpmc_probe_nand_child(struct device *dev,
struct device_node *child)
{
u32 val;
@@ -464,8 +464,7 @@ static int gpmc_probe_nand_child(struct device_d *dev,
int ret, i;
if (of_property_read_u32(child, "reg", &val) < 0) {
- dev_err(dev, "%s has no 'reg' property\n",
- child->full_name);
+ dev_err(dev, "%pOF has no 'reg' property\n", child);
return -ENODEV;
}
@@ -523,7 +522,7 @@ static int gpmc_probe_nand_child(struct device_d *dev,
dev = device_alloc("gpmc_nand", DEVICE_ID_DYNAMIC);
device_add_resource(dev, NULL, (resource_size_t)gpmc_base, SZ_4K, IORESOURCE_MEM);
device_add_data(dev, &gpmc_nand_data, sizeof(gpmc_nand_data));
- dev->device_node = child;
+ dev->of_node = child;
platform_device_register(dev);
return 0;
@@ -537,8 +536,8 @@ static int gpmc_probe_nand_child(struct device_d *dev,
* Allocates and configures a GPMC chip-select for a child device.
* Returns 0 on success and appropriate negative error code on failure.
*/
-static int gpmc_probe_generic_child(struct device_d *dev,
- struct device_node *child)
+static int gpmc_probe_generic_child(struct device *dev,
+ struct device_node *child)
{
struct gpmc_settings gpmc_s = {};
struct gpmc_timings gpmc_t = {};
@@ -548,14 +547,12 @@ static int gpmc_probe_generic_child(struct device_d *dev,
resource_size_t size;
if (of_property_read_u32(child, "reg", &cs) < 0) {
- dev_err(dev, "%s has no 'reg' property\n",
- child->full_name);
+ dev_err(dev, "%pOF has no 'reg' property\n", child);
return -ENODEV;
}
if (of_address_to_resource(child, 0, &res) < 0) {
- dev_err(dev, "%s has malformed 'reg' property\n",
- child->full_name);
+ dev_err(dev, "%pOF has malformed 'reg' property\n", child);
return -ENODEV;
}
@@ -600,9 +597,9 @@ err:
return ret;
}
-static int gpmc_probe(struct device_d *dev)
+static int gpmc_probe(struct device *dev)
{
- struct device_node *child, *node = dev->device_node;
+ struct device_node *child, *node = dev->of_node;
int ret;
gpmc_generic_init(0x12);
@@ -652,8 +649,9 @@ static struct of_device_id gpmc_id_table[] = {
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
{ }
};
+MODULE_DEVICE_TABLE(of, gpmc_id_table);
-static struct driver_d gpmc_driver = {
+static struct driver gpmc_driver = {
.name = "omap-gpmc",
.of_compatible = DRV_OF_COMPAT(gpmc_id_table),
.probe = gpmc_probe,
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index ff9d2f81cd..967c9d1f57 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -8,15 +8,15 @@
#include <of.h>
#include <linux/err.h>
-static int ti_sysc_probe(struct device_d *dev)
+static int ti_sysc_probe(struct device *dev)
{
int ret;
- ret = of_platform_populate(dev->device_node,
+ ret = of_platform_populate(dev->of_node,
of_default_bus_match_table, dev);
if (ret)
- dev_err(dev, "%s fail to create devices.\n",
- dev->device_node->full_name);
+ dev_err(dev, "%pOF failed to create devices.\n",
+ dev->of_node);
return ret;
};
@@ -27,8 +27,9 @@ static struct of_device_id ti_sysc_dt_ids[] = {
{ .compatible = "ti,sysc-omap2",},
{ },
};
+MODULE_DEVICE_TABLE(of, ti_sysc_dt_ids);
-static struct driver_d ti_sysc_driver = {
+static struct driver ti_sysc_driver = {
.name = "ti-sysc",
.probe = ti_sysc_probe,
.of_compatible = DRV_OF_COMPAT(ti_sysc_dt_ids),
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 0faea6488e..d2a61329e1 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -1,13 +1,33 @@
# SPDX-License-Identifier: GPL-2.0-only
config HAVE_CLK
bool
+ help
+ The <linux/clk.h> calls support software clock gating and
+ thus are a key power management tool on many systems.
+
+config HAVE_LEGACY_CLK
+ select HAVE_CLK
+ bool
+ help
+ Select this option when the clock API in <linux/clk.h> is implemented
+ by platform/architecture code. This method is deprecated. Modern
+ code should select COMMON_CLK instead and not define a custom
+ 'struct clk'.
config CLKDEV_LOOKUP
bool
config COMMON_CLK
+ bool "Common Clock Framework"
+ depends on !HAVE_LEGACY_CLK
select HAVE_CLK
- bool
+ select CLKDEV_LOOKUP
+ help
+ The common clock framework is a single definition of struct
+ clk, useful across many platforms, as well as an
+ implementation of the clock API in include/linux/clk.h.
+ Architectures utilizing the common struct clk should select
+ this option.
config COMMON_CLK_OF_PROVIDER
bool
@@ -28,6 +48,16 @@ config COMMON_CLK_STM32F
help
Support for stm32f4 and stm32f7 SoC families clocks
+config COMMON_CLK_STM32MP135
+ def_bool ARCH_STM32MP13
+ help
+ Support for stm32mp135 SoC family clocks
+
+config COMMON_CLK_STM32MP157
+ def_bool ARCH_STM32MP157
+ help
+ Support for stm32mp157 SoC family clocks
+
config COMMON_CLK_SCMI
tristate "Clock driver controlled via SCMI interface"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
@@ -38,6 +68,20 @@ config COMMON_CLK_SCMI
This driver uses SCMI Message Protocol to interact with the
firmware providing all the clock controls.
+config TI_SCI_CLK
+ tristate "TI System Control Interface clock drivers"
+ depends on TI_SCI_PROTOCOL
+ default ARCH_K3
+ help
+ This adds the clock driver support over TI System Control Interface.
+ If you wish to use clock resources from the PMMC firmware, say Y.
+ Otherwise, say N.
+
+config COMMON_CLK_GPIO
+ bool
+ default y
+ depends on COMMON_CLK_OF_PROVIDER
+
source "drivers/clk/sifive/Kconfig"
endif
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index baf452de98..764539e91e 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -2,7 +2,7 @@
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \
clk-mux.o clk-gate.o clk-composite.o \
clk-fractional-divider.o clk-conf.o \
- clk-gate-shared.o clk-gpio.o \
+ clk-gate-shared.o \
clk-bulk.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
@@ -17,7 +17,8 @@ obj-$(CONFIG_SOC_QCA_AR9331) += clk-ar933x.o
obj-$(CONFIG_SOC_QCA_AR9344) += clk-ar9344.o
obj-$(CONFIG_ARCH_IMX) += imx/
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
-obj-$(CONFIG_ARCH_STM32MP) += clk-stm32mp1.o
+obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o
+obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
obj-$(CONFIG_MACH_MIPS_LOONGSON)+= loongson/
obj-$(CONFIG_ARCH_LAYERSCAPE) += clk-qoric.o
@@ -28,3 +29,5 @@ obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
obj-$(CONFIG_MACH_RPI_COMMON) += clk-rpi.o
obj-y += bcm/
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
+obj-$(CONFIG_COMMON_CLK_GPIO) += clk-gpio.o
+obj-$(CONFIG_TI_SCI_CLK) += ti-sci-clk.o
diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c
index dbefcd92d3..df75a93edb 100644
--- a/drivers/clk/at91/at91rm9200.c
+++ b/drivers/clk/at91/at91rm9200.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-//
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(rm9200_mck_lock);
+
struct sck {
char *n;
char *p;
@@ -44,7 +41,7 @@ static const struct clk_pll_characteristics rm9200_pll_characteristics = {
};
static const struct sck at91rm9200_systemck[] = {
- { .n = "udpck", .p = "usbck", .id = 2 },
+ { .n = "udpck", .p = "usbck", .id = 1 },
{ .n = "uhpck", .p = "usbck", .id = 4 },
{ .n = "pck0", .p = "prog0", .id = 8 },
{ .n = "pck1", .p = "prog1", .id = 9 },
@@ -85,7 +82,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
u32 usb_div[] = { 1, 2, 0, 0 };
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -143,9 +140,19 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91rm9200_master_layout,
- &rm9200_mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91rm9200_master_layout,
+ &rm9200_mck_characteristics,
+ &rm9200_mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91rm9200_master_layout,
+ &rm9200_mck_characteristics,
+ &rm9200_mck_lock, CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -160,11 +167,14 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
for (i = 0; i < 4; i++) {
- char *name = xasprintf("prog%d", i);
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -174,7 +184,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) {
hw = at91_clk_register_system(regmap, at91rm9200_systemck[i].n,
at91rm9200_systemck[i].p,
- at91rm9200_systemck[i].id);
+ at91rm9200_systemck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -184,7 +194,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) {
hw = at91_clk_register_peripheral(regmap,
at91rm9200_periphck[i].n,
- "masterck",
+ "masterck_div",
at91rm9200_periphck[i].id);
if (IS_ERR(hw))
goto err_free;
@@ -192,7 +202,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
at91rm9200_pmc->phws[at91rm9200_periphck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91rm9200_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91rm9200_pmc);
return;
@@ -205,5 +215,4 @@ err_free:
* deferring properly. Once this is fixed, this can be switched to a platform
* driver.
*/
-CLK_OF_DECLARE_DRIVER(at91rm9200_pmc, "atmel,at91rm9200-pmc",
- at91rm9200_pmc_setup);
+CLK_OF_DECLARE(at91rm9200_pmc, "atmel,at91rm9200-pmc", at91rm9200_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c
index 3348cc6637..c94cd95566 100644
--- a/drivers/clk/at91/at91sam9260.c
+++ b/drivers/clk/at91/at91sam9260.c
@@ -1,13 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
@@ -38,6 +33,8 @@ struct at91sam926x_data {
bool has_slck;
};
+static DEFINE_SPINLOCK(at91sam9260_mck_lock);
+
static const struct clk_master_characteristics sam9260_mck_characteristics = {
.output = { .min = 0, .max = 105000000 },
.divisors = { 1, 2, 4, 0 },
@@ -224,8 +221,8 @@ static const struct sck at91sam9261_systemck[] = {
{ .n = "pck1", .p = "prog1", .id = 9 },
{ .n = "pck2", .p = "prog2", .id = 10 },
{ .n = "pck3", .p = "prog3", .id = 11 },
- { .n = "hclk0", .p = "masterck", .id = 16 },
- { .n = "hclk1", .p = "masterck", .id = 17 },
+ { .n = "hclk0", .p = "masterck_div", .id = 16 },
+ { .n = "hclk1", .p = "masterck_div", .id = 17 },
};
static const struct pck at91sam9261_periphck[] = {
@@ -339,7 +336,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
const char *parent_names[6];
const char *slck_name;
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -379,7 +376,10 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
at91sam9260_pmc->chws[PMC_MAIN] = hw;
if (data->has_slck) {
- hw = clk_fixed("slow_rc_osc", 32768);
+ hw = clk_hw_register_fixed_rate_with_accuracy(NULL,
+ "slow_rc_osc",
+ NULL, 0, 32768,
+ 50000000);
if (IS_ERR(hw))
goto err_free;
@@ -416,9 +416,20 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
parent_names[1] = "mainck";
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91rm9200_master_layout,
- data->mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91rm9200_master_layout,
+ data->mck_characteristics,
+ &at91sam9260_mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91rm9200_master_layout,
+ data->mck_characteristics,
+ &at91sam9260_mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -433,13 +444,14 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
for (i = 0; i < data->num_progck; i++) {
- char *name;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -449,7 +461,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
for (i = 0; i < data->num_sck; i++) {
hw = at91_clk_register_system(regmap, data->sck[i].n,
data->sck[i].p,
- data->sck[i].id);
+ data->sck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -459,7 +471,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
for (i = 0; i < data->num_pck; i++) {
hw = at91_clk_register_peripheral(regmap,
data->pck[i].n,
- "masterck",
+ "masterck_div",
data->pck[i].id);
if (IS_ERR(hw))
goto err_free;
@@ -467,7 +479,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
at91sam9260_pmc->phws[data->pck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc);
return;
@@ -479,26 +491,26 @@ static void __init at91sam9260_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9260_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9260_pmc, "atmel,at91sam9260-pmc",
- at91sam9260_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9260_pmc, "atmel,at91sam9260-pmc", at91sam9260_pmc_setup);
static void __init at91sam9261_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9261_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9261_pmc, "atmel,at91sam9261-pmc",
- at91sam9261_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9261_pmc, "atmel,at91sam9261-pmc", at91sam9261_pmc_setup);
static void __init at91sam9263_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9263_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9263_pmc, "atmel,at91sam9263-pmc",
- at91sam9263_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9263_pmc, "atmel,at91sam9263-pmc", at91sam9263_pmc_setup);
static void __init at91sam9g20_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9g20_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g20_pmc, "atmel,at91sam9g20-pmc",
- at91sam9g20_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g20_pmc, "atmel,at91sam9g20-pmc", at91sam9g20_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c
index 95dc3d6e79..fedf961393 100644
--- a/drivers/clk/at91/at91sam9g45.c
+++ b/drivers/clk/at91/at91sam9g45.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(at91sam9g45_mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 0, .max = 133333333 },
.divisors = { 1, 2, 4, 3 },
@@ -44,12 +41,17 @@ static const struct clk_pll_characteristics plla_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9g45_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
- { .n = "uhpck", .p = "usbck", .id = 6 },
- { .n = "pck0", .p = "prog0", .id = 8 },
- { .n = "pck1", .p = "prog1", .id = 9 },
+ /*
+ * 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 },
};
struct pck {
@@ -95,7 +97,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
struct pmc_data *at91sam9g45_pmc;
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -154,9 +156,20 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91rm9200_master_layout,
- &mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91rm9200_master_layout,
+ &mck_characteristics,
+ &at91sam9g45_mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91rm9200_master_layout,
+ &mck_characteristics,
+ &at91sam9g45_mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -172,13 +185,16 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
for (i = 0; i < 2; i++) {
- char *name = xasprintf("prog%d", i);
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9g45_programmable_layout);
+ &at91sam9g45_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -188,7 +204,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9g45_systemck[i].n,
at91sam9g45_systemck[i].p,
- at91sam9g45_systemck[i].id);
+ at91sam9g45_systemck[i].id,
+ at91sam9g45_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -198,7 +215,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) {
hw = at91_clk_register_peripheral(regmap,
at91sam9g45_periphck[i].n,
- "masterck",
+ "masterck_div",
at91sam9g45_periphck[i].id);
if (IS_ERR(hw))
goto err_free;
@@ -206,7 +223,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
at91sam9g45_pmc->phws[at91sam9g45_periphck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9g45_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9g45_pmc);
return;
@@ -217,5 +234,4 @@ err_free:
* The TCB is used as the clocksource so its clock is needed early. This means
* this can't be a platform driver.
*/
-CLK_OF_DECLARE_DRIVER(at91sam9g45_pmc, "atmel,at91sam9g45-pmc",
- at91sam9g45_pmc_setup);
+CLK_OF_DECLARE(at91sam9g45_pmc, "atmel,at91sam9g45-pmc", at91sam9g45_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
index bf17099453..bb075de9fd 100644
--- a/drivers/clk/at91/at91sam9n12.c
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -1,19 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
-
#include "pmc.h"
+static DEFINE_SPINLOCK(at91sam9n12_mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 0, .max = 133333333 },
.divisors = { 1, 2, 4, 3 },
@@ -59,14 +55,19 @@ static const struct clk_pll_characteristics pllb_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9n12_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
- { .n = "lcdck", .p = "masterck", .id = 3 },
- { .n = "uhpck", .p = "usbck", .id = 6 },
- { .n = "udpck", .p = "usbck", .id = 7 },
- { .n = "pck0", .p = "prog0", .id = 8 },
- { .n = "pck1", .p = "prog1", .id = 9 },
+ /*
+ * 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 = "lcdck", .p = "masterck_div", .id = 3 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
};
static const struct clk_pcr_layout at91sam9n12_pcr_layout = {
@@ -116,7 +117,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
struct pmc_data *at91sam9n12_pmc;
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -182,9 +183,20 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "pllbck";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91sam9x5_master_layout,
- &mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics,
+ &at91sam9n12_mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91sam9x5_master_layout,
+ &mck_characteristics,
+ &at91sam9n12_mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -198,13 +210,16 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "pllbck";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
for (i = 0; i < 2; i++) {
- char *name = xasprintf("prog%d", i);
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -214,7 +229,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9n12_systemck[i].n,
at91sam9n12_systemck[i].p,
- at91sam9n12_systemck[i].id);
+ at91sam9n12_systemck[i].id,
+ at91sam9n12_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -222,19 +238,19 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(at91sam9n12_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&at91sam9n12_pcr_layout,
at91sam9n12_periphck[i].n,
- "masterck",
+ "masterck_div",
at91sam9n12_periphck[i].id,
- &range);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
at91sam9n12_pmc->phws[at91sam9n12_periphck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9n12_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9n12_pmc);
return;
@@ -245,5 +261,4 @@ err_free:
* The TCB is used as the clocksource so its clock is needed early. This means
* this can't be a platform driver.
*/
-CLK_OF_DECLARE_DRIVER(at91sam9n12_pmc, "atmel,at91sam9n12-pmc",
- at91sam9n12_pmc_setup);
+CLK_OF_DECLARE(at91sam9n12_pmc, "atmel,at91sam9n12-pmc", at91sam9n12_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c
index 19002c8dab..95b02d86d5 100644
--- a/drivers/clk/at91/at91sam9rl.c
+++ b/drivers/clk/at91/at91sam9rl.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(sam9rl_mck_lock);
+
static const struct clk_master_characteristics sam9rl_mck_characteristics = {
.output = { .min = 0, .max = 94000000 },
.divisors = { 1, 2, 4, 0 },
@@ -75,7 +72,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
struct pmc_data *at91sam9rl_pmc;
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
i = of_property_match_string(np, "clock-names", "slow_clk");
@@ -123,9 +120,19 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "pllack";
parent_names[3] = "utmick";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91rm9200_master_layout,
- &sam9rl_mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91rm9200_master_layout,
+ &sam9rl_mck_characteristics,
+ &sam9rl_mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91rm9200_master_layout,
+ &sam9rl_mck_characteristics,
+ &sam9rl_mck_lock, CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -135,15 +142,16 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "pllack";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
for (i = 0; i < 2; i++) {
- char *name;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -153,7 +161,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9rl_systemck[i].n,
at91sam9rl_systemck[i].p,
- at91sam9rl_systemck[i].id);
+ at91sam9rl_systemck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -163,7 +171,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9rl_periphck); i++) {
hw = at91_clk_register_peripheral(regmap,
at91sam9rl_periphck[i].n,
- "masterck",
+ "masterck_div",
at91sam9rl_periphck[i].id);
if (IS_ERR(hw))
goto err_free;
@@ -171,11 +179,12 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
at91sam9rl_pmc->phws[at91sam9rl_periphck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc);
return;
err_free:
kfree(at91sam9rl_pmc);
}
-CLK_OF_DECLARE_DRIVER(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
index f9f45ed6e8..f4dc7ceeea 100644
--- a/drivers/clk/at91/at91sam9x5.c
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 0, .max = 133333333 },
.divisors = { 1, 2, 4, 3 },
@@ -45,9 +42,14 @@ static const struct clk_pll_characteristics plla_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9x5_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
+ /*
+ * 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 = "smdck", .p = "smdclk", .id = 4 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "udpck", .p = "usbck", .id = 7 },
@@ -137,7 +139,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
struct pmc_data *at91sam9x5_pmc;
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -202,9 +204,18 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91sam9x5_master_layout,
- &mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -224,15 +235,16 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
for (i = 0; i < 2; i++) {
- char *name;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -242,7 +254,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n,
at91sam9x5_systemck[i].p,
- at91sam9x5_systemck[i].id);
+ at91sam9x5_systemck[i].id,
+ at91sam9x5_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -250,7 +263,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
}
if (has_lcdck) {
- hw = at91_clk_register_system(regmap, "lcdck", "masterck", 3);
+ hw = at91_clk_register_system(regmap, "lcdck", "masterck_div",
+ 3, 0);
if (IS_ERR(hw))
goto err_free;
@@ -258,12 +272,12 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
}
for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&at91sam9x5_pcr_layout,
at91sam9x5_periphck[i].n,
- "masterck",
+ "masterck_div",
at91sam9x5_periphck[i].id,
- &range);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
@@ -271,19 +285,19 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
}
for (i = 0; extra_pcks[i].id; i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&at91sam9x5_pcr_layout,
extra_pcks[i].n,
- "masterck",
+ "masterck_div",
extra_pcks[i].id,
- &range);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
at91sam9x5_pmc->phws[extra_pcks[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc);
return;
@@ -295,33 +309,33 @@ static void __init at91sam9g15_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9g15_periphck, true);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g15_pmc, "atmel,at91sam9g15-pmc",
- at91sam9g15_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g15_pmc, "atmel,at91sam9g15-pmc", at91sam9g15_pmc_setup);
static void __init at91sam9g25_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9g25_periphck, false);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g25_pmc, "atmel,at91sam9g25-pmc",
- at91sam9g25_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g25_pmc, "atmel,at91sam9g25-pmc", at91sam9g25_pmc_setup);
static void __init at91sam9g35_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9g35_periphck, true);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g35_pmc, "atmel,at91sam9g35-pmc",
- at91sam9g35_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g35_pmc, "atmel,at91sam9g35-pmc", at91sam9g35_pmc_setup);
static void __init at91sam9x25_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9x25_periphck, false);
}
-CLK_OF_DECLARE_DRIVER(at91sam9x25_pmc, "atmel,at91sam9x25-pmc",
- at91sam9x25_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9x25_pmc, "atmel,at91sam9x25-pmc", at91sam9x25_pmc_setup);
static void __init at91sam9x35_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9x35_periphck, true);
}
-CLK_OF_DECLARE_DRIVER(at91sam9x35_pmc, "atmel,at91sam9x35-pmc",
- at91sam9x35_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9x35_pmc, "atmel,at91sam9x35-pmc", at91sam9x35_pmc_setup);
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
index e9a30b0516..71976567ea 100644
--- a/drivers/clk/at91/clk-audio-pll.c
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -30,14 +30,14 @@
* parent - fixed parent. No clk_set_parent support
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
#include <linux/clk.h>
+#include <linux/printk.h>
+#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
#include "pmc.h"
@@ -61,7 +61,6 @@ struct clk_audio_frac {
struct regmap *regmap;
u32 fracr;
u8 nd;
- const char *parent_name;
};
struct clk_audio_pad {
@@ -69,19 +68,17 @@ struct clk_audio_pad {
struct regmap *regmap;
u8 qdaudio;
u8 div;
- const char *parent_name;
};
struct clk_audio_pmc {
struct clk_hw hw;
struct regmap *regmap;
u8 qdpmc;
- const char *parent_name;
};
-#define to_clk_audio_frac(_hw) container_of(_hw, struct clk_audio_frac, hw)
-#define to_clk_audio_pad(_hw) container_of(_hw, struct clk_audio_pad, hw)
-#define to_clk_audio_pmc(_hw) container_of(_hw, struct clk_audio_pmc, hw)
+#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
+#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
+#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
static int clk_audio_pll_frac_enable(struct clk_hw *hw)
{
@@ -248,7 +245,7 @@ static int clk_audio_pll_frac_compute_frac(unsigned long rate,
static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct clk *pclk = clk_get_parent(clk_hw_to_clk(hw));
+ struct clk_hw *pclk = clk_hw_get_parent(hw);
long best_rate = -EINVAL;
unsigned long best_parent_rate;
unsigned long tmp_qd;
@@ -278,7 +275,7 @@ static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
if (div == 2 && tmp_qd % 3 == 0)
continue;
- best_parent_rate = clk_round_rate(pclk,
+ best_parent_rate = clk_hw_round_rate(pclk,
rate * tmp_qd * div);
tmp_rate = best_parent_rate / (div * tmp_qd);
tmp_diff = abs(rate - tmp_rate);
@@ -299,8 +296,7 @@ static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct clk *clk = clk_hw_to_clk(hw);
- struct clk *pclk = clk_get_parent(clk);
+ struct clk_hw *pclk = clk_hw_get_parent(hw);
long best_rate = -EINVAL;
unsigned long best_parent_rate = 0;
u32 tmp_qd = 0, div;
@@ -314,10 +310,10 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
if (!rate)
return 0;
- best_parent_rate = clk_round_rate(pclk, 1);
+ best_parent_rate = clk_round_rate(&pclk->clk, 1);
div = max(best_parent_rate / rate, 1UL);
for (; div <= AUDIO_PLL_QDPMC_MAX; div++) {
- best_parent_rate = clk_round_rate(pclk, rate * div);
+ best_parent_rate = clk_round_rate(&pclk->clk, rate * div);
tmp_rate = best_parent_rate / div;
tmp_diff = abs(rate - tmp_rate);
@@ -423,91 +419,94 @@ static const struct clk_ops audio_pll_pmc_ops = {
.set_rate = clk_audio_pll_pmc_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_audio_frac *frac_ck;
+ struct clk_init_data init = {};
int ret;
frac_ck = kzalloc(sizeof(*frac_ck), GFP_KERNEL);
if (!frac_ck)
return ERR_PTR(-ENOMEM);
- frac_ck->hw.clk.name = name;
- frac_ck->hw.clk.ops = &audio_pll_frac_ops;
- frac_ck->parent_name = parent_name;
- frac_ck->hw.clk.parent_names = &frac_ck->parent_name;
- frac_ck->hw.clk.num_parents = 1;
- /* frac_ck->clk.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &audio_pll_frac_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE;
+ frac_ck->hw.init = &init;
frac_ck->regmap = regmap;
- ret = bclk_register(&frac_ck->hw.clk);
+ ret = clk_hw_register(NULL, &frac_ck->hw);
if (ret) {
kfree(frac_ck);
return ERR_PTR(ret);
}
- return &frac_ck->hw.clk;
+ return &frac_ck->hw;
}
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_audio_pad *apad_ck;
+ struct clk_init_data init;
int ret;
apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
if (!apad_ck)
return ERR_PTR(-ENOMEM);
- apad_ck->hw.clk.name = name;
- apad_ck->hw.clk.ops = &audio_pll_pad_ops;
- apad_ck->parent_name = parent_name;
- apad_ck->hw.clk.parent_names = &apad_ck->parent_name;
- apad_ck->hw.clk.num_parents = 1;
- /* apad_ck->clk.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT; */
+ init.name = name;
+ init.ops = &audio_pll_pad_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ apad_ck->hw.init = &init;
apad_ck->regmap = regmap;
- ret = bclk_register(&apad_ck->hw.clk);
+ ret = clk_hw_register(NULL, &apad_ck->hw);
if (ret) {
kfree(apad_ck);
return ERR_PTR(ret);
}
- return &apad_ck->hw.clk;
+ return &apad_ck->hw;
}
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_audio_pmc *apmc_ck;
+ struct clk_init_data init;
int ret;
apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
if (!apmc_ck)
return ERR_PTR(-ENOMEM);
- apmc_ck->hw.clk.name = name;
- apmc_ck->hw.clk.ops = &audio_pll_pmc_ops;
- apmc_ck->parent_name = parent_name;
- apmc_ck->hw.clk.parent_names = &apmc_ck->parent_name;
- apmc_ck->hw.clk.num_parents = 1;
- /* apmc_ck.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT; */
+ init.name = name;
+ init.ops = &audio_pll_pmc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ apmc_ck->hw.init = &init;
apmc_ck->regmap = regmap;
- ret = bclk_register(&apmc_ck->hw.clk);
+ ret = clk_hw_register(NULL, &apmc_ck->hw);
if (ret) {
kfree(apmc_ck);
return ERR_PTR(ret);
}
- return &apmc_ck->hw.clk;
+ return &apmc_ck->hw;
}
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index e639fb3a1a..e59cff2bdf 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -6,76 +6,95 @@
* Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/bitfield.h>
+#include <linux/printk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-#include <linux/bitfield.h>
+#include <linux/regmap.h>
#include "pmc.h"
#define GENERATED_MAX_DIV 255
-#define GCK_INDEX_DT_AUDIO_PLL 5
-
struct clk_generated {
struct clk_hw hw;
struct regmap *regmap;
struct clk_range range;
+ spinlock_t *lock;
+ u32 *mux_table;
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
+ struct at91_clk_pms pms;
u8 parent_id;
- bool audio_pll_allowed;
+ int chg_pid;
};
-#define to_clk_generated(_hw) \
- container_of(_hw, struct clk_generated, hw)
+#define to_clk_generated(hw) \
+ container_of(hw, struct clk_generated, hw)
-static int clk_generated_enable(struct clk_hw *hw)
+static int clk_generated_set(struct clk_generated *gck, int status)
{
- struct clk_generated *gck = to_clk_generated(hw);
-
- pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
- __func__, gck->gckdiv, gck->parent_id);
+ unsigned long flags;
+ unsigned int enable = status ? AT91_PMC_PCR_GCKEN : 0;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_update_bits(gck->regmap, gck->layout->offset,
AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
- gck->layout->cmd | AT91_PMC_PCR_GCKEN,
+ gck->layout->cmd | enable,
field_prep(gck->layout->gckcss_mask, gck->parent_id) |
gck->layout->cmd |
FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
- AT91_PMC_PCR_GCKEN);
+ enable);
+ spin_unlock_irqrestore(gck->lock, flags);
+
+ return 0;
+}
+
+static int clk_generated_enable(struct clk_hw *hw)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+
+ pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
+ __func__, gck->gckdiv, gck->parent_id);
+
+ clk_generated_set(gck, 1);
+
return 0;
}
static void clk_generated_disable(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_update_bits(gck->regmap, gck->layout->offset,
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
gck->layout->cmd);
+ spin_unlock_irqrestore(gck->lock, flags);
}
static int clk_generated_is_enabled(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
unsigned int status;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_read(gck->regmap, gck->layout->offset, &status);
+ spin_unlock_irqrestore(gck->lock, flags);
- return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
+ return !!(status & AT91_PMC_PCR_GCKEN);
}
static unsigned long
@@ -95,7 +114,11 @@ static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;
- gck->parent_id = index;
+ if (gck->mux_table)
+ gck->parent_id = clk_mux_index_to_val(gck->mux_table, 0, index);
+ else
+ gck->parent_id = index;
+
return 0;
}
@@ -150,54 +173,59 @@ static const struct clk_ops generated_ops = {
static void clk_generated_startup(struct clk_generated *gck)
{
u32 tmp;
+ unsigned long flags;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_read(gck->regmap, gck->layout->offset, &tmp);
+ spin_unlock_irqrestore(gck->lock, flags);
gck->parent_id = field_get(gck->layout->gckcss_mask, tmp);
gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp);
}
-struct clk * __init
-at91_clk_register_generated(struct regmap *regmap,
+struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id, bool pll_audio,
- const struct clk_range *range)
+ u32 *mux_table, u8 num_parents, u8 id,
+ const struct clk_range *range,
+ int chg_pid)
{
- size_t parents_array_size;
struct clk_generated *gck;
- struct clk *clk;
+ struct clk_init_data init;
+ struct clk_hw *hw;
int ret;
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
return ERR_PTR(-ENOMEM);
- gck->id = id;
- gck->hw.clk.name = name;
- gck->hw.clk.ops = &generated_ops;
-
- parents_array_size = num_parents * sizeof(gck->hw.clk.parent_names[0]);
- gck->hw.clk.parent_names = xmemdup(parent_names, parents_array_size);
- gck->hw.clk.num_parents = num_parents;
+ init.name = name;
+ init.ops = &generated_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ if (chg_pid >= 0)
+ init.flags |= CLK_SET_RATE_PARENT;
- /* gck->hw.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | CLK_SET_PARENT; */
+ gck->id = id;
+ gck->hw.init = &init;
gck->regmap = regmap;
+ gck->lock = lock;
gck->range = *range;
- gck->audio_pll_allowed = pll_audio;
+ gck->chg_pid = chg_pid;
gck->layout = layout;
+ gck->mux_table = mux_table;
clk_generated_startup(gck);
- clk = &gck->hw.clk;
- ret = bclk_register(&gck->hw.clk);
+ hw = &gck->hw;
+ ret = clk_hw_register(NULL, &gck->hw);
if (ret) {
kfree(gck);
- clk = ERR_PTR(ret);
- } else {
- pmc_register_id(id);
+ hw = ERR_PTR(ret);
}
- return clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
index b2c5007cf7..e5b98692a9 100644
--- a/drivers/clk/at91/clk-h32mx.c
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -7,13 +7,13 @@
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <regmap.h>
-
+#include <of.h>
+#include <linux/regmap.h>
+#include <mfd/syscon.h>
+#include <linux/printk.h>
#include "pmc.h"
@@ -22,10 +22,9 @@
struct clk_sama5d4_h32mx {
struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
};
-#define to_clk_sama5d4_h32mx(_hw) container_of(_hw, struct clk_sama5d4_h32mx, hw)
+#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -83,31 +82,32 @@ static const struct clk_ops h32mx_ops = {
.set_rate = clk_sama5d4_h32mx_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_sama5d4_h32mx *h32mxclk;
+ struct clk_init_data init;
int ret;
h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL);
if (!h32mxclk)
return ERR_PTR(-ENOMEM);
- h32mxclk->parent = parent_name;
- h32mxclk->hw.clk.name = name;
- h32mxclk->hw.clk.ops = &h32mx_ops;
- h32mxclk->hw.clk.parent_names = &h32mxclk->parent;
- h32mxclk->hw.clk.num_parents = 1;
- /* h32mxclk.hw.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &h32mx_ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = CLK_SET_RATE_GATE;
+ h32mxclk->hw.init = &init;
h32mxclk->regmap = regmap;
- ret = bclk_register(&h32mxclk->hw.clk);
+ ret = clk_hw_register(NULL, &h32mxclk->hw);
if (ret) {
kfree(h32mxclk);
return ERR_PTR(ret);
}
- return &h32mxclk->hw.clk;
+ return &h32mxclk->hw;
}
diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c
index 5e7040a5b7..71ef2e6386 100644
--- a/drivers/clk/at91/clk-i2s-mux.c
+++ b/drivers/clk/at91/clk-i2s-mux.c
@@ -6,15 +6,11 @@
*
*/
-#include <common.h>
-#include <clock.h>
+#include <linux/clk-provider.h>
#include <of.h>
-#include <linux/list.h>
-#include <linux/clk.h>
-#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
#include <soc/at91/atmel-sfr.h>
@@ -24,10 +20,9 @@ struct clk_i2s_mux {
struct clk_hw hw;
struct regmap *regmap;
u8 bus_id;
- const char *parent_names[];
};
-#define to_clk_i2s_mux(_hw) container_of(_hw, struct clk_i2s_mux, hw)
+#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
static int clk_i2s_mux_get_parent(struct clk_hw *hw)
{
@@ -48,39 +43,37 @@ static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_i2s_mux_ops = {
- .set_rate = clk_parent_set_rate,
- .round_rate = clk_parent_round_rate,
.get_parent = clk_i2s_mux_get_parent,
.set_parent = clk_i2s_mux_set_parent,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
const char * const *parent_names,
unsigned int num_parents, u8 bus_id)
{
+ struct clk_init_data init = {};
struct clk_i2s_mux *i2s_ck;
int ret;
- i2s_ck = kzalloc(struct_size(i2s_ck, parent_names, num_parents), GFP_KERNEL);
+ i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
if (!i2s_ck)
return ERR_PTR(-ENOMEM);
- i2s_ck->hw.clk.name = name;
- i2s_ck->hw.clk.ops = &clk_i2s_mux_ops;
- memcpy(i2s_ck->parent_names, parent_names,
- num_parents * sizeof(i2s_ck->parent_names[0]));
- i2s_ck->hw.clk.parent_names = &i2s_ck->parent_names[0];
- i2s_ck->hw.clk.num_parents = num_parents;
+ init.name = name;
+ init.ops = &clk_i2s_mux_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ i2s_ck->hw.init = &init;
i2s_ck->bus_id = bus_id;
i2s_ck->regmap = regmap;
- ret = bclk_register(&i2s_ck->hw.clk);
+ ret = clk_hw_register(NULL, &i2s_ck->hw);
if (ret) {
kfree(i2s_ck);
return ERR_PTR(ret);
}
- return &i2s_ck->hw.clk;
+ return &i2s_ck->hw;
}
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 38e72d6538..a1dd327b56 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -2,21 +2,22 @@
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <clock.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/printk.h>
#include "pmc.h"
#define SLOW_CLOCK_FREQ 32768
#define MAINF_DIV 16
-#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * SECOND) / \
+#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \
SLOW_CLOCK_FREQ)
-#define MAINF_LOOP_MIN_WAIT (SECOND / SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ)
#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT
#define MOR_KEY_MASK (0xff << 16)
@@ -28,34 +29,36 @@
struct clk_main_osc {
struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
+ struct at91_clk_pms pms;
};
-#define to_clk_main_osc(_hw) container_of(_hw, struct clk_main_osc, hw)
+#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
struct clk_main_rc_osc {
struct clk_hw hw;
struct regmap *regmap;
unsigned long frequency;
+ unsigned long accuracy;
+ struct at91_clk_pms pms;
};
-#define to_clk_main_rc_osc(_hw) container_of(_hw, struct clk_main_rc_osc, hw)
+#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)
struct clk_rm9200_main {
struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
};
-#define to_clk_rm9200_main(_hw) container_of(_hw, struct clk_rm9200_main, hw)
+#define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw)
struct clk_sam9x5_main {
struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
u8 parent;
};
-#define to_clk_sam9x5_main(_hw) container_of(_hw, struct clk_sam9x5_main, hw)
+#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw)
static inline bool clk_main_osc_ready(struct regmap *regmap)
{
@@ -66,7 +69,7 @@ static inline bool clk_main_osc_ready(struct regmap *regmap)
return status & AT91_PMC_MOSCS;
}
-static int clk_main_osc_enable(struct clk_hw *hw)
+static int clk_main_osc_prepare(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);
struct regmap *regmap = osc->regmap;
@@ -84,12 +87,12 @@ static int clk_main_osc_enable(struct clk_hw *hw)
}
while (!clk_main_osc_ready(regmap))
- barrier();
+ cpu_relax();
return 0;
}
-static void clk_main_osc_disable(struct clk_hw *hw)
+static void clk_main_osc_unprepare(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);
struct regmap *regmap = osc->regmap;
@@ -106,7 +109,7 @@ static void clk_main_osc_disable(struct clk_hw *hw)
regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);
}
-static int clk_main_osc_is_enabled(struct clk_hw *hw)
+static int clk_main_osc_is_prepared(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);
struct regmap *regmap = osc->regmap;
@@ -122,45 +125,52 @@ static int clk_main_osc_is_enabled(struct clk_hw *hw)
}
static const struct clk_ops main_osc_ops = {
- .enable = clk_main_osc_enable,
- .disable = clk_main_osc_disable,
- .is_enabled = clk_main_osc_is_enabled,
+ .enable = clk_main_osc_prepare,
+ .disable = clk_main_osc_unprepare,
+ .is_enabled = clk_main_osc_is_prepared,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap,
const char *name,
const char *parent_name,
bool bypass)
{
struct clk_main_osc *osc;
+ struct clk_init_data init;
+ struct clk_hw *hw;
int ret;
if (!name || !parent_name)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
- osc->parent = parent_name;
- osc->hw.clk.name = name;
- osc->hw.clk.ops = &main_osc_ops;
- osc->hw.clk.parent_names = &osc->parent;
- osc->hw.clk.num_parents = 1;
+ init.name = name;
+ init.ops = &main_osc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ osc->hw.init = &init;
osc->regmap = regmap;
if (bypass)
- regmap_write_bits(regmap,
- AT91_CKGR_MOR, MOR_KEY_MASK |
- AT91_PMC_MOSCEN,
- AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
+ regmap_update_bits(regmap,
+ AT91_CKGR_MOR, MOR_KEY_MASK |
+ AT91_PMC_OSCBYPASS,
+ AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
- ret = bclk_register(&osc->hw.clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
- free(osc);
- return ERR_PTR(ret);
+ kfree(osc);
+ hw = ERR_PTR(ret);
}
- return &osc->hw.clk;
+ return hw;
}
static bool clk_main_rc_osc_ready(struct regmap *regmap)
@@ -169,10 +179,10 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MOSCRCS;
+ return !!(status & AT91_PMC_MOSCRCS);
}
-static int clk_main_rc_osc_enable(struct clk_hw *hw)
+static int clk_main_rc_osc_prepare(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
struct regmap *regmap = osc->regmap;
@@ -181,17 +191,17 @@ static int clk_main_rc_osc_enable(struct clk_hw *hw)
regmap_read(regmap, AT91_CKGR_MOR, &mor);
if (!(mor & AT91_PMC_MOSCRCEN))
- regmap_write_bits(regmap, AT91_CKGR_MOR,
- MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
- AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+ AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
while (!clk_main_rc_osc_ready(regmap))
- barrier();
+ cpu_relax();
return 0;
}
-static void clk_main_rc_osc_disable(struct clk_hw *hw)
+static void clk_main_rc_osc_unprepare(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
struct regmap *regmap = osc->regmap;
@@ -202,11 +212,11 @@ static void clk_main_rc_osc_disable(struct clk_hw *hw)
if (!(mor & AT91_PMC_MOSCRCEN))
return;
- regmap_write_bits(regmap, AT91_CKGR_MOR,
- MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);
}
-static int clk_main_rc_osc_is_enabled(struct clk_hw *hw)
+static int clk_main_rc_osc_is_prepared(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
struct regmap *regmap = osc->regmap;
@@ -227,52 +237,62 @@ static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw,
}
static const struct clk_ops main_rc_osc_ops = {
- .enable = clk_main_rc_osc_enable,
- .disable = clk_main_rc_osc_disable,
- .is_enabled = clk_main_rc_osc_is_enabled,
+ .enable = clk_main_rc_osc_prepare,
+ .disable = clk_main_rc_osc_unprepare,
+ .is_enabled = clk_main_rc_osc_is_prepared,
.recalc_rate = clk_main_rc_osc_recalc_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_rc_osc(struct regmap *regmap,
const char *name,
u32 frequency, u32 accuracy)
{
- int ret;
struct clk_main_rc_osc *osc;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !frequency)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
- osc->hw.clk.name = name;
- osc->hw.clk.ops = &main_rc_osc_ops;
- osc->hw.clk.parent_names = NULL;
- osc->hw.clk.num_parents = 0;
+ init.name = name;
+ init.ops = &main_rc_osc_ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->regmap = regmap;
osc->frequency = frequency;
+ osc->accuracy = accuracy;
- ret = bclk_register(&osc->hw.clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(osc);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &osc->hw.clk;
+ return hw;
}
static int clk_main_probe_frequency(struct regmap *regmap)
{
+ u64 start_time;
unsigned int mcfr;
- uint64_t start = get_time_ns();
+ start_time = get_time_ns();
do {
regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
if (mcfr & AT91_PMC_MAINRDY)
return 0;
- } while (!is_timeout(start, MAINFRDY_TIMEOUT * USECOND));
+ udelay(MAINF_LOOP_MIN_WAIT);
+ } while (!is_timeout(start_time, MAINFRDY_TIMEOUT * NSEC_PER_USEC));
return -ETIMEDOUT;
}
@@ -293,21 +313,21 @@ static unsigned long clk_main_recalc_rate(struct regmap *regmap,
return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV;
}
-static int clk_rm9200_main_enable(struct clk_hw *hw)
+static int clk_rm9200_main_prepare(struct clk_hw *hw)
{
struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
return clk_main_probe_frequency(clkmain->regmap);
}
-static int clk_rm9200_main_is_enabled(struct clk_hw *hw)
+static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
{
struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
unsigned int status;
regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
- return status & AT91_PMC_MAINRDY ? 1 : 0;
+ return !!(status & AT91_PMC_MAINRDY);
}
static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
@@ -319,18 +339,20 @@ static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
}
static const struct clk_ops rm9200_main_ops = {
- .enable = clk_rm9200_main_enable,
- .is_enabled = clk_rm9200_main_is_enabled,
+ .enable = clk_rm9200_main_prepare,
+ .is_enabled = clk_rm9200_main_is_prepared,
.recalc_rate = clk_rm9200_main_recalc_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
const char *parent_name)
{
- int ret;
struct clk_rm9200_main *clkmain;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -338,22 +360,27 @@ at91_clk_register_rm9200_main(struct regmap *regmap,
if (!parent_name)
return ERR_PTR(-EINVAL);
- clkmain = xzalloc(sizeof(*clkmain));
+ clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
+ if (!clkmain)
+ return ERR_PTR(-ENOMEM);
- clkmain->parent = parent_name;
- clkmain->hw.clk.name = name;
- clkmain->hw.clk.ops = &rm9200_main_ops;
- clkmain->hw.clk.parent_names = &clkmain->parent;
- clkmain->hw.clk.num_parents = 1;
+ init.name = name;
+ init.ops = &rm9200_main_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
+
+ clkmain->hw.init = &init;
clkmain->regmap = regmap;
- ret = bclk_register(&clkmain->hw.clk);
+ hw = &clkmain->hw;
+ ret = clk_hw_register(NULL, &clkmain->hw);
if (ret) {
kfree(clkmain);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &clkmain->hw.clk;
+ return hw;
}
static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
@@ -362,21 +389,21 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MOSCSELS ? 1 : 0;
+ return !!(status & AT91_PMC_MOSCSELS);
}
-static int clk_sam9x5_main_enable(struct clk_hw *hw)
+static int clk_sam9x5_main_prepare(struct clk_hw *hw)
{
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
struct regmap *regmap = clkmain->regmap;
while (!clk_sam9x5_main_ready(regmap))
- barrier();
+ cpu_relax();
return clk_main_probe_frequency(regmap);
}
-static int clk_sam9x5_main_is_enabled(struct clk_hw *hw)
+static int clk_sam9x5_main_is_prepared(struct clk_hw *hw)
{
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
@@ -401,15 +428,20 @@ static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)
return -EINVAL;
regmap_read(regmap, AT91_CKGR_MOR, &tmp);
- tmp &= ~MOR_KEY_MASK;
if (index && !(tmp & AT91_PMC_MOSCSEL))
- regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+ tmp = AT91_PMC_MOSCSEL;
else if (!index && (tmp & AT91_PMC_MOSCSEL))
- regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+ tmp = 0;
+ else
+ return 0;
+
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ AT91_PMC_MOSCSEL | MOR_KEY_MASK,
+ tmp | AT91_PMC_KEY);
while (!clk_sam9x5_main_ready(regmap))
- barrier();
+ cpu_relax();
return 0;
}
@@ -425,23 +457,24 @@ static int clk_sam9x5_main_get_parent(struct clk_hw *hw)
}
static const struct clk_ops sam9x5_main_ops = {
- .enable = clk_sam9x5_main_enable,
- .is_enabled = clk_sam9x5_main_is_enabled,
+ .enable = clk_sam9x5_main_prepare,
+ .is_enabled = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents)
{
- int ret;
- unsigned int status;
- size_t parents_array_size;
struct clk_sam9x5_main *clkmain;
+ struct clk_init_data init;
+ unsigned int status;
+ struct clk_hw *hw;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -449,25 +482,27 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
if (!parent_names || !num_parents)
return ERR_PTR(-EINVAL);
- clkmain = xzalloc(sizeof(*clkmain));
-
- clkmain->hw.clk.name = name;
- clkmain->hw.clk.ops = &sam9x5_main_ops;
- parents_array_size = num_parents * sizeof (clkmain->hw.clk.parent_names[0]);
- clkmain->hw.clk.parent_names = xmemdup(parent_names, parents_array_size);
- clkmain->hw.clk.num_parents = num_parents;
+ clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
+ if (!clkmain)
+ return ERR_PTR(-ENOMEM);
- /* init.flags = CLK_SET_PARENT_GATE; */
+ init.name = name;
+ init.ops = &sam9x5_main_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_PARENT_GATE;
+ clkmain->hw.init = &init;
clkmain->regmap = regmap;
regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
clkmain->parent = clk_main_parent_select(status);
- ret = bclk_register(&clkmain->hw.clk);
+ hw = &clkmain->hw;
+ ret = clk_hw_register(NULL, &clkmain->hw);
if (ret) {
kfree(clkmain);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &clkmain->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 3e4836b667..db5e235b6b 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -2,144 +2,462 @@
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/printk.h>
#include "pmc.h"
#define MASTER_PRES_MASK 0x7
#define MASTER_PRES_MAX MASTER_PRES_MASK
#define MASTER_DIV_SHIFT 8
-#define MASTER_DIV_MASK 0x3
+#define MASTER_DIV_MASK 0x7
+
+#define PMC_MCR_CSS_SHIFT (16)
-#define to_clk_master(_hw) container_of(_hw, struct clk_master, hw)
+#define MASTER_MAX_ID 4
+
+#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
struct clk_master {
struct clk_hw hw;
struct regmap *regmap;
+ spinlock_t *lock;
const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics;
+ struct at91_clk_pms pms;
+ u32 *mux_table;
u32 mckr;
- const char *parents[];
+ int chg_pid;
+ u8 id;
+ u8 parent;
+ u8 div;
};
-static inline bool clk_master_ready(struct regmap *regmap)
+static inline bool clk_master_ready(struct clk_master *master)
{
+ unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
unsigned int status;
- regmap_read(regmap, AT91_PMC_SR, &status);
+ regmap_read(master->regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MCKRDY ? 1 : 0;
+ return !!(status & bit);
}
-static int clk_master_enable(struct clk_hw *hw)
+static int clk_master_prepare(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
- while (!clk_master_ready(master->regmap))
- barrier();
+ spin_lock_irqsave(master->lock, flags);
+
+ while (!clk_master_ready(master))
+ cpu_relax();
+
+ spin_unlock_irqrestore(master->lock, flags);
return 0;
}
-static int clk_master_is_enabled(struct clk_hw *hw)
+static int clk_master_is_prepared(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ bool status;
+
+ spin_lock_irqsave(master->lock, flags);
+ status = clk_master_ready(master);
+ spin_unlock_irqrestore(master->lock, flags);
- return clk_master_ready(master->regmap);
+ return status;
}
-static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static unsigned long clk_master_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- u8 pres;
u8 div;
- unsigned long rate = parent_rate;
+ unsigned long flags, rate = parent_rate;
struct clk_master *master = to_clk_master(hw);
const struct clk_master_layout *layout = master->layout;
const struct clk_master_characteristics *characteristics =
master->characteristics;
unsigned int mckr;
+ spin_lock_irqsave(master->lock, flags);
regmap_read(master->regmap, master->layout->offset, &mckr);
+ spin_unlock_irqrestore(master->lock, flags);
+
mckr &= layout->mask;
- pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
- if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
- rate /= 3;
- else
- rate >>= pres;
-
rate /= characteristics->divisors[div];
if (rate < characteristics->output.min)
- pr_warn("master clk is underclocked");
+ pr_warn("master clk div is underclocked");
else if (rate > characteristics->output.max)
- pr_warn("master clk is overclocked");
+ pr_warn("master clk div is overclocked");
return rate;
}
-static int clk_master_get_parent(struct clk_hw *hw)
+static const struct clk_ops master_div_ops = {
+ .enable = clk_master_prepare,
+ .is_enabled = clk_master_is_prepared,
+ .recalc_rate = clk_master_div_recalc_rate,
+};
+
+static unsigned long clk_master_div_recalc_rate_chg(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, master->div);
+}
+
+static const struct clk_ops master_div_ops_chg = {
+ .enable = clk_master_prepare,
+ .is_enabled = clk_master_is_prepared,
+ .recalc_rate = clk_master_div_recalc_rate_chg,
+};
+
+static unsigned long clk_master_pres_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+ const struct clk_master_characteristics *characteristics =
+ master->characteristics;
+ unsigned long flags;
+ unsigned int val, pres;
+
+ spin_lock_irqsave(master->lock, flags);
+ regmap_read(master->regmap, master->layout->offset, &val);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ val &= master->layout->mask;
+ pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
+ if (pres == MASTER_PRES_MAX && characteristics->have_div3_pres)
+ pres = 3;
+ else
+ pres = (1 << pres);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, pres);
+}
+
+static int clk_master_pres_get_parent(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
unsigned int mckr;
+ spin_lock_irqsave(master->lock, flags);
regmap_read(master->regmap, master->layout->offset, &mckr);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ mckr &= master->layout->mask;
return mckr & AT91_PMC_CSS;
}
-static const struct clk_ops master_ops = {
- .enable = clk_master_enable,
- .is_enabled = clk_master_is_enabled,
- .recalc_rate = clk_master_recalc_rate,
- .get_parent = clk_master_get_parent,
+static const struct clk_ops master_pres_ops = {
+ .enable = clk_master_prepare,
+ .is_enabled = clk_master_is_prepared,
+ .recalc_rate = clk_master_pres_recalc_rate,
+ .get_parent = clk_master_pres_get_parent,
};
-struct clk * __init
-at91_clk_register_master(struct regmap *regmap,
- const char *name, int num_parents,
- const char **parent_names,
- const struct clk_master_layout *layout,
- const struct clk_master_characteristics *characteristics)
+static struct clk_hw * __init
+at91_clk_register_master_internal(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics,
+ const struct clk_ops *ops, spinlock_t *lock, u32 flags)
{
- int ret;
- const size_t parent_names_size = num_parents * sizeof(parent_names[0]);
struct clk_master *master;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ unsigned int mckr;
+ unsigned long irqflags;
+ int ret;
- if (!name || !num_parents || !parent_names)
+ if (!name || !num_parents || !parent_names || !lock)
return ERR_PTR(-EINVAL);
- master = xzalloc(struct_size(master, parents, num_parents));
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return ERR_PTR(-ENOMEM);
- master->hw.clk.name = name;
- master->hw.clk.ops = &master_ops;
- memcpy(master->parents, parent_names, parent_names_size);
- master->hw.clk.parent_names = master->parents;
- master->hw.clk.num_parents = num_parents;
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = flags;
+ master->hw.init = &init;
master->layout = layout;
master->characteristics = characteristics;
master->regmap = regmap;
+ master->lock = lock;
+
+ if (ops == &master_div_ops_chg) {
+ spin_lock_irqsave(master->lock, irqflags);
+ regmap_read(master->regmap, master->layout->offset, &mckr);
+ spin_unlock_irqrestore(master->lock, irqflags);
+
+ mckr &= layout->mask;
+ mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+ master->div = characteristics->divisors[mckr];
+ }
- ret = bclk_register(&master->hw.clk);
+ hw = &master->hw;
+ ret = clk_hw_register(NULL, &master->hw);
if (ret) {
kfree(master);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &master->hw.clk;
+ return hw;
+}
+
+struct clk_hw * __init
+at91_clk_register_master_pres(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics,
+ spinlock_t *lock)
+{
+ return at91_clk_register_master_internal(regmap, name, num_parents,
+ parent_names, layout,
+ characteristics,
+ &master_pres_ops,
+ lock, CLK_SET_RATE_GATE);
+}
+
+struct clk_hw * __init
+at91_clk_register_master_div(struct regmap *regmap,
+ const char *name, const char *parent_name,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics,
+ spinlock_t *lock, u32 flags)
+{
+ const struct clk_ops *ops;
+
+ if (flags & CLK_SET_RATE_GATE)
+ ops = &master_div_ops;
+ else
+ ops = &master_div_ops_chg;
+
+ return at91_clk_register_master_internal(regmap, name, 1,
+ &parent_name, layout,
+ characteristics, ops,
+ lock, flags);
+}
+
+static unsigned long
+clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
+}
+
+static int clk_sama7g5_master_get_parent(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ u8 index;
+
+ spin_lock_irqsave(master->lock, flags);
+ index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
+ master->parent);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return index;
}
+static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ spin_lock_irqsave(master->lock, flags);
+ master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static void clk_sama7g5_master_set(struct clk_master *master,
+ unsigned int status)
+{
+ unsigned long flags;
+ unsigned int val, cparent;
+ unsigned int enable = status ? AT91_PMC_MCR_V2_EN : 0;
+ unsigned int parent = master->parent << PMC_MCR_CSS_SHIFT;
+ unsigned int div = master->div << MASTER_DIV_SHIFT;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, AT91_PMC_MCR_V2,
+ AT91_PMC_MCR_V2_ID(master->id));
+ regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+ regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
+ enable | AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV |
+ AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK,
+ enable | parent | div | AT91_PMC_MCR_V2_CMD |
+ AT91_PMC_MCR_V2_ID(master->id));
+
+ cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
+
+ /* Wait here only if parent is being changed. */
+ while ((cparent != master->parent) && !clk_master_ready(master))
+ cpu_relax();
+
+ spin_unlock_irqrestore(master->lock, flags);
+}
+
+static int clk_sama7g5_master_enable(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+
+ clk_sama7g5_master_set(master, 1);
+
+ return 0;
+}
+
+static void clk_sama7g5_master_disable(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+ regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
+ AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD |
+ AT91_PMC_MCR_V2_ID_MSK,
+ AT91_PMC_MCR_V2_CMD |
+ AT91_PMC_MCR_V2_ID(master->id));
+
+ spin_unlock_irqrestore(master->lock, flags);
+}
+
+static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+ regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return !!(val & AT91_PMC_MCR_V2_EN);
+}
+
+static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long div, flags;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
+ return -EINVAL;
+
+ if (div == 3)
+ div = MASTER_PRES_MAX;
+ else if (div)
+ div = ffs(div) - 1;
+
+ spin_lock_irqsave(master->lock, flags);
+ master->div = div;
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops sama7g5_master_ops = {
+ .enable = clk_sama7g5_master_enable,
+ .disable = clk_sama7g5_master_disable,
+ .is_enabled = clk_sama7g5_master_is_enabled,
+ .recalc_rate = clk_sama7g5_master_recalc_rate,
+ .set_rate = clk_sama7g5_master_set_rate,
+ .get_parent = clk_sama7g5_master_get_parent,
+ .set_parent = clk_sama7g5_master_set_parent,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_master(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names,
+ u32 *mux_table,
+ spinlock_t *lock, u8 id,
+ bool critical, int chg_pid)
+{
+ struct clk_master *master;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long flags;
+ unsigned int val;
+ int ret;
+
+ if (!name || !num_parents || !parent_names || !mux_table ||
+ !lock || id > MASTER_MAX_ID)
+ return ERR_PTR(-EINVAL);
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sama7g5_master_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ if (chg_pid >= 0)
+ init.flags |= CLK_SET_RATE_PARENT;
+ if (critical)
+ init.flags |= CLK_IS_CRITICAL;
+
+ master->hw.init = &init;
+ master->regmap = regmap;
+ master->id = id;
+ master->chg_pid = chg_pid;
+ master->lock = lock;
+ master->mux_table = mux_table;
+
+ spin_lock_irqsave(master->lock, flags);
+ regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+ regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+ master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
+ master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT;
+ spin_unlock_irqrestore(master->lock, flags);
+
+ hw = &master->hw;
+ ret = clk_hw_register(NULL, &master->hw);
+ if (ret) {
+ kfree(master);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F,
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index c768947647..bd4b50b142 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -3,47 +3,47 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
+DEFINE_SPINLOCK(pmc_pcr_lock);
+
#define PERIPHERAL_ID_MIN 2
#define PERIPHERAL_ID_MAX 31
#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
-#define PERIPHERAL_RSHIFT_MASK 0x3
-#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
-
#define PERIPHERAL_MAX_SHIFT 3
struct clk_peripheral {
struct clk_hw hw;
struct regmap *regmap;
u32 id;
- const char *parent;
};
-#define to_clk_peripheral(_hw) container_of(_hw, struct clk_peripheral, hw)
+#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
struct clk_sam9x5_peripheral {
struct clk_hw hw;
struct regmap *regmap;
struct clk_range range;
+ spinlock_t *lock;
u32 id;
u32 div;
const struct clk_pcr_layout *layout;
+ struct at91_clk_pms pms;
bool auto_div;
- const char *parent;
+ int chg_pid;
};
-#define to_clk_sam9x5_peripheral(_hw) \
- container_of(_hw, struct clk_sam9x5_peripheral, hw)
+#define to_clk_sam9x5_peripheral(hw) \
+ container_of(hw, struct clk_sam9x5_peripheral, hw)
static int clk_peripheral_enable(struct clk_hw *hw)
{
@@ -95,42 +95,45 @@ static const struct clk_ops peripheral_ops = {
.is_enabled = clk_peripheral_is_enabled,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id)
{
- int ret;
struct clk_peripheral *periph;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
return ERR_PTR(-EINVAL);
- periph = xzalloc(sizeof(*periph));
+ periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+ if (!periph)
+ return ERR_PTR(-ENOMEM);
- periph->hw.clk.name = name;
- periph->hw.clk.ops = &peripheral_ops;
-
- if (parent_name) {
- periph->parent = parent_name;
- periph->hw.clk.parent_names = &periph->parent;
- periph->hw.clk.num_parents = 1;
- }
+ init.name = name;
+ init.ops = &peripheral_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
periph->id = id;
+ periph->hw.init = &init;
periph->regmap = regmap;
- ret = bclk_register(&periph->hw.clk);
+ hw = &periph->hw;
+ ret = clk_hw_register(NULL, &periph->hw);
if (ret) {
kfree(periph);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &periph->hw.clk;
+ return hw;
}
static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
{
- struct clk *parent;
+ struct clk_hw *parent;
unsigned long parent_rate;
int shift = 0;
@@ -138,8 +141,8 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
return;
if (periph->range.max) {
- parent = clk_get_parent(&periph->hw.clk);
- parent_rate = clk_get_rate(parent);
+ parent = clk_hw_get_parent_by_index(&periph->hw, 0);
+ parent_rate = clk_hw_get_rate(parent);
if (!parent_rate)
return;
@@ -153,52 +156,68 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
periph->div = shift;
}
-static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
+static int clk_sam9x5_peripheral_set(struct clk_sam9x5_peripheral *periph,
+ unsigned int status)
{
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
+ unsigned int enable = status ? AT91_PMC_PCR_EN : 0;
if (periph->id < PERIPHERAL_ID_MIN)
return 0;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
- regmap_write_bits(periph->regmap, periph->layout->offset,
- periph->layout->div_mask | periph->layout->cmd |
- AT91_PMC_PCR_EN,
- field_prep(periph->layout->div_mask, periph->div) |
- periph->layout->cmd |
- AT91_PMC_PCR_EN);
+ regmap_update_bits(periph->regmap, periph->layout->offset,
+ periph->layout->div_mask | periph->layout->cmd |
+ enable,
+ field_prep(periph->layout->div_mask, periph->div) |
+ periph->layout->cmd | enable);
+ spin_unlock_irqrestore(periph->lock, flags);
return 0;
}
+static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+
+ return clk_sam9x5_peripheral_set(periph, 1);
+}
+
static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
if (periph->id < PERIPHERAL_ID_MIN)
return;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
- regmap_write_bits(periph->regmap, periph->layout->offset,
- AT91_PMC_PCR_EN | periph->layout->cmd,
- periph->layout->cmd);
+ regmap_update_bits(periph->regmap, periph->layout->offset,
+ AT91_PMC_PCR_EN | periph->layout->cmd,
+ periph->layout->cmd);
+ spin_unlock_irqrestore(periph->lock, flags);
}
static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
unsigned int status;
if (periph->id < PERIPHERAL_ID_MIN)
return 1;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
regmap_read(periph->regmap, periph->layout->offset, &status);
+ spin_unlock_irqrestore(periph->lock, flags);
- return status & AT91_PMC_PCR_EN ? 1 : 0;
+ return !!(status & AT91_PMC_PCR_EN);
}
static unsigned long
@@ -206,14 +225,17 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
unsigned int status;
if (periph->id < PERIPHERAL_ID_MIN)
return parent_rate;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
regmap_read(periph->regmap, periph->layout->offset, &status);
+ spin_unlock_irqrestore(periph->lock, flags);
if (status & AT91_PMC_PCR_EN) {
periph->div = field_get(periph->layout->div_mask, status);
@@ -307,45 +329,64 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate,
};
-struct clk * __init
-at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+static const struct clk_ops sam9x5_peripheral_chg_ops = {
+ .enable = clk_sam9x5_peripheral_enable,
+ .disable = clk_sam9x5_peripheral_disable,
+ .is_enabled = clk_sam9x5_peripheral_is_enabled,
+ .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
+ .set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
+struct clk_hw * __init
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
- u32 id, const struct clk_range *range)
+ u32 id, const struct clk_range *range,
+ int chg_pid, unsigned long flags)
{
- int ret;
struct clk_sam9x5_peripheral *periph;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name)
return ERR_PTR(-EINVAL);
- periph = xzalloc(sizeof(*periph));
+ periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+ if (!periph)
+ return ERR_PTR(-ENOMEM);
- periph->hw.clk.name = name;
- periph->hw.clk.ops = &sam9x5_peripheral_ops;
-
- if (parent_name) {
- periph->parent = parent_name;
- periph->hw.clk.parent_names = &periph->parent;
- periph->hw.clk.num_parents = 1;
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = flags;
+ if (chg_pid < 0) {
+ init.ops = &sam9x5_peripheral_ops;
+ } else {
+ init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ init.ops = &sam9x5_peripheral_chg_ops;
}
periph->id = id;
+ periph->hw.init = &init;
periph->div = 0;
periph->regmap = regmap;
+ periph->lock = lock;
if (layout->div_mask)
periph->auto_div = true;
periph->layout = layout;
periph->range = *range;
+ periph->chg_pid = chg_pid;
- ret = bclk_register(&periph->hw.clk);
+ hw = &periph->hw;
+ ret = clk_hw_register(NULL, &periph->hw);
if (ret) {
kfree(periph);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
+ } else {
+ clk_sam9x5_peripheral_autodiv(periph);
}
- clk_sam9x5_peripheral_autodiv(periph);
- pmc_register_id(id);
-
- return &periph->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index d8ea566f49..027e1fc773 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -3,14 +3,12 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -31,7 +29,7 @@
#define PLL_OUT_SHIFT 14
#define PLL_MAX_ID 1
-#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+#define to_clk_pll(hw) container_of(hw, struct clk_pll, hw)
struct clk_pll {
struct clk_hw hw;
@@ -42,7 +40,7 @@ struct clk_pll {
u16 mul;
const struct clk_pll_layout *layout;
const struct clk_pll_characteristics *characteristics;
- const char *parent;
+ struct at91_clk_pms pms;
};
static inline bool clk_pll_ready(struct regmap *regmap, int id)
@@ -54,7 +52,7 @@ static inline bool clk_pll_ready(struct regmap *regmap, int id)
return status & PLL_STATUS_MASK(id) ? 1 : 0;
}
-static int clk_pll_enable(struct clk_hw *hw)
+static int clk_pll_prepare(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
struct regmap *regmap = pll->regmap;
@@ -83,33 +81,33 @@ static int clk_pll_enable(struct clk_hw *hw)
out = characteristics->out[pll->range];
if (characteristics->icpll)
- regmap_write_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
+ regmap_update_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
characteristics->icpll[pll->range] << PLL_ICPR_SHIFT(id));
- regmap_write_bits(regmap, offset, layout->pllr_mask,
- pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
- (out << PLL_OUT_SHIFT) |
- ((pll->mul & layout->mul_mask) << layout->mul_shift));
+ regmap_update_bits(regmap, offset, layout->pllr_mask,
+ pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
+ (out << PLL_OUT_SHIFT) |
+ ((pll->mul & layout->mul_mask) << layout->mul_shift));
while (!clk_pll_ready(regmap, pll->id))
- barrier();
+ cpu_relax();
return 0;
}
-static int clk_pll_is_enabled(struct clk_hw *hw)
+static int clk_pll_is_prepared(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
return clk_pll_ready(pll->regmap, pll->id);
}
-static void clk_pll_disable(struct clk_hw *hw)
+static void clk_pll_unprepare(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
unsigned int mask = pll->layout->pllr_mask;
- regmap_write_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask);
+ regmap_update_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask);
}
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
@@ -234,7 +232,7 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
}
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+ unsigned long *parent_rate)
{
struct clk_pll *pll = to_clk_pll(hw);
@@ -264,21 +262,23 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
}
static const struct clk_ops pll_ops = {
- .enable = clk_pll_enable,
- .disable = clk_pll_disable,
- .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_prepare,
+ .disable = clk_pll_unprepare,
+ .is_enabled = clk_pll_is_prepared,
.recalc_rate = clk_pll_recalc_rate,
.round_rate = clk_pll_round_rate,
.set_rate = clk_pll_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_layout *layout,
const struct clk_pll_characteristics *characteristics)
{
struct clk_pll *pll;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int offset = PLL_REG(id);
unsigned int pllr;
int ret;
@@ -286,17 +286,18 @@ at91_clk_register_pll(struct regmap *regmap, const char *name,
if (id > PLL_MAX_ID)
return ERR_PTR(-EINVAL);
- pll = xzalloc(sizeof(*pll));
-
- pll->parent = parent_name;
- pll->hw.clk.name = name;
- pll->hw.clk.ops = &pll_ops;
- pll->hw.clk.parent_names = &pll->parent;
- pll->hw.clk.num_parents = 1;
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
- /* init.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &pll_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE;
pll->id = id;
+ pll->hw.init = &init;
pll->layout = layout;
pll->characteristics = characteristics;
pll->regmap = regmap;
@@ -304,13 +305,14 @@ at91_clk_register_pll(struct regmap *regmap, const char *name,
pll->div = PLL_DIV(pllr);
pll->mul = PLL_MUL(pllr, layout);
- ret = bclk_register(&pll->hw.clk);
+ hw = &pll->hw;
+ ret = clk_hw_register(NULL, &pll->hw);
if (ret) {
kfree(pll);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &pll->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
index 2830b16722..7fe4411149 100644
--- a/drivers/clk/at91/clk-plldiv.c
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -3,23 +3,20 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define to_clk_plldiv(_hw) container_of(_hw, struct clk_plldiv, hw)
+#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
struct clk_plldiv {
struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
};
static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
@@ -37,7 +34,7 @@ static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
}
static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+ unsigned long *parent_rate)
{
unsigned long div;
@@ -61,8 +58,8 @@ static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
if ((parent_rate != rate) && (parent_rate / 2 != rate))
return -EINVAL;
- regmap_write_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
- parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
+ regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
+ parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
return 0;
}
@@ -73,33 +70,34 @@ static const struct clk_ops plldiv_ops = {
.set_rate = clk_plldiv_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name)
{
- int ret;
struct clk_plldiv *plldiv;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
- plldiv = xzalloc(sizeof(*plldiv));
-
- plldiv->hw.clk.name = name;
- plldiv->hw.clk.ops = &plldiv_ops;
-
- if (parent_name) {
- plldiv->parent = parent_name;
- plldiv->hw.clk.parent_names = &plldiv->parent;
- plldiv->hw.clk.num_parents = 1;
- }
+ plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
+ if (!plldiv)
+ return ERR_PTR(-ENOMEM);
- /* init.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &plldiv_ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = CLK_SET_RATE_GATE;
+ plldiv->hw.init = &init;
plldiv->regmap = regmap;
- ret = bclk_register(&plldiv->hw.clk);
+ hw = &plldiv->hw;
+ ret = clk_hw_register(NULL, &plldiv->hw);
if (ret) {
kfree(plldiv);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &plldiv->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index ec53f1addd..3bf13568f5 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -3,15 +3,12 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -24,12 +21,13 @@
struct clk_programmable {
struct clk_hw hw;
struct regmap *regmap;
+ u32 *mux_table;
u8 id;
const struct clk_programmable_layout *layout;
- const char *parent_names[];
+ struct at91_clk_pms pms;
};
-#define to_clk_programmable(_hw) container_of(_hw, struct clk_programmable, hw)
+#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -59,6 +57,9 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
if (layout->have_slck_mck)
mask |= AT91_PMC_CSSMCK_MCK;
+ if (prog->mux_table)
+ pckr = clk_mux_index_to_val(prog->mux_table, 0, index);
+
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
return -EINVAL;
@@ -66,7 +67,7 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
pckr |= AT91_PMC_CSSMCK_MCK;
}
- regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
+ regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
return 0;
}
@@ -85,6 +86,9 @@ static int clk_programmable_get_parent(struct clk_hw *hw)
if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
ret = PROG_MAX_RM9200_CSS + 1;
+ if (prog->mux_table)
+ ret = clk_mux_val_to_index(&prog->hw, prog->mux_table, 0, ret);
+
return ret;
}
@@ -114,9 +118,9 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
return -EINVAL;
}
- regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
- layout->pres_mask << layout->pres_shift,
- shift << layout->pres_shift);
+ regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
+ layout->pres_mask << layout->pres_shift,
+ shift << layout->pres_shift);
return 0;
}
@@ -128,43 +132,45 @@ static const struct clk_ops programmable_ops = {
.set_rate = clk_programmable_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
- const struct clk_programmable_layout *layout)
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table)
{
struct clk_programmable *prog;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (id > PROG_ID_MAX)
return ERR_PTR(-EINVAL);
- prog = kzalloc(struct_size(prog, parent_names, num_parents), GFP_KERNEL);
+ prog = kzalloc(sizeof(*prog), GFP_KERNEL);
if (!prog)
return ERR_PTR(-ENOMEM);
- prog->hw.clk.name = name;
- prog->hw.clk.ops = &programmable_ops;
- memcpy(prog->parent_names, parent_names,
- num_parents * sizeof(prog->parent_names[0]));
- prog->hw.clk.parent_names = &prog->parent_names[0];
- prog->hw.clk.num_parents = num_parents;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+ init.name = name;
+ init.ops = &programmable_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
prog->id = id;
prog->layout = layout;
+ prog->hw.init = &init;
prog->regmap = regmap;
+ prog->mux_table = mux_table;
- ret = bclk_register(&prog->hw.clk);
+ hw = &prog->hw;
+ ret = clk_hw_register(NULL, &prog->hw);
if (ret) {
kfree(prog);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- pmc_register_pck(id);
-
- return &prog->hw.clk;
+ return hw;
}
const struct clk_programmable_layout at91rm9200_programmable_layout = {
diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index e94b3eec41..c4f1606128 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -1,322 +1,657 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Microchip Technology Inc.
*
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-#include <linux/bitfield.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define PMC_PLL_CTRL0 0xc
-#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
-#define PMC_PLL_CTRL0_ENPLL BIT(28)
-#define PMC_PLL_CTRL0_ENPLLCK BIT(29)
-#define PMC_PLL_CTRL0_ENLOCK BIT(31)
-
-#define PMC_PLL_CTRL1 0x10
-#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
-#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24)
-
-#define PMC_PLL_ACR 0x18
-#define PMC_PLL_ACR_DEFAULT_UPLL 0x12020010UL
-#define PMC_PLL_ACR_DEFAULT_PLLA 0x00020010UL
-#define PMC_PLL_ACR_UTMIVR BIT(12)
-#define PMC_PLL_ACR_UTMIBG BIT(13)
-#define PMC_PLL_ACR_LOOP_FILTER_MSK GENMASK(31, 24)
-
-#define PMC_PLL_UPDT 0x1c
-#define PMC_PLL_UPDT_UPDATE BIT(8)
-
-#define PMC_PLL_ISR0 0xec
+#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
+#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
+#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
#define UPLL_DIV 2
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
-#define PLL_MAX_ID 1
+#define FCORE_MIN (600000000)
+#define FCORE_MAX (1200000000)
-struct sam9x60_pll {
- struct clk clk;
+#define PLL_MAX_ID 7
+
+struct sam9x60_pll_core {
struct regmap *regmap;
+ spinlock_t *lock;
const struct clk_pll_characteristics *characteristics;
- u32 frac;
+ const struct clk_pll_layout *layout;
+ struct clk_hw hw;
u8 id;
- u8 div;
+};
+
+struct sam9x60_frac {
+ struct sam9x60_pll_core core;
+ struct at91_clk_pms pms;
+ u32 frac;
u16 mul;
- const char *parent_name;
};
-#define to_sam9x60_pll(_hw) container_of(_hw->clk, struct sam9x60_pll, clk)
+struct sam9x60_div {
+ struct sam9x60_pll_core core;
+ struct at91_clk_pms pms;
+ u8 div;
+};
+
+#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
+#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
+#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
{
unsigned int status;
- regmap_read(regmap, PMC_PLL_ISR0, &status);
+ regmap_read(regmap, AT91_PMC_PLL_ISR0, &status);
return !!(status & BIT(id));
}
-static int sam9x60_pll_enable(struct clk_hw *hw)
+static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
- struct regmap *regmap = pll->regmap;
- u8 div;
- u16 mul;
- u32 val;
+ return sam9x60_pll_ready(regmap, id);
+}
- regmap_write(regmap, PMC_PLL_UPDT, pll->id);
+static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
- regmap_read(regmap, PMC_PLL_CTRL0, &val);
- div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
+ return parent_rate * (frac->mul + 1) +
+ DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22));
+}
- regmap_read(regmap, PMC_PLL_CTRL1, &val);
- mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
+static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
+{
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ struct regmap *regmap = core->regmap;
+ unsigned int val, cfrac, cmul;
+ unsigned long flags;
- if (sam9x60_pll_ready(regmap, pll->id) &&
- (div == pll->div && mul == pll->mul)) {
- return 0;
- }
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
+ cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
+
+ if (sam9x60_frac_pll_ready(regmap, core->id) &&
+ (cmul == frac->mul && cfrac == frac->frac))
+ goto unlock;
/* Recommended value for PMC_PLL_ACR */
- if (pll->characteristics->upll)
- val = PMC_PLL_ACR_DEFAULT_UPLL;
+ if (core->characteristics->upll)
+ val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
else
- val = PMC_PLL_ACR_DEFAULT_PLLA;
- regmap_write(regmap, PMC_PLL_ACR, val);
+ val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
+ regmap_write(regmap, AT91_PMC_PLL_ACR, val);
- regmap_write(regmap, PMC_PLL_CTRL1,
- FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
+ regmap_write(regmap, AT91_PMC_PLL_CTRL1,
+ (frac->mul << core->layout->mul_shift) |
+ (frac->frac << core->layout->frac_shift));
- if (pll->characteristics->upll) {
+ if (core->characteristics->upll) {
/* Enable the UTMI internal bandgap */
- val |= PMC_PLL_ACR_UTMIBG;
- regmap_write(regmap, PMC_PLL_ACR, val);
+ val |= AT91_PMC_PLL_ACR_UTMIBG;
+ regmap_write(regmap, AT91_PMC_PLL_ACR, val);
udelay(10);
/* Enable the UTMI internal regulator */
- val |= PMC_PLL_ACR_UTMIVR;
- regmap_write(regmap, PMC_PLL_ACR, val);
+ val |= AT91_PMC_PLL_ACR_UTMIVR;
+ regmap_write(regmap, AT91_PMC_PLL_ACR, val);
udelay(10);
}
- regmap_update_bits(regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
- regmap_write(regmap, PMC_PLL_CTRL0,
- PMC_PLL_CTRL0_ENLOCK | PMC_PLL_CTRL0_ENPLL |
- PMC_PLL_CTRL0_ENPLLCK | pll->div);
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
- regmap_update_bits(regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
- while (!sam9x60_pll_ready(regmap, pll->id))
+ while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
+unlock:
+ spin_unlock_irqrestore(core->lock, flags);
+
return 0;
}
-static int sam9x60_pll_is_enabled(struct clk_hw *hw)
+static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- return sam9x60_pll_ready(pll->regmap, pll->id);
+ return sam9x60_frac_pll_set(core);
}
-static void sam9x60_pll_disable(struct clk_hw *hw)
+static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
- regmap_write(pll->regmap, PMC_PLL_UPDT, pll->id);
+ spin_lock_irqsave(core->lock, flags);
- regmap_update_bits(pll->regmap, PMC_PLL_CTRL0,
- PMC_PLL_CTRL0_ENPLLCK, 0);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
- regmap_update_bits(pll->regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
- regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL, 0);
+ if (core->characteristics->upll)
+ regmap_update_bits(regmap, AT91_PMC_PLL_ACR,
+ AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0);
- if (pll->characteristics->upll)
- regmap_update_bits(pll->regmap, PMC_PLL_ACR,
- PMC_PLL_ACR_UTMIBG | PMC_PLL_ACR_UTMIVR, 0);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
- regmap_update_bits(pll->regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ spin_unlock_irqrestore(core->lock, flags);
}
-static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- return (parent_rate * (pll->mul + 1)) / (pll->div + 1);
+ return sam9x60_pll_ready(core->regmap, core->id);
}
-static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
- unsigned long rate,
- unsigned long parent_rate,
- bool update)
+static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
+ unsigned long rate,
+ unsigned long parent_rate,
+ bool update)
{
- const struct clk_pll_characteristics *characteristics =
- pll->characteristics;
- unsigned long bestremainder = ULONG_MAX;
- unsigned long maxdiv, mindiv, tmpdiv;
- long bestrate = -ERANGE;
- unsigned long bestdiv = 0;
- unsigned long bestmul = 0;
- unsigned long bestfrac = 0;
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ unsigned long tmprate, remainder;
+ unsigned long nmul = 0;
+ unsigned long nfrac = 0;
- if (rate < characteristics->output[0].min ||
- rate > characteristics->output[0].max)
+ if (rate < FCORE_MIN || rate > FCORE_MAX)
return -ERANGE;
- if (!pll->characteristics->upll) {
- mindiv = parent_rate / rate;
- if (mindiv < 2)
- mindiv = 2;
+ /*
+ * Calculate the multiplier associated with the current
+ * divider that provide the closest rate to the requested one.
+ */
+ nmul = mult_frac(rate, 1, parent_rate);
+ tmprate = mult_frac(parent_rate, nmul, 1);
+ remainder = rate - tmprate;
- maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
- if (maxdiv > PLL_DIV_MAX)
- maxdiv = PLL_DIV_MAX;
- } else {
- mindiv = maxdiv = UPLL_DIV;
+ if (remainder) {
+ nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
+ parent_rate);
+
+ tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
+ (1 << 22));
}
- for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
- unsigned long remainder;
- unsigned long tmprate;
- unsigned long tmpmul;
- unsigned long tmpfrac = 0;
+ /* Check if resulted rate is a valid. */
+ if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+ return -ERANGE;
- /*
- * Calculate the multiplier associated with the current
- * divider that provide the closest rate to the requested one.
- */
- tmpmul = mult_frac(rate, tmpdiv, parent_rate);
- tmprate = mult_frac(parent_rate, tmpmul, tmpdiv);
- remainder = rate - tmprate;
+ if (update) {
+ frac->mul = nmul - 1;
+ frac->frac = nfrac;
+ }
- if (remainder) {
- tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
- parent_rate);
+ return tmprate;
+}
- tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
- tmpdiv * (1 << 22));
+static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- if (tmprate > rate)
- remainder = tmprate - rate;
- else
- remainder = rate - tmprate;
- }
+ return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false);
+}
- /*
- * Compare the remainder with the best remainder found until
- * now and elect a new best multiplier/divider pair if the
- * current remainder is smaller than the best one.
- */
- if (remainder < bestremainder) {
- bestremainder = remainder;
- bestdiv = tmpdiv;
- bestmul = tmpmul;
- bestrate = tmprate;
- bestfrac = tmpfrac;
+static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
+}
+
+static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long irqflags;
+ unsigned int val, cfrac, cmul;
+ long ret;
+
+ ret = sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
+ if (ret <= 0)
+ return ret;
+
+ spin_lock_irqsave(core->lock, irqflags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
+ cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
+
+ if (cmul == frac->mul && cfrac == frac->frac)
+ goto unlock;
+
+ regmap_write(regmap, AT91_PMC_PLL_CTRL1,
+ (frac->mul << core->layout->mul_shift) |
+ (frac->frac << core->layout->frac_shift));
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+ AT91_PMC_PLL_CTRL0_ENLOCK |
+ AT91_PMC_PLL_CTRL0_ENPLL);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ while (!sam9x60_pll_ready(regmap, core->id))
+ cpu_relax();
+
+unlock:
+ spin_unlock_irqrestore(core->lock, irqflags);
+
+ return ret;
+}
+
+static const struct clk_ops sam9x60_frac_pll_ops = {
+ .enable = sam9x60_frac_pll_prepare,
+ .disable = sam9x60_frac_pll_unprepare,
+ .is_enabled = sam9x60_frac_pll_is_prepared,
+ .recalc_rate = sam9x60_frac_pll_recalc_rate,
+ .round_rate = sam9x60_frac_pll_round_rate,
+ .set_rate = sam9x60_frac_pll_set_rate,
+};
+
+static const struct clk_ops sam9x60_frac_pll_ops_chg = {
+ .enable = sam9x60_frac_pll_prepare,
+ .disable = sam9x60_frac_pll_unprepare,
+ .is_enabled = sam9x60_frac_pll_is_prepared,
+ .recalc_rate = sam9x60_frac_pll_recalc_rate,
+ .round_rate = sam9x60_frac_pll_round_rate,
+ .set_rate = sam9x60_frac_pll_set_rate_chg,
+};
+
+/* This function should be called with spinlock acquired. */
+static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
+ bool enable)
+{
+ struct regmap *regmap = core->regmap;
+ u32 ena_msk = enable ? core->layout->endiv_mask : 0;
+ u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ core->layout->div_mask | ena_msk,
+ (div << core->layout->div_shift) | ena_val);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ while (!sam9x60_pll_ready(regmap, core->id))
+ cpu_relax();
+}
+
+static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
+{
+ struct sam9x60_div *div = to_sam9x60_div(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+ unsigned int val, cdiv;
+
+ spin_lock_irqsave(core->lock, flags);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
+
+ /* Stop if enabled an nothing changed. */
+ if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
+ goto unlock;
+
+ sam9x60_div_pll_set_div(core, div->div, 1);
+
+unlock:
+ spin_unlock_irqrestore(core->lock, flags);
+
+ return 0;
+}
+
+static int sam9x60_div_pll_prepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_div_pll_set(core);
+}
+
+static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ core->layout->endiv_mask, 0);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ spin_unlock_irqrestore(core->lock, flags);
+}
+
+static int sam9x60_div_pll_is_prepared(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+
+ spin_unlock_irqrestore(core->lock, flags);
+
+ return !!(val & core->layout->endiv_mask);
+}
+
+static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1));
+}
+
+static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core,
+ unsigned long *parent_rate,
+ unsigned long rate)
+{
+ const struct clk_pll_characteristics *characteristics =
+ core->characteristics;
+ struct clk_hw *parent = clk_hw_get_parent(&core->hw);
+ unsigned long tmp_rate, tmp_parent_rate, tmp_diff;
+ long best_diff = -1, best_rate = -EINVAL;
+ u32 divid;
+
+ if (!rate)
+ return 0;
+
+ if (rate < characteristics->output[0].min ||
+ rate > characteristics->output[0].max)
+ return -ERANGE;
+
+ for (divid = 1; divid < core->layout->div_mask; divid++) {
+ tmp_parent_rate = clk_hw_round_rate(parent, rate * divid);
+ if (!tmp_parent_rate)
+ continue;
+
+ tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid);
+ tmp_diff = abs(rate - tmp_rate);
+
+ if (best_diff < 0 || best_diff > tmp_diff) {
+ *parent_rate = tmp_parent_rate;
+ best_rate = tmp_rate;
+ best_diff = tmp_diff;
}
- /* We've found a perfect match! */
- if (!remainder)
+ if (!best_diff)
break;
}
- /* Check if bestrate is a valid output rate */
- if (bestrate < characteristics->output[0].min &&
- bestrate > characteristics->output[0].max)
+ if (best_rate < characteristics->output[0].min ||
+ best_rate > characteristics->output[0].max)
return -ERANGE;
- if (update) {
- pll->div = bestdiv - 1;
- pll->mul = bestmul - 1;
- pll->frac = bestfrac;
- }
+ return best_rate;
+}
- return bestrate;
+static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_div_pll_compute_div(core, parent_rate, rate);
}
-static long sam9x60_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+
+ div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
- return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false);
+ return 0;
}
-static int sam9x60_pll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long irqflags;
+ unsigned int val, cdiv;
+
+ div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
- return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true);
+ spin_lock_irqsave(core->lock, irqflags);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
+
+ /* Stop if nothing changed. */
+ if (cdiv == div->div)
+ goto unlock;
+
+ sam9x60_div_pll_set_div(core, div->div, 0);
+
+unlock:
+ spin_unlock_irqrestore(core->lock, irqflags);
+
+ return 0;
}
-static const struct clk_ops pll_ops = {
- .enable = sam9x60_pll_enable,
- .disable = sam9x60_pll_disable,
- .is_enabled = sam9x60_pll_is_enabled,
- .recalc_rate = sam9x60_pll_recalc_rate,
- .round_rate = sam9x60_pll_round_rate,
- .set_rate = sam9x60_pll_set_rate,
+static const struct clk_ops sam9x60_div_pll_ops = {
+ .enable = sam9x60_div_pll_prepare,
+ .disable = sam9x60_div_pll_unprepare,
+ .is_enabled = sam9x60_div_pll_is_prepared,
+ .recalc_rate = sam9x60_div_pll_recalc_rate,
+ .round_rate = sam9x60_div_pll_round_rate,
+ .set_rate = sam9x60_div_pll_set_rate,
};
-struct clk * __init
-sam9x60_clk_register_pll(struct regmap *regmap,
- const char *name, const char *parent_name, u8 id,
- const struct clk_pll_characteristics *characteristics)
+static const struct clk_ops sam9x60_div_pll_ops_chg = {
+ .enable = sam9x60_div_pll_prepare,
+ .disable = sam9x60_div_pll_unprepare,
+ .is_enabled = sam9x60_div_pll_is_prepared,
+ .recalc_rate = sam9x60_div_pll_recalc_rate,
+ .round_rate = sam9x60_div_pll_round_rate,
+ .set_rate = sam9x60_div_pll_set_rate_chg,
+};
+
+struct clk_hw * __init
+sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags)
{
- struct sam9x60_pll *pll;
- unsigned int pllr;
+ struct sam9x60_frac *frac;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long parent_rate, irqflags;
+ unsigned int val;
int ret;
- if (id > PLL_MAX_ID)
+ if (id > PLL_MAX_ID || !lock || !parent_hw)
return ERR_PTR(-EINVAL);
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll)
+ frac = kzalloc(sizeof(*frac), GFP_KERNEL);
+ if (!frac)
return ERR_PTR(-ENOMEM);
- pll->clk.name = name;
- pll->clk.ops = &pll_ops;
- pll->parent_name = parent_name;
- pll->clk.parent_names = &pll->parent_name;
- pll->clk.num_parents = 1;
- /* pll->clk.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ if (flags & CLK_SET_RATE_GATE)
+ init.ops = &sam9x60_frac_pll_ops;
+ else
+ init.ops = &sam9x60_frac_pll_ops_chg;
+
+ init.flags = flags;
+
+ frac->core.id = id;
+ frac->core.hw.init = &init;
+ frac->core.characteristics = characteristics;
+ frac->core.layout = layout;
+ frac->core.regmap = regmap;
+ frac->core.lock = lock;
+
+ spin_lock_irqsave(frac->core.lock, irqflags);
+ if (sam9x60_pll_ready(regmap, id)) {
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
+ frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
+ } else {
+ /*
+ * This means the PLL is not setup by bootloaders. In this
+ * case we need to set the minimum rate for it. Otherwise
+ * a clock child of this PLL may be enabled before setting
+ * its rate leading to enabling this PLL with unsupported
+ * rate. This will lead to PLL not being locked at all.
+ */
+ parent_rate = clk_hw_get_rate(parent_hw);
+ if (!parent_rate) {
+ hw = ERR_PTR(-EINVAL);
+ goto free;
+ }
+
+ ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN,
+ parent_rate, true);
+ if (ret < 0) {
+ hw = ERR_PTR(ret);
+ goto free;
+ }
+ }
+ spin_unlock_irqrestore(frac->core.lock, irqflags);
+
+ hw = &frac->core.hw;
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ kfree(frac);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+
+free:
+ spin_unlock_irqrestore(frac->core.lock, irqflags);
+ kfree(frac);
+ return hw;
+}
+
+struct clk_hw * __init
+sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags)
+{
+ struct sam9x60_div *div;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long irqflags;
+ unsigned int val;
+ int ret;
+
+ /* We only support one changeable PLL. */
+ if (id > PLL_MAX_ID || !lock)
+ return ERR_PTR(-EINVAL);
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ 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;
+ init.flags = flags;
+
+ div->core.id = id;
+ div->core.hw.init = &init;
+ div->core.characteristics = characteristics;
+ div->core.layout = layout;
+ div->core.regmap = regmap;
+ div->core.lock = lock;
+
+ spin_lock_irqsave(div->core.lock, irqflags);
- pll->id = id;
- pll->characteristics = characteristics;
- pll->regmap = regmap;
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
- regmap_write(regmap, PMC_PLL_UPDT, id);
- regmap_read(regmap, PMC_PLL_CTRL0, &pllr);
- pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr);
- regmap_read(regmap, PMC_PLL_CTRL1, &pllr);
- pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr);
+ spin_unlock_irqrestore(div->core.lock, irqflags);
- ret = bclk_register(&pll->clk);
+ hw = &div->core.hw;
+ ret = clk_hw_register(NULL, hw);
if (ret) {
- kfree(pll);
- return ERR_PTR(ret);
+ kfree(div);
+ hw = ERR_PTR(ret);
}
- return &pll->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
index bc4285e4bf..3a070d0d34 100644
--- a/drivers/clk/at91/clk-slow.c
+++ b/drivers/clk/at91/clk-slow.c
@@ -5,25 +5,21 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
struct clk_sam9260_slow {
struct clk_hw hw;
struct regmap *regmap;
- const char *parent_names[];
};
-#define to_clk_sam9260_slow(_hw) container_of(_hw, struct clk_sam9260_slow, hw)
+#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw)
static int clk_sam9260_slow_get_parent(struct clk_hw *hw)
{
@@ -39,13 +35,15 @@ static const struct clk_ops sam9260_slow_ops = {
.get_parent = clk_sam9260_slow_get_parent,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents)
{
struct clk_sam9260_slow *slowck;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!name)
@@ -54,20 +52,25 @@ at91_clk_register_sam9260_slow(struct regmap *regmap,
if (!parent_names || !num_parents)
return ERR_PTR(-EINVAL);
- slowck = xzalloc(struct_size(slowck, parent_names, num_parents));
- slowck->hw.clk.name = name;
- slowck->hw.clk.ops = &sam9260_slow_ops;
- memcpy(slowck->parent_names, parent_names,
- num_parents * sizeof(slowck->parent_names[0]));
- slowck->hw.clk.parent_names = slowck->parent_names;
- slowck->hw.clk.num_parents = num_parents;
+ slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
+ if (!slowck)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sam9260_slow_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = 0;
+
+ slowck->hw.init = &init;
slowck->regmap = regmap;
- ret = bclk_register(&slowck->hw.clk);
+ hw = &slowck->hw;
+ ret = clk_hw_register(NULL, &slowck->hw);
if (ret) {
kfree(slowck);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &slowck->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
index 6df698637c..dc1b150750 100644
--- a/drivers/clk/at91/clk-smd.c
+++ b/drivers/clk/at91/clk-smd.c
@@ -3,31 +3,25 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define SMD_SOURCE_MAX 2
-
#define SMD_DIV_SHIFT 8
#define SMD_MAX_DIV 0xf
struct at91sam9x5_clk_smd {
struct clk_hw hw;
struct regmap *regmap;
- const char *parent_names[];
};
-#define to_at91sam9x5_clk_smd(_hw) \
- container_of(_hw, struct at91sam9x5_clk_smd, hw)
+#define to_at91sam9x5_clk_smd(hw) \
+ container_of(hw, struct at91sam9x5_clk_smd, hw)
static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -71,8 +65,8 @@ static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
if (index > 1)
return -EINVAL;
- regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
- index ? AT91_PMC_SMDS : 0);
+ regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
+ index ? AT91_PMC_SMDS : 0);
return 0;
}
@@ -96,8 +90,8 @@ static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
return -EINVAL;
- regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
- (div - 1) << SMD_DIV_SHIFT);
+ regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
+ (div - 1) << SMD_DIV_SHIFT);
return 0;
}
@@ -110,28 +104,34 @@ static const struct clk_ops at91sam9x5_smd_ops = {
.set_rate = at91sam9x5_clk_smd_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
struct at91sam9x5_clk_smd *smd;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- smd = xzalloc(struct_size(smd, parent_names, num_parents));
- smd->hw.clk.name = name;
- smd->hw.clk.ops = &at91sam9x5_smd_ops;
- memcpy(smd->parent_names, parent_names,
- num_parents * sizeof(smd->parent_names[0]));
- smd->hw.clk.parent_names = smd->parent_names;
- smd->hw.clk.num_parents = num_parents;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+ smd = kzalloc(sizeof(*smd), GFP_KERNEL);
+ if (!smd)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91sam9x5_smd_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ smd->hw.init = &init;
smd->regmap = regmap;
- ret = bclk_register(&smd->hw.clk);
+ hw = &smd->hw;
+ ret = clk_hw_register(NULL, &smd->hw);
if (ret) {
kfree(smd);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &smd->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 9a15d5b04a..5f367e292a 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -2,14 +2,13 @@
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -17,12 +16,12 @@
#define SYSTEM_MAX_NAME_SZ 32
-#define to_clk_system(_hw) container_of(_hw, struct clk_system, hw)
+#define to_clk_system(hw) container_of(hw, struct clk_system, hw)
struct clk_system {
struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
u8 id;
- const char *parent_name;
};
static inline int is_pck(int id)
@@ -36,10 +35,10 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & (1 << id) ? 1 : 0;
+ return !!(status & (1 << id));
}
-static int clk_system_enable(struct clk_hw *hw)
+static int clk_system_prepare(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
@@ -49,19 +48,19 @@ static int clk_system_enable(struct clk_hw *hw)
return 0;
while (!clk_system_ready(sys->regmap, sys->id))
- barrier();
+ cpu_relax();
return 0;
}
-static void clk_system_disable(struct clk_hw *hw)
+static void clk_system_unprepare(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
}
-static int clk_system_is_enabled(struct clk_hw *hw)
+static int clk_system_is_prepared(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
unsigned int status;
@@ -76,40 +75,47 @@ static int clk_system_is_enabled(struct clk_hw *hw)
regmap_read(sys->regmap, AT91_PMC_SR, &status);
- return status & (1 << sys->id) ? 1 : 0;
+ return !!(status & (1 << sys->id));
}
static const struct clk_ops system_ops = {
- .enable = clk_system_enable,
- .disable = clk_system_disable,
- .is_enabled = clk_system_is_enabled,
+ .enable = clk_system_prepare,
+ .disable = clk_system_unprepare,
+ .is_enabled = clk_system_is_prepared,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
- const char *parent_name, u8 id)
+ const char *parent_name, u8 id, unsigned long flags)
{
struct clk_system *sys;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!parent_name || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
- sys = xzalloc(sizeof(*sys));
- sys->hw.clk.name = name;
- sys->hw.clk.ops = &system_ops;
- sys->parent_name = parent_name;
- sys->hw.clk.parent_names = &sys->parent_name;
- sys->hw.clk.num_parents = 1;
- /* init.flags = CLK_SET_RATE_PARENT; */
+ sys = kzalloc(sizeof(*sys), GFP_KERNEL);
+ if (!sys)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &system_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT | flags;
+
sys->id = id;
+ sys->hw.init = &init;
sys->regmap = regmap;
- ret = bclk_register(&sys->hw.clk);
+ hw = &sys->hw;
+ ret = clk_hw_register(NULL, &sys->hw);
if (ret) {
kfree(sys);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &sys->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 148befc8ac..4473dc7c34 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -3,20 +3,15 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define USB_SOURCE_MAX 2
-
#define SAM9X5_USB_DIV_SHIFT 8
#define SAM9X5_USB_MAX_DIV 0xf
@@ -29,23 +24,22 @@
struct at91sam9x5_clk_usb {
struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
u32 usbs_mask;
u8 num_parents;
- const char *parent_names[];
};
-#define to_at91sam9x5_clk_usb(_hw) \
- container_of(_hw, struct at91sam9x5_clk_usb, hw)
+#define to_at91sam9x5_clk_usb(hw) \
+ container_of(hw, struct at91sam9x5_clk_usb, hw)
struct at91rm9200_clk_usb {
struct clk_hw hw;
struct regmap *regmap;
u32 divisors[4];
- const char *parent_name;
};
-#define to_at91rm9200_clk_usb(_hw) \
- container_of(_hw, struct at91rm9200_clk_usb, hw)
+#define to_at91rm9200_clk_usb(hw) \
+ container_of(hw, struct at91rm9200_clk_usb, hw)
static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -67,7 +61,7 @@ static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
if (index >= usb->num_parents)
return -EINVAL;
- regmap_write_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
return 0;
}
@@ -95,8 +89,8 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
return -EINVAL;
- regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
- (div - 1) << SAM9X5_USB_DIV_SHIFT);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
+ (div - 1) << SAM9X5_USB_DIV_SHIFT);
return 0;
}
@@ -112,8 +106,8 @@ static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
{
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
- regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
- AT91_PMC_USBS);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
+ AT91_PMC_USBS);
return 0;
}
@@ -122,7 +116,7 @@ static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
{
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
- regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
}
static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
@@ -143,38 +137,43 @@ static const struct clk_ops at91sam9n12_usb_ops = {
.set_rate = at91sam9x5_clk_usb_set_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents,
u32 usbs_mask)
{
struct at91sam9x5_clk_usb *usb;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- usb = kzalloc(struct_size(usb, parent_names, num_parents), GFP_KERNEL);
- usb->hw.clk.name = name;
- usb->hw.clk.ops = &at91sam9x5_usb_ops;
- memcpy(usb->parent_names, parent_names,
- num_parents * sizeof(usb->parent_names[0]));
- usb->hw.clk.parent_names = usb->parent_names;
- usb->hw.clk.num_parents = num_parents;
- usb->hw.clk.flags = CLK_SET_RATE_PARENT;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */
- /* CLK_SET_RATE_PARENT; */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91sam9x5_usb_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+
+ usb->hw.init = &init;
usb->regmap = regmap;
usb->usbs_mask = usbs_mask;
usb->num_parents = num_parents;
- ret = bclk_register(&usb->hw.clk);
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
if (ret) {
kfree(usb);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &usb->hw.clk;
+ return hw;
}
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
@@ -182,7 +181,7 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
num_parents, SAM9X5_USBS_MASK);
}
-struct clk * __init
+struct clk_hw * __init
sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
@@ -190,29 +189,36 @@ sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
num_parents, SAM9X60_USBS_MASK);
}
-struct clk * __init
+struct clk_hw * __init
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct at91sam9x5_clk_usb *usb;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- usb = xzalloc(sizeof(*usb));
- usb->hw.clk.name = name;
- usb->hw.clk.ops = &at91sam9n12_usb_ops;
- usb->parent_names[0] = parent_name;
- usb->hw.clk.parent_names = &usb->parent_names[0];
- usb->hw.clk.num_parents = 1;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91sam9n12_usb_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
+
+ usb->hw.init = &init;
usb->regmap = regmap;
- ret = bclk_register(&usb->hw.clk);
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
if (ret) {
kfree(usb);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &usb->hw.clk;
+ return hw;
}
static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
@@ -235,7 +241,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
- struct clk *parent = clk_get_parent(clk_hw_to_clk(hw));
+ struct clk_hw *parent = clk_hw_get_parent(hw);
unsigned long bestrate = 0;
int bestdiff = -1;
unsigned long tmprate;
@@ -249,10 +255,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
continue;
tmp_parent_rate = rate * usb->divisors[i];
- tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate);
- if (!tmp_parent_rate)
- continue;
-
+ tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate);
tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
if (tmprate < rate)
tmpdiff = rate - tmprate;
@@ -286,9 +289,9 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
if (usb->divisors[i] == div) {
- regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR,
- AT91_PMC_USBDIV,
- i << RM9200_USB_DIV_SHIFT);
+ regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR,
+ AT91_PMC_USBDIV,
+ i << RM9200_USB_DIV_SHIFT);
return 0;
}
@@ -303,29 +306,35 @@ static const struct clk_ops at91rm9200_usb_ops = {
.set_rate = at91rm9200_clk_usb_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name, const u32 *divisors)
{
struct at91rm9200_clk_usb *usb;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- usb = xzalloc(sizeof(*usb));
- usb->hw.clk.name = name;
- usb->hw.clk.ops = &at91rm9200_usb_ops;
- usb->parent_name = parent_name;
- usb->hw.clk.parent_names = &usb->parent_name;
- usb->hw.clk.num_parents = 1;
- /* init.flags = CLK_SET_RATE_PARENT; */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91rm9200_usb_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+ usb->hw.init = &init;
usb->regmap = regmap;
memcpy(usb->divisors, divisors, sizeof(usb->divisors));
- ret = bclk_register(&usb->hw.clk);
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
if (ret) {
kfree(usb);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &usb->hw.clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index 1389983bde..7d85e43024 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -3,15 +3,14 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-
+#include <linux/regmap.h>
#include <soc/at91/atmel-sfr.h>
+#include <linux/printk.h>
#include "pmc.h"
@@ -19,16 +18,16 @@
* The purpose of this clock is to generate a 480 MHz signal. A different
* rate can't be configured.
*/
-#define UTMI_RATE 480000000
+#define UTMI_RATE 480000000
struct clk_utmi {
struct clk_hw hw;
- const char *parent;
struct regmap *regmap_pmc;
struct regmap *regmap_sfr;
+ struct at91_clk_pms pms;
};
-#define to_clk_utmi(_hw) container_of(_hw, struct clk_utmi, hw)
+#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
static inline bool clk_utmi_ready(struct regmap *regmap)
{
@@ -39,9 +38,9 @@ static inline bool clk_utmi_ready(struct regmap *regmap)
return status & AT91_PMC_LOCKU;
}
-static int clk_utmi_enable(struct clk_hw *hw)
+static int clk_utmi_prepare(struct clk_hw *hw)
{
- struct clk *hw_parent;
+ struct clk_hw *hw_parent;
struct clk_utmi *utmi = to_clk_utmi(hw);
unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN;
@@ -53,8 +52,8 @@ static int clk_utmi_enable(struct clk_hw *hw)
* FREQ field of the SFR_UTMICKTRIM register to generate properly
* the utmi clock.
*/
- hw_parent = clk_get_parent(clk_hw_to_clk(hw));
- parent_rate = clk_get_rate(hw_parent);
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
switch (parent_rate) {
case 12000000:
@@ -78,80 +77,173 @@ static int clk_utmi_enable(struct clk_hw *hw)
return -EINVAL;
}
-
if (utmi->regmap_sfr) {
- regmap_write_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
- AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
+ regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
+ AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
} else if (utmi_ref_clk_freq) {
pr_err("UTMICK: sfr node required\n");
return -EINVAL;
}
- regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
+ regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
while (!clk_utmi_ready(utmi->regmap_pmc))
- barrier();
+ cpu_relax();
return 0;
}
-static int clk_utmi_is_enabled(struct clk_hw *hw)
+static int clk_utmi_is_prepared(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
return clk_utmi_ready(utmi->regmap_pmc);
}
-static void clk_utmi_disable(struct clk_hw *hw)
+static void clk_utmi_unprepare(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
- regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
- AT91_PMC_UPLLEN, 0);
+ regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
+ AT91_PMC_UPLLEN, 0);
}
static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- /* UTMI clk rate is fixed */
+ /* UTMI clk rate is fixed. */
return UTMI_RATE;
}
static const struct clk_ops utmi_ops = {
- .enable = clk_utmi_enable,
- .disable = clk_utmi_disable,
- .is_enabled = clk_utmi_is_enabled,
+ .enable = clk_utmi_prepare,
+ .disable = clk_utmi_unprepare,
+ .is_enabled = clk_utmi_is_prepared,
.recalc_rate = clk_utmi_recalc_rate,
};
-struct clk * __init
-at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
- const char *name, const char *parent_name)
+static struct clk_hw * __init
+at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
+ struct regmap *regmap_sfr,
+ const char *name, const char *parent_name,
+ const struct clk_ops *ops, unsigned long flags)
{
- int ret;
struct clk_utmi *utmi;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
- utmi = xzalloc(sizeof(*utmi));
-
- utmi->hw.clk.name = name;
- utmi->hw.clk.ops = &utmi_ops;
+ utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+ if (!utmi)
+ return ERR_PTR(-ENOMEM);
- if (parent_name) {
- utmi->parent = parent_name;
- utmi->hw.clk.parent_names = &utmi->parent;
- utmi->hw.clk.num_parents = 1;
- }
-
- /* utmi->clk.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = flags;
+ utmi->hw.init = &init;
utmi->regmap_pmc = regmap_pmc;
utmi->regmap_sfr = regmap_sfr;
- ret = bclk_register(&utmi->hw.clk);
+ hw = &utmi->hw;
+ ret = clk_hw_register(NULL, &utmi->hw);
if (ret) {
kfree(utmi);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &utmi->hw.clk;
+ return hw;
+}
+
+struct clk_hw * __init
+at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
+ const char *name, const char *parent_name)
+{
+ return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
+ parent_name, &utmi_ops, CLK_SET_RATE_GATE);
+}
+
+static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
+{
+ struct clk_utmi *utmi = to_clk_utmi(hw);
+ struct clk_hw *hw_parent;
+ unsigned long parent_rate;
+ unsigned int val;
+
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
+
+ switch (parent_rate) {
+ case 16000000:
+ val = 0;
+ break;
+ case 20000000:
+ val = 2;
+ break;
+ case 24000000:
+ val = 3;
+ break;
+ case 32000000:
+ val = 5;
+ break;
+ default:
+ pr_err("UTMICK: unsupported main_xtal rate\n");
+ return -EINVAL;
+ }
+
+ regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
+
+ return 0;
+
+}
+
+static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
+{
+ struct clk_utmi *utmi = to_clk_utmi(hw);
+ struct clk_hw *hw_parent;
+ unsigned long parent_rate;
+ unsigned int val;
+
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
+
+ regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
+ switch (val & 0x7) {
+ case 0:
+ if (parent_rate == 16000000)
+ return 1;
+ break;
+ case 2:
+ if (parent_rate == 20000000)
+ return 1;
+ break;
+ case 3:
+ if (parent_rate == 24000000)
+ return 1;
+ break;
+ case 5:
+ if (parent_rate == 32000000)
+ return 1;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct clk_ops sama7g5_utmi_ops = {
+ .enable = clk_utmi_sama7g5_prepare,
+ .is_enabled = clk_utmi_sama7g5_is_prepared,
+ .recalc_rate = clk_utmi_recalc_rate,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
+ const char *parent_name)
+{
+ return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
+ parent_name, &sama7g5_utmi_ops, 0);
}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index f260d08c5d..4780b5790d 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -3,15 +3,14 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <module.h>
-#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/clkdev.h>
-#include <linux/overflow.h>
+#include <linux/clk/at91_pmc.h>
#include <of.h>
+#include <of_address.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-
-#include <dt-bindings/clock/at91.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -41,7 +40,7 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
}
EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
-struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
+struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
{
unsigned int type = clkspec->args[0];
unsigned int idx = clkspec->args[1];
@@ -106,172 +105,3 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
return pmc_data;
}
-
-#ifdef CONFIG_PM
-static struct regmap *pmcreg;
-
-static u8 registered_ids[PMC_MAX_IDS];
-static u8 registered_pcks[PMC_MAX_PCKS];
-
-static struct
-{
- u32 scsr;
- u32 pcsr0;
- u32 uckr;
- u32 mor;
- u32 mcfr;
- u32 pllar;
- u32 mckr;
- u32 usb;
- u32 imr;
- u32 pcsr1;
- u32 pcr[PMC_MAX_IDS];
- u32 audio_pll0;
- u32 audio_pll1;
- u32 pckr[PMC_MAX_PCKS];
-} pmc_cache;
-
-/*
- * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
- * without alteration in the table, and 0 is for unused clocks.
- */
-void pmc_register_id(u8 id)
-{
- int i;
-
- for (i = 0; i < PMC_MAX_IDS; i++) {
- if (registered_ids[i] == 0) {
- registered_ids[i] = id;
- break;
- }
- if (registered_ids[i] == id)
- break;
- }
-}
-
-/*
- * As Programmable Clock 0 is valid on AT91 chips, there is an offset
- * of 1 between the stored value and the real clock ID.
- */
-void pmc_register_pck(u8 pck)
-{
- int i;
-
- for (i = 0; i < PMC_MAX_PCKS; i++) {
- if (registered_pcks[i] == 0) {
- registered_pcks[i] = pck + 1;
- break;
- }
- if (registered_pcks[i] == (pck + 1))
- break;
- }
-}
-
-static int pmc_suspend(void)
-{
- int i;
- u8 num;
-
- regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
- regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
- regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr);
- regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor);
- regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr);
- regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar);
- regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr);
- regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb);
- regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr);
- regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1);
-
- for (i = 0; registered_ids[i]; i++) {
- regmap_write(pmcreg, AT91_PMC_PCR,
- (registered_ids[i] & AT91_PMC_PCR_PID_MASK));
- regmap_read(pmcreg, AT91_PMC_PCR,
- &pmc_cache.pcr[registered_ids[i]]);
- }
- for (i = 0; registered_pcks[i]; i++) {
- num = registered_pcks[i] - 1;
- regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]);
- }
-
- return 0;
-}
-
-static bool pmc_ready(unsigned int mask)
-{
- unsigned int status;
-
- regmap_read(pmcreg, AT91_PMC_SR, &status);
-
- return ((status & mask) == mask) ? 1 : 0;
-}
-
-static void pmc_resume(void)
-{
- int i;
- u8 num;
- u32 tmp;
- u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
-
- regmap_read(pmcreg, AT91_PMC_MCKR, &tmp);
- if (pmc_cache.mckr != tmp)
- pr_warn("MCKR was not configured properly by the firmware\n");
- regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp);
- if (pmc_cache.pllar != tmp)
- pr_warn("PLLAR was not configured properly by the firmware\n");
-
- regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
- regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
- regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
- regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
- regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr);
- regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb);
- regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr);
- regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1);
-
- for (i = 0; registered_ids[i]; i++) {
- regmap_write(pmcreg, AT91_PMC_PCR,
- pmc_cache.pcr[registered_ids[i]] |
- AT91_PMC_PCR_CMD);
- }
- for (i = 0; registered_pcks[i]; i++) {
- num = registered_pcks[i] - 1;
- regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
- }
-
- if (pmc_cache.uckr & AT91_PMC_UPLLEN)
- mask |= AT91_PMC_LOCKU;
-
- while (!pmc_ready(mask))
- cpu_relax();
-}
-
-static struct syscore_ops pmc_syscore_ops = {
- .suspend = pmc_suspend,
- .resume = pmc_resume,
-};
-
-static const struct of_device_id sama5d2_pmc_dt_ids[] = {
- { .compatible = "atmel,sama5d2-pmc" },
- { /* sentinel */ }
-};
-
-static int __init pmc_register_ops(void)
-{
- struct device_node *np;
-
- np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
- if (!np)
- return -ENODEV;
-
- pmcreg = device_node_to_regmap(np);
- if (IS_ERR(pmcreg))
- return PTR_ERR(pmcreg);
-
- register_syscore_ops(&pmc_syscore_ops);
-
- return 0;
-}
-/* This has to happen before arch_initcall because of the tcb_clksrc driver */
-postcore_initcall(pmc_register_ops);
-#endif
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index f9b2324f6a..6c8801a0f9 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -8,23 +8,29 @@
#ifndef __PMC_H_
#define __PMC_H_
-#include <io.h>
-#include <linux/bitops.h>
-#include <linux/printk.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <of.h>
+#include <linux/barebox-wrapper.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/at91.h>
+
+extern spinlock_t pmc_pcr_lock;
struct pmc_data {
unsigned int ncore;
- struct clk **chws;
+ struct clk_hw **chws;
unsigned int nsystem;
- struct clk **shws;
+ struct clk_hw **shws;
unsigned int nperiph;
- struct clk **phws;
+ struct clk_hw **phws;
unsigned int ngck;
- struct clk **ghws;
+ struct clk_hw **ghws;
unsigned int npck;
- struct clk **pchws;
+ struct clk_hw **pchws;
- struct clk *hwtable[];
+ struct clk_hw *hwtable[];
};
struct clk_range {
@@ -45,14 +51,20 @@ extern const struct clk_master_layout at91sam9x5_master_layout;
struct clk_master_characteristics {
struct clk_range output;
- u32 divisors[4];
+ u32 divisors[5];
u8 have_div3_pres;
};
struct clk_pll_layout {
u32 pllr_mask;
- u16 mul_mask;
+ u32 mul_mask;
+ u32 frac_mask;
+ u32 div_mask;
+ u32 endiv_mask;
u8 mul_shift;
+ u8 frac_shift;
+ u8 div_shift;
+ u8 endiv_shift;
};
extern const struct clk_pll_layout at91rm9200_pll_layout;
@@ -89,6 +101,20 @@ struct clk_pcr_layout {
u32 pid_mask;
};
+/**
+ * struct at91_clk_pms - Power management state for AT91 clock
+ * @rate: clock rate
+ * @parent_rate: clock parent rate
+ * @status: clock status (enabled or disabled)
+ * @parent: clock parent index
+ */
+struct at91_clk_pms {
+ unsigned long rate;
+ unsigned long parent_rate;
+ unsigned int status;
+ unsigned int parent;
+};
+
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
@@ -101,122 +127,142 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
int of_at91_get_clk_range(struct device_node *np, const char *propname,
struct clk_range *range);
-struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data);
+struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
-at91_clk_register_generated(struct regmap *regmap,
+struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id, bool pll_audio,
- const struct clk_range *range);
+ u32 *mux_table, u8 num_parents, u8 id,
+ const struct clk_range *range, int chg_pid);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
const char * const *parent_names,
unsigned int num_parents, u8 bus_id);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name,
u32 frequency, u32 accuracy);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap, const char *name,
const char *parent_name, bool bypass);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name,
const char **parent_names, int num_parents);
-struct clk * __init
-at91_clk_register_master(struct regmap *regmap, const char *name,
- int num_parents, const char **parent_names,
- const struct clk_master_layout *layout,
- const struct clk_master_characteristics *characteristics);
-
-struct clk * __init
+struct clk_hw * __init
+at91_clk_register_master_pres(struct regmap *regmap, const char *name,
+ int num_parents, const char **parent_names,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics,
+ spinlock_t *lock);
+
+struct clk_hw * __init
+at91_clk_register_master_div(struct regmap *regmap, const char *name,
+ const char *parent_names,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics,
+ spinlock_t *lock, u32 flags);
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_master(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names, u32 *mux_table,
+ spinlock_t *lock, u8 id, bool critical,
+ int chg_pid);
+
+struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id);
-struct clk * __init
-at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+struct clk_hw * __init
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
- u32 id, const struct clk_range *range);
+ u32 id, const struct clk_range *range,
+ int chg_pid, unsigned long flags);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_layout *layout,
const struct clk_pll_characteristics *characteristics);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
-sam9x60_clk_register_pll(struct regmap *regmap,
- const char *name, const char *parent_name, u8 id,
- const struct clk_pll_characteristics *characteristics);
+struct clk_hw * __init
+sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags);
-struct clk * __init
+struct clk_hw * __init
+sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags);
+
+struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents, u8 id,
- const struct clk_programmable_layout *layout);
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents);
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
- const char *parent_name, u8 id);
+ const char *parent_name, u8 id, unsigned long flags);
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents);
-struct clk * __init
+struct clk_hw * __init
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents);
-
-struct clk * __init
+struct clk_hw * __init
at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name, const u32 *divisors);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name);
-#ifdef CONFIG_PM
-void pmc_register_id(u8 id);
-void pmc_register_pck(u8 pck);
-#else
-static inline void pmc_register_id(u8 id) {}
-static inline void pmc_register_pck(u8 pck) {}
-#endif
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
+ const char *parent_name);
#endif /* __PMC_H_ */
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 5368b0dbc9..3a477ffc95 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -1,18 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 140000000, .max = 200000000 },
.divisors = { 1, 2, 4, 3 },
@@ -26,7 +24,7 @@ static const struct clk_master_layout sam9x60_master_layout = {
};
static const struct clk_range plla_outputs[] = {
- { .min = 300000000, .max = 600000000 },
+ { .min = 2343750, .max = 1200000000 },
};
static const struct clk_pll_characteristics plla_characteristics = {
@@ -46,6 +44,20 @@ static const struct clk_pll_characteristics upll_characteristics = {
.upll = true,
};
+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,
+};
+
+static const struct clk_pll_layout pll_div_layout = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
static const struct clk_programmable_layout sam9x60_programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 8,
@@ -64,17 +76,23 @@ static const struct clk_pcr_layout sam9x60_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sam9x60_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
+ /*
+ * 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 },
- { .n = "qspick", .p = "masterck", .id = 19 },
+ { .n = "qspick", .p = "masterck_div", .id = 19 },
};
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sam9x60_periphck[] = {
{ .n = "pioA_clk", .id = 2, },
@@ -121,7 +139,11 @@ static const struct {
{ .n = "pioD_clk", .id = 44, },
{ .n = "tcb1_clk", .id = 45, },
{ .n = "dbgu_clk", .id = 47, },
- { .n = "mpddr_clk", .id = 49, },
+ /*
+ * 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, .flags = CLK_IS_CRITICAL },
};
static const struct {
@@ -160,10 +182,10 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
const char *td_slck_name, *md_slck_name, *mainxtal_name;
struct pmc_data *sam9x60_pmc;
const char *parent_names[6];
+ struct clk_hw *main_osc_hw;
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
- bool bypass;
i = of_property_match_string(np, "clock-names", "td_slck");
if (i < 0)
@@ -193,17 +215,15 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
if (!sam9x60_pmc)
return;
- hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000,
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
50000000);
if (IS_ERR(hw))
goto err_free;
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
-
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
- bypass);
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, 0);
if (IS_ERR(hw))
goto err_free;
+ main_osc_hw = hw;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
@@ -213,15 +233,45 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_pmc->chws[PMC_MAIN] = hw;
- hw = sam9x60_clk_register_pll(regmap, "pllack",
- "mainck", 0, &plla_characteristics);
+ hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
+ "mainck", sam9x60_pmc->chws[PMC_MAIN],
+ 0, &plla_characteristics,
+ &pll_frac_layout,
+ /*
+ * This feeds pllack_divck which
+ * feeds CPU. It should not be
+ * disabled.
+ */
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
+ "pllack_fracck", 0, &plla_characteristics,
+ &pll_div_layout,
+ /*
+ * This feeds CPU. It should not
+ * be disabled.
+ */
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
sam9x60_pmc->chws[PMC_PLLACK] = hw;
- hw = sam9x60_clk_register_pll(regmap, "upllck",
- "main_osc", 1, &upll_characteristics);
+ hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
+ "main_osc", main_osc_hw, 1,
+ &upll_characteristics,
+ &pll_frac_layout, CLK_SET_RATE_GATE);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
+ "upllck_fracck", 1, &upll_characteristics,
+ &pll_div_layout,
+ CLK_SET_RATE_GATE |
+ CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT);
if (IS_ERR(hw))
goto err_free;
@@ -229,17 +279,24 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[0] = md_slck_name;
parent_names[1] = "mainck";
- parent_names[2] = "pllack";
- hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
- &sam9x60_master_layout,
- &mck_characteristics);
+ parent_names[2] = "pllack_divck";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
+ parent_names, &sam9x60_master_layout,
+ &mck_characteristics, &mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres", &sam9x60_master_layout,
+ &mck_characteristics, &mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
sam9x60_pmc->chws[PMC_MCK] = hw;
- parent_names[0] = "pllack";
- parent_names[1] = "upllck";
+ parent_names[0] = "pllack_divck";
+ parent_names[1] = "upllck_divck";
parent_names[2] = "main_osc";
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
if (IS_ERR(hw))
@@ -248,15 +305,18 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[0] = md_slck_name;
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
- parent_names[3] = "masterck";
- parent_names[4] = "pllack";
- parent_names[5] = "upllck";
- for (i = 0; i < 8; i++) {
- char *name = xasprintf("prog%d", i);
+ parent_names[3] = "masterck_div";
+ parent_names[4] = "pllack_divck";
+ parent_names[5] = "upllck_divck";
+ 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, 6, i,
- &sam9x60_programmable_layout);
+ &sam9x60_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -266,7 +326,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n,
sam9x60_systemck[i].p,
- sam9x60_systemck[i].id);
+ sam9x60_systemck[i].id,
+ sam9x60_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -274,12 +335,13 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_periphck[i].n,
- "masterck",
+ "masterck_div",
sam9x60_periphck[i].id,
- &range);
+ &range, INT_MIN,
+ sam9x60_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -287,20 +349,19 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) {
- hw = at91_clk_register_generated(regmap,
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_gck[i].n,
- parent_names, 6,
+ parent_names, NULL, 6,
sam9x60_gck[i].id,
- false,
- &sam9x60_gck[i].r);
+ &sam9x60_gck[i].r, INT_MIN);
if (IS_ERR(hw))
goto err_free;
sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
return;
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index 31c7259b91..96c0d1f6a4 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 124000000, .max = 166000000 },
.divisors = { 1, 2, 4, 3 },
@@ -44,16 +41,21 @@ static const struct clk_pcr_layout sama5d2_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sama5d2_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
- { .n = "lcdck", .p = "masterck", .id = 3 },
- { .n = "uhpck", .p = "usbck", .id = 6 },
- { .n = "udpck", .p = "usbck", .id = 7 },
- { .n = "pck0", .p = "prog0", .id = 8 },
- { .n = "pck1", .p = "prog1", .id = 9 },
- { .n = "pck2", .p = "prog2", .id = 10 },
- { .n = "iscck", .p = "masterck", .id = 18 },
+ /*
+ * 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 = "lcdck", .p = "masterck_div", .id = 3 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
+ { .n = "iscck", .p = "masterck_div", .id = 18 },
};
static const struct {
@@ -101,6 +103,7 @@ static const struct {
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sama5d2_periphck[] = {
{ .n = "dma0_clk", .id = 6, },
@@ -108,7 +111,11 @@ static const struct {
{ .n = "aes_clk", .id = 9, },
{ .n = "aesb_clk", .id = 10, },
{ .n = "sha_clk", .id = 12, },
- { .n = "mpddr_clk", .id = 13, },
+ /*
+ * 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 = 13, .flags = CLK_IS_CRITICAL },
{ .n = "matrix0_clk", .id = 15, },
{ .n = "sdmmc0_hclk", .id = 31, },
{ .n = "sdmmc1_hclk", .id = 32, },
@@ -122,21 +129,30 @@ static const struct {
char *n;
u8 id;
struct clk_range r;
- bool pll;
+ int chg_pid;
} sama5d2_gck[] = {
- { .n = "sdmmc0_gclk", .id = 31, },
- { .n = "sdmmc1_gclk", .id = 32, },
- { .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, },
- { .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, },
- { .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
- { .n = "isc_gclk", .id = 46, },
- { .n = "pdmic_gclk", .id = 48, },
- { .n = "i2s0_gclk", .id = 54, .pll = true },
- { .n = "i2s1_gclk", .id = 55, .pll = true },
- { .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, },
- { .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, },
- { .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 },
- .pll = true },
+ { .n = "flx0_gclk", .id = 19, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx1_gclk", .id = 20, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx2_gclk", .id = 21, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx3_gclk", .id = 22, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx4_gclk", .id = 23, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart0_gclk", .id = 24, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart1_gclk", .id = 25, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart2_gclk", .id = 26, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart3_gclk", .id = 27, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart4_gclk", .id = 28, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, },
+ { .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, },
+ { .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tcb1_gclk", .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "pwm_gclk", .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "isc_gclk", .id = 46, .chg_pid = INT_MIN, },
+ { .n = "pdmic_gclk", .id = 48, .chg_pid = INT_MIN, },
+ { .n = "i2s0_gclk", .id = 54, .chg_pid = 5, },
+ { .n = "i2s1_gclk", .id = 55, .chg_pid = 5, },
+ { .n = "can0_gclk", .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "can1_gclk", .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "classd_gclk", .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, },
};
static const struct clk_programmable_layout sama5d2_programmable_layout = {
@@ -154,7 +170,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
struct pmc_data *sama5d2_pmc;
const char *parent_names[6];
struct regmap *regmap, *regmap_sfr;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -173,7 +189,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- sama5d2_pmc = pmc_data_allocate(PMC_AUDIOPLLCK + 1,
+ sama5d2_pmc = pmc_data_allocate(PMC_AUDIOPINCK + 1,
nck(sama5d2_systemck),
nck(sama5d2_periph32ck),
nck(sama5d2_gck), 3);
@@ -221,6 +237,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
+ sama5d2_pmc->chws[PMC_AUDIOPINCK] = hw;
+
hw = at91_clk_register_audio_pll_pmc(regmap, "audiopll_pmcck",
"audiopll_fracck");
if (IS_ERR(hw))
@@ -242,15 +260,24 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91sam9x5_master_layout,
- &mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
sama5d2_pmc->chws[PMC_MCK] = hw;
- hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck");
+ hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck_div");
if (IS_ERR(hw))
goto err_free;
@@ -266,16 +293,17 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
parent_names[5] = "audiopll_pmcck";
for (i = 0; i < 3; i++) {
- char *name;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
- &sama5d2_programmable_layout);
+ &sama5d2_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -285,7 +313,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d2_systemck[i].n,
sama5d2_systemck[i].p,
- sama5d2_systemck[i].id);
+ sama5d2_systemck[i].id,
+ sama5d2_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -293,12 +322,13 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_periphck[i].n,
- "masterck",
+ "masterck_div",
sama5d2_periphck[i].id,
- &range);
+ &range, INT_MIN,
+ sama5d2_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -306,12 +336,13 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_periph32ck[i].n,
"h32mxck",
sama5d2_periph32ck[i].id,
- &sama5d2_periph32ck[i].r);
+ &sama5d2_periph32ck[i].r,
+ INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
@@ -322,16 +353,16 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
parent_names[5] = "audiopll_pmcck";
for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) {
- hw = at91_clk_register_generated(regmap,
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_gck[i].n,
- parent_names, 6,
+ parent_names, NULL, 6,
sama5d2_gck[i].id,
- sama5d2_gck[i].pll,
- &sama5d2_gck[i].r);
+ &sama5d2_gck[i].r,
+ sama5d2_gck[i].chg_pid);
if (IS_ERR(hw))
goto err_free;
@@ -358,11 +389,12 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_pmc->chws[PMC_I2S1_MUX] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d2_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d2_pmc);
return;
err_free:
kfree(sama5d2_pmc);
}
-CLK_OF_DECLARE_DRIVER(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup);
+
+CLK_OF_DECLARE(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup);
diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
index f9c89dccee..53a1a7413a 100644
--- a/drivers/clk/at91/sama5d3.c
+++ b/drivers/clk/at91/sama5d3.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 0, .max = 166000000 },
.divisors = { 1, 2, 4, 3 },
@@ -44,22 +41,28 @@ static const struct clk_pcr_layout sama5d3_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sama5d3_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
- { .n = "lcdck", .p = "masterck", .id = 3 },
- { .n = "smdck", .p = "smdclk", .id = 4 },
- { .n = "uhpck", .p = "usbck", .id = 6 },
- { .n = "udpck", .p = "usbck", .id = 7 },
- { .n = "pck0", .p = "prog0", .id = 8 },
- { .n = "pck1", .p = "prog1", .id = 9 },
- { .n = "pck2", .p = "prog2", .id = 10 },
+ /*
+ * 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 = "lcdck", .p = "masterck_div", .id = 3 },
+ { .n = "smdck", .p = "smdclk", .id = 4 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
};
static const struct {
char *n;
u8 id;
struct clk_range r;
+ unsigned long flags;
} sama5d3_periphck[] = {
{ .n = "dbgu_clk", .id = 2, },
{ .n = "hsmc_clk", .id = 5, },
@@ -103,7 +106,11 @@ static const struct {
{ .n = "tdes_clk", .id = 44, },
{ .n = "trng_clk", .id = 45, },
{ .n = "fuse_clk", .id = 48, },
- { .n = "mpddr_clk", .id = 49, },
+ /*
+ * 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, .flags = CLK_IS_CRITICAL },
};
static void __init sama5d3_pmc_setup(struct device_node *np)
@@ -112,7 +119,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
struct pmc_data *sama5d3_pmc;
const char *parent_names[5];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -176,9 +183,18 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91sam9x5_master_layout,
- &mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
@@ -198,13 +214,16 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
for (i = 0; i < 3; i++) {
- char *name = xasprintf("prog%d", i);
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -214,7 +233,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d3_systemck[i].n,
sama5d3_systemck[i].p,
- sama5d3_systemck[i].id);
+ sama5d3_systemck[i].id,
+ sama5d3_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -222,19 +242,21 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d3_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d3_pcr_layout,
sama5d3_periphck[i].n,
- "masterck",
+ "masterck_div",
sama5d3_periphck[i].id,
- &sama5d3_periphck[i].r);
+ &sama5d3_periphck[i].r,
+ INT_MIN,
+ sama5d3_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
sama5d3_pmc->phws[sama5d3_periphck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d3_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d3_pmc);
return;
@@ -245,4 +267,4 @@ err_free:
* The TCB is used as the clocksource so its clock is needed early. This means
* this can't be a platform driver.
*/
-CLK_OF_DECLARE_DRIVER(sama5d3_pmc, "atmel,sama5d3-pmc", sama5d3_pmc_setup);
+CLK_OF_DECLARE(sama5d3_pmc, "atmel,sama5d3-pmc", sama5d3_pmc_setup);
diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
index 2d6fc6df97..8fbd810883 100644
--- a/drivers/clk/at91/sama5d4.c
+++ b/drivers/clk/at91/sama5d4.c
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
+static DEFINE_SPINLOCK(mck_lock);
+
static const struct clk_master_characteristics mck_characteristics = {
.output = { .min = 125000000, .max = 200000000 },
.divisors = { 1, 2, 4, 3 },
@@ -43,16 +40,21 @@ static const struct clk_pcr_layout sama5d4_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sama5d4_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
- { .n = "lcdck", .p = "masterck", .id = 3 },
- { .n = "smdck", .p = "smdclk", .id = 4 },
- { .n = "uhpck", .p = "usbck", .id = 6 },
- { .n = "udpck", .p = "usbck", .id = 7 },
- { .n = "pck0", .p = "prog0", .id = 8 },
- { .n = "pck1", .p = "prog1", .id = 9 },
- { .n = "pck2", .p = "prog2", .id = 10 },
+ /*
+ * 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 = "lcdck", .p = "masterck_div", .id = 3 },
+ { .n = "smdck", .p = "smdclk", .id = 4 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
};
static const struct {
@@ -107,12 +109,17 @@ static const struct {
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sama5d4_periphck[] = {
{ .n = "dma0_clk", .id = 8 },
{ .n = "cpkcc_clk", .id = 10 },
{ .n = "aesb_clk", .id = 13 },
- { .n = "mpddr_clk", .id = 16 },
+ /*
+ * 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 = 16, .flags = CLK_IS_CRITICAL },
{ .n = "matrix0_clk", .id = 18 },
{ .n = "vdec_clk", .id = 19 },
{ .n = "dma1_clk", .id = 50 },
@@ -127,7 +134,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
struct pmc_data *sama5d4_pmc;
const char *parent_names[5];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -191,15 +198,24 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
- &at91sam9x5_master_layout,
- &mck_characteristics);
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres",
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock,
+ CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
sama5d4_pmc->chws[PMC_MCK] = hw;
- hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck");
+ hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck_div");
if (IS_ERR(hw))
goto err_free;
@@ -221,15 +237,16 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
- parent_names[4] = "masterck";
+ parent_names[4] = "masterck_div";
for (i = 0; i < 3; i++) {
- char *name;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
@@ -239,7 +256,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama5d4_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d4_systemck[i].n,
sama5d4_systemck[i].p,
- sama5d4_systemck[i].id);
+ sama5d4_systemck[i].id,
+ sama5d4_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -247,12 +265,13 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d4_pcr_layout,
sama5d4_periphck[i].n,
- "masterck",
+ "masterck_div",
sama5d4_periphck[i].id,
- &range);
+ &range, INT_MIN,
+ sama5d4_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -260,23 +279,24 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d4_pcr_layout,
sama5d4_periph32ck[i].n,
"h32mxck",
sama5d4_periph32ck[i].id,
- &range);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
sama5d4_pmc->phws[sama5d4_periph32ck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d4_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d4_pmc);
return;
err_free:
kfree(sama5d4_pmc);
}
-CLK_OF_DECLARE_DRIVER(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup);
+
+CLK_OF_DECLARE(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup);
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
new file mode 100644
index 0000000000..7d33367176
--- /dev/null
+++ b/drivers/clk/at91/sama7g5.c
@@ -0,0 +1,1133 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SAMA7G5 PMC code.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <mfd/syscon.h>
+#include <linux/slab.h>
+#include <stdio.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+#define SAMA7G5_INIT_TABLE(_table, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) \
+ (_table)[_i] = _i; \
+ } while (0)
+
+#define SAMA7G5_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(pmc_mck0_lock);
+static DEFINE_SPINLOCK(pmc_mckX_lock);
+
+/*
+ * PLL clocks identifiers
+ * @PLL_ID_CPU: CPU PLL identifier
+ * @PLL_ID_SYS: System PLL identifier
+ * @PLL_ID_DDR: DDR PLL identifier
+ * @PLL_ID_IMG: Image subsystem PLL identifier
+ * @PLL_ID_BAUD: Baud PLL identifier
+ * @PLL_ID_AUDIO: Audio PLL identifier
+ * @PLL_ID_ETH: Ethernet PLL identifier
+ */
+enum pll_ids {
+ PLL_ID_CPU,
+ PLL_ID_SYS,
+ PLL_ID_DDR,
+ PLL_ID_IMG,
+ PLL_ID_BAUD,
+ PLL_ID_AUDIO,
+ PLL_ID_ETH,
+ PLL_ID_MAX,
+};
+
+/*
+ * PLL type identifiers
+ * @PLL_TYPE_FRAC: fractional PLL identifier
+ * @PLL_TYPE_DIV: divider PLL identifier
+ */
+enum pll_type {
+ PLL_TYPE_FRAC,
+ PLL_TYPE_DIV,
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_layout_frac = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+/* Layout for DIVPMC dividers. */
+static const struct clk_pll_layout pll_layout_divpmc = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_layout_divio = {
+ .div_mask = GENMASK(19, 12),
+ .endiv_mask = BIT(30),
+ .div_shift = 12,
+ .endiv_shift = 30,
+};
+
+/*
+ * CPU PLL output range.
+ * Notice: The upper limit has been setup to 1000000002 due to hardware
+ * block which cannot output exactly 1GHz.
+ */
+static const struct clk_range cpu_pll_outputs[] = {
+ { .min = 2343750, .max = 1000000002 },
+};
+
+/* PLL output range. */
+static const struct clk_range pll_outputs[] = {
+ { .min = 2343750, .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,
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics pll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(pll_outputs),
+ .output = pll_outputs,
+};
+
+/*
+ * PLL clocks description
+ * @n: clock name
+ * @p: clock parent
+ * @l: clock layout
+ * @c: clock characteristics
+ * @t: clock type
+ * @f: clock flags
+ * @eid: export index in sama7g5->chws[] array
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ const struct clk_pll_layout *l;
+ const struct clk_pll_characteristics *c;
+ unsigned long f;
+ u8 t;
+ u8 eid;
+} sama7g5_plls[][PLL_ID_MAX] = {
+ [PLL_ID_CPU] = {
+ { .n = "cpupll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds cpupll_divpmcck which feeds CPU. It should
+ * not be disabled.
+ */
+ .f = CLK_IS_CRITICAL, },
+
+ { .n = "cpupll_divpmcck",
+ .p = "cpupll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds CPU. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
+ .eid = PMC_CPUPLL, },
+ },
+
+ [PLL_ID_SYS] = {
+ { .n = "syspll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds syspll_divpmcck which may feed critical parts
+ * of the systems like timers. Therefore it should not be
+ * disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+
+ { .n = "syspll_divpmcck",
+ .p = "syspll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /*
+ * This may feed critical parts of the systems like timers.
+ * Therefore it should not be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .eid = PMC_SYSPLL, },
+ },
+
+ [PLL_ID_DDR] = {
+ { .n = "ddrpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds ddrpll_divpmcck which feeds DDR. It should not
+ * be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+
+ { .n = "ddrpll_divpmcck",
+ .p = "ddrpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds DDR. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+ },
+
+ [PLL_ID_IMG] = {
+ { .n = "imgpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "imgpll_divpmcck",
+ .p = "imgpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT, },
+ },
+
+ [PLL_ID_BAUD] = {
+ { .n = "baudpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "baudpll_divpmcck",
+ .p = "baudpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT, },
+ },
+
+ [PLL_ID_AUDIO] = {
+ { .n = "audiopll_fracck",
+ .p = "main_xtal",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "audiopll_divpmcck",
+ .p = "audiopll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOPMCPLL, },
+
+ { .n = "audiopll_diviock",
+ .p = "audiopll_fracck",
+ .l = &pll_layout_divio,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOIOPLL, },
+ },
+
+ [PLL_ID_ETH] = {
+ { .n = "ethpll_fracck",
+ .p = "main_xtal",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "ethpll_divpmcck",
+ .p = "ethpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT, },
+ },
+};
+
+/*
+ * Master clock (MCK[1..4]) description
+ * @n: clock name
+ * @ep: extra parents names array
+ * @ep_chg_chg_id: index in parents array that specifies the changeable
+ * parent
+ * @ep_count: extra parents count
+ * @ep_mux_table: mux table for extra parents
+ * @id: clock id
+ * @eid: export index in sama7g5->chws[] array
+ * @c: true if clock is critical and cannot be disabled
+ */
+static const struct {
+ const char *n;
+ const char *ep[4];
+ int ep_chg_id;
+ u8 ep_count;
+ u8 ep_mux_table[4];
+ u8 id;
+ u8 eid;
+ u8 c;
+} sama7g5_mckx[] = {
+ { .n = "mck1",
+ .id = 1,
+ .ep = { "syspll_divpmcck", },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .eid = PMC_MCK1,
+ .c = 1, },
+
+ { .n = "mck2",
+ .id = 2,
+ .ep = { "ddrpll_divpmcck", },
+ .ep_mux_table = { 6, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+
+ { .n = "mck3",
+ .id = 3,
+ .ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .ep_mux_table = { 5, 6, 7, },
+ .ep_count = 3,
+ .ep_chg_id = 5, },
+
+ { .n = "mck4",
+ .id = 4,
+ .ep = { "syspll_divpmcck", },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+};
+
+/*
+ * System clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ u8 id;
+} sama7g5_systemck[] = {
+ { .n = "pck0", .p = "prog0", .id = 8, },
+ { .n = "pck1", .p = "prog1", .id = 9, },
+ { .n = "pck2", .p = "prog2", .id = 10, },
+ { .n = "pck3", .p = "prog3", .id = 11, },
+ { .n = "pck4", .p = "prog4", .id = 12, },
+ { .n = "pck5", .p = "prog5", .id = 13, },
+ { .n = "pck6", .p = "prog6", .id = 14, },
+ { .n = "pck7", .p = "prog7", .id = 15, },
+};
+
+/* Mux table for programmable clocks. */
+static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 5, 6, 7, 8, 9, 10, };
+
+/*
+ * Peripheral clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @r: clock range values
+ * @id: clock id
+ * @chgp: index in parent array of the changeable parent
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ struct clk_range r;
+ u8 chgp;
+ u8 id;
+} sama7g5_periphck[] = {
+ { .n = "pioA_clk", .p = "mck0", .id = 11, },
+ { .n = "securam_clk", .p = "mck0", .id = 18, },
+ { .n = "sfr_clk", .p = "mck1", .id = 19, },
+ { .n = "hsmc_clk", .p = "mck1", .id = 21, },
+ { .n = "xdmac0_clk", .p = "mck1", .id = 22, },
+ { .n = "xdmac1_clk", .p = "mck1", .id = 23, },
+ { .n = "xdmac2_clk", .p = "mck1", .id = 24, },
+ { .n = "acc_clk", .p = "mck1", .id = 25, },
+ { .n = "aes_clk", .p = "mck1", .id = 27, },
+ { .n = "tzaesbasc_clk", .p = "mck1", .id = 28, },
+ { .n = "asrc_clk", .p = "mck1", .id = 30, .r = { .max = 200000000, }, },
+ { .n = "cpkcc_clk", .p = "mck0", .id = 32, },
+ { .n = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "eic_clk", .p = "mck1", .id = 37, },
+ { .n = "flex0_clk", .p = "mck1", .id = 38, },
+ { .n = "flex1_clk", .p = "mck1", .id = 39, },
+ { .n = "flex2_clk", .p = "mck1", .id = 40, },
+ { .n = "flex3_clk", .p = "mck1", .id = 41, },
+ { .n = "flex4_clk", .p = "mck1", .id = 42, },
+ { .n = "flex5_clk", .p = "mck1", .id = 43, },
+ { .n = "flex6_clk", .p = "mck1", .id = 44, },
+ { .n = "flex7_clk", .p = "mck1", .id = 45, },
+ { .n = "flex8_clk", .p = "mck1", .id = 46, },
+ { .n = "flex9_clk", .p = "mck1", .id = 47, },
+ { .n = "flex10_clk", .p = "mck1", .id = 48, },
+ { .n = "flex11_clk", .p = "mck1", .id = 49, },
+ { .n = "gmac0_clk", .p = "mck1", .id = 51, },
+ { .n = "gmac1_clk", .p = "mck1", .id = 52, },
+ { .n = "icm_clk", .p = "mck1", .id = 55, },
+ { .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, },
+ { .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, },
+ { .n = "matrix_clk", .p = "mck1", .id = 60, },
+ { .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, },
+ { .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, },
+ { .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, },
+ { .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, },
+ { .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, },
+ { .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, },
+ { .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, },
+ { .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, },
+ { .n = "pit64b0_clk", .p = "mck1", .id = 70, },
+ { .n = "pit64b1_clk", .p = "mck1", .id = 71, },
+ { .n = "pit64b2_clk", .p = "mck1", .id = 72, },
+ { .n = "pit64b3_clk", .p = "mck1", .id = 73, },
+ { .n = "pit64b4_clk", .p = "mck1", .id = 74, },
+ { .n = "pit64b5_clk", .p = "mck1", .id = 75, },
+ { .n = "pwm_clk", .p = "mck1", .id = 77, },
+ { .n = "qspi0_clk", .p = "mck1", .id = 78, },
+ { .n = "qspi1_clk", .p = "mck1", .id = 79, },
+ { .n = "sdmmc0_clk", .p = "mck1", .id = 80, },
+ { .n = "sdmmc1_clk", .p = "mck1", .id = 81, },
+ { .n = "sdmmc2_clk", .p = "mck1", .id = 82, },
+ { .n = "sha_clk", .p = "mck1", .id = 83, },
+ { .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, },
+ { .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, },
+ { .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, },
+ { .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, },
+ { .n = "tcpca_clk", .p = "mck1", .id = 94, },
+ { .n = "tcpcb_clk", .p = "mck1", .id = 95, },
+ { .n = "tdes_clk", .p = "mck1", .id = 96, },
+ { .n = "trng_clk", .p = "mck1", .id = 97, },
+ { .n = "udphsa_clk", .p = "mck1", .id = 104, },
+ { .n = "udphsb_clk", .p = "mck1", .id = 105, },
+ { .n = "uhphs_clk", .p = "mck1", .id = 106, },
+};
+
+/*
+ * Generic clock description
+ * @n: clock name
+ * @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;
+} sama7g5_gck[] = {
+ { .n = "adc_gclk",
+ .id = 26,
+ .r = { .max = 100000000, },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 7, 9, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "asrc_gclk",
+ .id = 30,
+ .r = { .max = 200000000 },
+ .pp = { "audiopll_divpmcck", },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = 3, },
+
+ { .n = "csi_gclk",
+ .id = 33,
+ .r = { .max = 27000000 },
+ .pp = { "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .pp_mux_table = { 6, 7, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex0_gclk",
+ .id = 38,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex1_gclk",
+ .id = 39,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex2_gclk",
+ .id = 40,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex3_gclk",
+ .id = 41,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex4_gclk",
+ .id = 42,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex5_gclk",
+ .id = 43,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex6_gclk",
+ .id = 44,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex7_gclk",
+ .id = 45,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex8_gclk",
+ .id = 46,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex9_gclk",
+ .id = 47,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex10_gclk",
+ .id = 48,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex11_gclk",
+ .id = 49,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_gclk",
+ .id = 51,
+ .r = { .max = 125000000 },
+ .pp = { "ethpll_divpmcck", },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = 3, },
+
+ { .n = "gmac1_gclk",
+ .id = 52,
+ .r = { .max = 50000000 },
+ .pp = { "ethpll_divpmcck", },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_tsu_gclk",
+ .id = 53,
+ .r = { .max = 300000000 },
+ .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 9, 10, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac1_tsu_gclk",
+ .id = 54,
+ .r = { .max = 300000000 },
+ .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 9, 10, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "i2smcc0_gclk",
+ .id = 57,
+ .r = { .max = 100000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "i2smcc1_gclk",
+ .id = 58,
+ .r = { .max = 100000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "mcan0_gclk",
+ .id = 61,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan1_gclk",
+ .id = 62,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan2_gclk",
+ .id = 63,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan3_gclk",
+ .id = 64,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan4_gclk",
+ .id = 65,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan5_gclk",
+ .id = 66,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc0_gclk",
+ .id = 68,
+ .r = { .max = 50000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc1_gclk",
+ .id = 69,
+ .r = { .max = 50000000, },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b0_gclk",
+ .id = 70,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b1_gclk",
+ .id = 71,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b2_gclk",
+ .id = 72,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b3_gclk",
+ .id = 73,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b4_gclk",
+ .id = 74,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b5_gclk",
+ .id = 75,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi0_gclk",
+ .id = 78,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi1_gclk",
+ .id = 79,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "sdmmc0_gclk",
+ .id = 80,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "sdmmc1_gclk",
+ .id = 81,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "sdmmc2_gclk",
+ .id = 82,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "spdifrx_gclk",
+ .id = 84,
+ .r = { .max = 150000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "spdiftx_gclk",
+ .id = 85,
+ .r = { .max = 25000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "tcb0_ch0_gclk",
+ .id = 88,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcb1_ch0_gclk",
+ .id = 91,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcpca_gclk",
+ .id = 94,
+ .r = { .max = 32768, },
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcpcb_gclk",
+ .id = 95,
+ .r = { .max = 32768, },
+ .pp_chg_id = INT_MIN, },
+};
+
+/* MCK0 characteristics. */
+static const struct clk_master_characteristics mck0_characteristics = {
+ .output = { .min = 32768, .max = 200000000 },
+ .divisors = { 1, 2, 4, 3, 5 },
+ .have_div3_pres = 1,
+};
+
+/* MCK0 layout. */
+static const struct clk_master_layout mck0_layout = {
+ .mask = 0x773,
+ .pres_shift = 4,
+ .offset = 0x28,
+};
+
+/* Programmable clock layout. */
+static const struct clk_programmable_layout programmable_layout = {
+ .pres_mask = 0xff,
+ .pres_shift = 8,
+ .css_mask = 0x1f,
+ .have_slck_mck = 0,
+ .is_pres_direct = 1,
+};
+
+/* Peripheral clock layout. */
+static const struct clk_pcr_layout sama7g5_pcr_layout = {
+ .offset = 0x88,
+ .cmd = BIT(31),
+ .gckcss_mask = GENMASK(12, 8),
+ .pid_mask = GENMASK(6, 0),
+};
+
+static void __init sama7g5_pmc_setup(struct device_node *np)
+{
+ const char *td_slck_name, *md_slck_name, *mainxtal_name;
+ struct pmc_data *sama7g5_pmc;
+ const char *parent_names[10];
+ void **alloc_mem = NULL;
+ int alloc_mem_size = 0;
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ bool bypass;
+ 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;
+
+ sama7g5_pmc = pmc_data_allocate(PMC_MCK1 + 1,
+ nck(sama7g5_systemck),
+ nck(sama7g5_periphck),
+ nck(sama7g5_gck), 8);
+ if (!sama7g5_pmc)
+ return;
+
+ alloc_mem = kmalloc(sizeof(void *) *
+ (ARRAY_SIZE(sama7g5_mckx) + ARRAY_SIZE(sama7g5_gck)),
+ GFP_KERNEL);
+ if (!alloc_mem)
+ goto err_free;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_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 (!sama7g5_plls[i][j].n)
+ continue;
+
+ switch (sama7g5_plls[i][j].t) {
+ case PLL_TYPE_FRAC:
+ if (!strcmp(sama7g5_plls[i][j].p, "mainck"))
+ parent_hw = sama7g5_pmc->chws[PMC_MAIN];
+ else
+ parent_hw = __clk_get_hw(of_clk_get_by_name(np,
+ sama7g5_plls[i][j].p));
+
+ hw = sam9x60_clk_register_frac_pll(regmap,
+ &pmc_pll_lock, sama7g5_plls[i][j].n,
+ sama7g5_plls[i][j].p, parent_hw, i,
+ sama7g5_plls[i][j].c,
+ sama7g5_plls[i][j].l,
+ sama7g5_plls[i][j].f);
+ break;
+
+ case PLL_TYPE_DIV:
+ hw = sam9x60_clk_register_div_pll(regmap,
+ &pmc_pll_lock, sama7g5_plls[i][j].n,
+ sama7g5_plls[i][j].p, i,
+ sama7g5_plls[i][j].c,
+ sama7g5_plls[i][j].l,
+ sama7g5_plls[i][j].f);
+ break;
+
+ default:
+ continue;
+ }
+
+ if (IS_ERR(hw))
+ goto err_free;
+
+ if (sama7g5_plls[i][j].eid)
+ sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw;
+ }
+ }
+
+ parent_names[0] = "cpupll_divpmcck";
+ hw = at91_clk_register_master_div(regmap, "mck0", "cpupll_divpmcck",
+ &mck0_layout, &mck0_characteristics,
+ &pmc_mck0_lock, CLK_GET_RATE_NOCACHE);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
+ u8 num_parents = 3 + sama7g5_mckx[i].ep_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ SAMA7G5_INIT_TABLE(mux_table, 3);
+ SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_mckx[i].ep_mux_table,
+ sama7g5_mckx[i].ep_count);
+ SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_mckx[i].ep,
+ sama7g5_mckx[i].ep_count);
+
+ hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n,
+ num_parents, parent_names, mux_table,
+ &pmc_mckX_lock, sama7g5_mckx[i].id,
+ sama7g5_mckx[i].c,
+ sama7g5_mckx[i].ep_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ alloc_mem[alloc_mem_size++] = mux_table;
+
+ if (sama7g5_mckx[i].eid)
+ sama7g5_pmc->chws[sama7g5_mckx[i].eid] = hw;
+ }
+
+ hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "syspll_divpmcck";
+ parent_names[4] = "ddrpll_divpmcck";
+ parent_names[5] = "imgpll_divpmcck";
+ parent_names[6] = "baudpll_divpmcck";
+ parent_names[7] = "audiopll_divpmcck";
+ parent_names[8] = "ethpll_divpmcck";
+ for (i = 0; i < 8; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name, parent_names,
+ 9, i,
+ &programmable_layout,
+ sama7g5_prog_mux_table);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n,
+ sama7g5_systemck[i].p,
+ sama7g5_systemck[i].id, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->shws[sama7g5_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &sama7g5_pcr_layout,
+ sama7g5_periphck[i].n,
+ sama7g5_periphck[i].p,
+ sama7g5_periphck[i].id,
+ &sama7g5_periphck[i].r,
+ sama7g5_periphck[i].chgp ? 0 :
+ INT_MIN, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw;
+ }
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
+ u8 num_parents = 3 + sama7g5_gck[i].pp_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ SAMA7G5_INIT_TABLE(mux_table, 3);
+ SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_gck[i].pp_mux_table,
+ sama7g5_gck[i].pp_count);
+ SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_gck[i].pp,
+ sama7g5_gck[i].pp_count);
+
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
+ &sama7g5_pcr_layout,
+ sama7g5_gck[i].n,
+ parent_names, mux_table,
+ num_parents,
+ sama7g5_gck[i].id,
+ &sama7g5_gck[i].r,
+ sama7g5_gck[i].pp_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->ghws[sama7g5_gck[i].id] = hw;
+ alloc_mem[alloc_mem_size++] = mux_table;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7g5_pmc);
+
+ return;
+
+err_free:
+ if (alloc_mem) {
+ for (i = 0; i < alloc_mem_size; i++)
+ kfree(alloc_mem[i]);
+ kfree(alloc_mem);
+ }
+
+ kfree(sama7g5_pmc);
+}
+
+/* Some clks are used for a clocksource */
+CLK_OF_DECLARE(sama7g5_pmc, "microchip,sama7g5-pmc", sama7g5_pmc_setup);
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index 579fbf2479..1e03537cf1 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -5,19 +5,12 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <clock.h>
#include <of.h>
#include <of_address.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
-#include <linux/clk/at91_pmc.h>
-#include <linux/overflow.h>
-#include <mfd/syscon.h>
-#include <regmap.h>
-
-
+#include <linux/io.h>
#define SLOW_CLOCK_FREQ 32768
#define SLOWCK_SW_CYCLES 5
@@ -38,10 +31,9 @@ struct clk_slow_osc {
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long startup_usec;
- const char *parent_name;
};
-#define to_clk_slow_osc(_hw) container_of(_hw, struct clk_slow_osc, hw)
+#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
struct clk_sama5d4_slow_osc {
struct clk_hw hw;
@@ -49,33 +41,31 @@ struct clk_sama5d4_slow_osc {
const struct clk_slow_bits *bits;
unsigned long startup_usec;
bool prepared;
- const char *parent_name;
};
-#define to_clk_sama5d4_slow_osc(_hw) container_of(_hw, struct clk_sama5d4_slow_osc, hw)
+#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
struct clk_slow_rc_osc {
struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long frequency;
+ unsigned long accuracy;
unsigned long startup_usec;
- const char *parent_name;
};
-#define to_clk_slow_rc_osc(_hw) container_of(_hw, struct clk_slow_rc_osc, hw)
+#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
struct clk_sam9x5_slow {
struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
u8 parent;
- const char *parent_names[];
};
-#define to_clk_sam9x5_slow(_hw) container_of(_hw, struct clk_sam9x5_slow, hw)
+#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
-static int clk_slow_osc_enable(struct clk_hw *hw)
+static int clk_slow_osc_prepare(struct clk_hw *hw)
{
struct clk_slow_osc *osc = to_clk_slow_osc(hw);
void __iomem *sckcr = osc->sckcr;
@@ -91,7 +81,7 @@ static int clk_slow_osc_enable(struct clk_hw *hw)
return 0;
}
-static void clk_slow_osc_disable(struct clk_hw *hw)
+static void clk_slow_osc_unprepare(struct clk_hw *hw)
{
struct clk_slow_osc *osc = to_clk_slow_osc(hw);
void __iomem *sckcr = osc->sckcr;
@@ -103,7 +93,7 @@ static void clk_slow_osc_disable(struct clk_hw *hw)
writel(tmp & ~osc->bits->cr_osc32en, sckcr);
}
-static int clk_slow_osc_is_enabled(struct clk_hw *hw)
+static int clk_slow_osc_is_prepared(struct clk_hw *hw)
{
struct clk_slow_osc *osc = to_clk_slow_osc(hw);
void __iomem *sckcr = osc->sckcr;
@@ -116,12 +106,12 @@ static int clk_slow_osc_is_enabled(struct clk_hw *hw)
}
static const struct clk_ops slow_osc_ops = {
- .enable = clk_slow_osc_enable,
- .disable = clk_slow_osc_disable,
- .is_enabled = clk_slow_osc_is_enabled,
+ .enable = clk_slow_osc_prepare,
+ .disable = clk_slow_osc_unprepare,
+ .is_enabled = clk_slow_osc_is_prepared,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_slow_osc(void __iomem *sckcr,
const char *name,
const char *parent_name,
@@ -129,21 +119,25 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
bool bypass,
const struct clk_slow_bits *bits)
{
- int ret;
struct clk_slow_osc *osc;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
if (!sckcr || !name || !parent_name)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
- osc->hw.clk.name = name;
- osc->hw.clk.ops = &slow_osc_ops;
- osc->parent_name = parent_name;
- osc->hw.clk.parent_names = &osc->parent_name;
- osc->hw.clk.num_parents = 1;
- /* osc->clk.flags = CLK_IGNORE_UNUSED; */
+ init.name = name;
+ init.ops = &slow_osc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->sckcr = sckcr;
osc->startup_usec = startup;
osc->bits = bits;
@@ -152,20 +146,21 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
osc->bits->cr_osc32byp, sckcr);
- ret = bclk_register(&osc->hw.clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
kfree(osc);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &osc->hw.clk;
+ return hw;
}
-static void at91_clk_unregister_slow_osc(struct clk *clk)
+static void at91_clk_unregister_slow_osc(struct clk_hw *hw)
{
- struct clk_slow_osc *osc = to_clk_slow_osc(clk_to_clk_hw(clk));
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
- clk_unregister(clk);
+ clk_hw_unregister(hw);
kfree(osc);
}
@@ -177,7 +172,7 @@ static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
return osc->frequency;
}
-static int clk_slow_rc_osc_enable(struct clk_hw *hw)
+static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
{
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
void __iomem *sckcr = osc->sckcr;
@@ -189,7 +184,7 @@ static int clk_slow_rc_osc_enable(struct clk_hw *hw)
return 0;
}
-static void clk_slow_rc_osc_disable(struct clk_hw *hw)
+static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
{
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
void __iomem *sckcr = osc->sckcr;
@@ -197,7 +192,7 @@ static void clk_slow_rc_osc_disable(struct clk_hw *hw)
writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
}
-static int clk_slow_rc_osc_is_enabled(struct clk_hw *hw)
+static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
{
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
@@ -205,13 +200,13 @@ static int clk_slow_rc_osc_is_enabled(struct clk_hw *hw)
}
static const struct clk_ops slow_rc_osc_ops = {
- .enable = clk_slow_rc_osc_enable,
- .disable = clk_slow_rc_osc_disable,
- .is_enabled = clk_slow_rc_osc_is_enabled,
+ .enable = clk_slow_rc_osc_prepare,
+ .disable = clk_slow_rc_osc_unprepare,
+ .is_enabled = clk_slow_rc_osc_is_prepared,
.recalc_rate = clk_slow_rc_osc_recalc_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_slow_rc_osc(void __iomem *sckcr,
const char *name,
unsigned long frequency,
@@ -220,37 +215,45 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
const struct clk_slow_bits *bits)
{
struct clk_slow_rc_osc *osc;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!sckcr || !name)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
- osc->hw.clk.name = name;
- osc->hw.clk.ops = &slow_rc_osc_ops;
- osc->hw.clk.parent_names = NULL;
- osc->hw.clk.num_parents = 0;
- /* init.flags = CLK_IGNORE_UNUSED; */
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &slow_rc_osc_ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->sckcr = sckcr;
osc->bits = bits;
osc->frequency = frequency;
+ osc->accuracy = accuracy;
osc->startup_usec = startup;
- ret = bclk_register(&osc->hw.clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
kfree(osc);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &osc->hw.clk;
+ return hw;
}
-static void at91_clk_unregister_slow_rc_osc(struct clk *clk)
+static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)
{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk_to_clk_hw(clk));
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
- clk_unregister(clk);
+ clk_hw_unregister(hw);
kfree(osc);
}
@@ -293,7 +296,7 @@ static const struct clk_ops sam9x5_slow_ops = {
.get_parent = clk_sam9x5_slow_get_parent,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const char *name,
const char **parent_names,
@@ -301,37 +304,43 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const struct clk_slow_bits *bits)
{
struct clk_sam9x5_slow *slowck;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!sckcr || !name || !parent_names || !num_parents)
return ERR_PTR(-EINVAL);
- slowck = xzalloc(struct_size(slowck, parent_names, num_parents));
- slowck->hw.clk.name = name;
- slowck->hw.clk.ops = &sam9x5_slow_ops;
+ slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
+ if (!slowck)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sam9x5_slow_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = 0;
- memcpy(slowck->parent_names, parent_names,
- num_parents * sizeof(slowck->parent_names[0]));
- slowck->hw.clk.parent_names = slowck->parent_names;
- slowck->hw.clk.num_parents = num_parents;
+ slowck->hw.init = &init;
slowck->sckcr = sckcr;
slowck->bits = bits;
slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
- ret = bclk_register(&slowck->hw.clk);
+ hw = &slowck->hw;
+ ret = clk_hw_register(NULL, &slowck->hw);
if (ret) {
kfree(slowck);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &slowck->hw.clk;
+ return hw;
}
-static void at91_clk_unregister_sam9x5_slow(struct clk *clk)
+static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)
{
- struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk_to_clk_hw(clk));
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
- clk_unregister(clk);
+ clk_hw_unregister(hw);
kfree(slowck);
}
@@ -343,7 +352,7 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
void __iomem *regbase = of_iomap(np, 0);
struct device_node *child = NULL;
const char *xtal_name;
- struct clk *slow_rc, *slow_osc, *slowck;
+ struct clk_hw *slow_rc, *slow_osc, *slowck;
bool bypass;
int ret;
@@ -386,10 +395,10 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
/* DT backward compatibility */
if (child)
- ret = of_clk_add_provider(child, of_clk_src_simple_get,
+ ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,
slowck);
else
- ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
if (WARN_ON(ret))
goto unregister_slowck;
@@ -434,8 +443,8 @@ static const struct clk_slow_bits at91sam9x60_bits = {
static void __init of_sam9x60_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
- struct clk_onecell_data *clk_data;
- struct clk *slow_rc, *slow_osc;
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_hw *slow_rc, *slow_osc;
const char *xtal_name;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
bool bypass;
@@ -444,8 +453,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (!regbase)
return;
- slow_rc = clk_register_fixed_rate(parent_names[0], NULL, 0,
- 32768);
+ slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
+ NULL, 0, 32768,
+ 93750000);
if (IS_ERR(slow_rc))
return;
@@ -460,52 +470,45 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (IS_ERR(slow_osc))
goto unregister_slow_rc;
- clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);
if (!clk_data)
goto unregister_slow_osc;
/* MD_SLCK and TD_SLCK. */
- clk_data->clk_num = 2;
- clk_data->clks = kcalloc(clk_data->clk_num,
- sizeof(*clk_data->clks), GFP_KERNEL);
- if (!clk_data->clks)
+ clk_data->num = 2;
+ clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck",
+ parent_names[0],
+ 0, 32768);
+ if (IS_ERR(clk_data->hws[0]))
goto clk_data_free;
- clk_data->clks[0] = clk_register_fixed_rate("md_slck",
- parent_names[0],
- 0, 32768);
- if (IS_ERR(clk_data->clks[0]))
- goto clks_free;
-
- clk_data->clks[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
+ clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
parent_names, 2,
&at91sam9x60_bits);
- if (IS_ERR(clk_data->clks[1]))
+ if (IS_ERR(clk_data->hws[1]))
goto unregister_md_slck;
- ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
if (WARN_ON(ret))
goto unregister_td_slck;
return;
unregister_td_slck:
- at91_clk_unregister_sam9x5_slow(clk_data->clks[1]);
+ at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
unregister_md_slck:
- clk_unregister(clk_data->clks[0]);
-clks_free:
- kfree(clk_data->clks);
+ clk_hw_unregister(clk_data->hws[0]);
clk_data_free:
kfree(clk_data);
unregister_slow_osc:
at91_clk_unregister_slow_osc(slow_osc);
unregister_slow_rc:
- clk_unregister(slow_rc);
+ clk_hw_unregister(slow_rc);
}
CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
of_sam9x60_sckc_setup);
-static int clk_sama5d4_slow_osc_enable(struct clk_hw *hw)
+static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
{
struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
@@ -514,7 +517,7 @@ static int clk_sama5d4_slow_osc_enable(struct clk_hw *hw)
/*
* Assume that if it has already been selected (for example by the
- * bootloader), enough time has aready passed.
+ * bootloader), enough time has already passed.
*/
if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
osc->prepared = true;
@@ -527,7 +530,7 @@ static int clk_sama5d4_slow_osc_enable(struct clk_hw *hw)
return 0;
}
-static int clk_sama5d4_slow_osc_is_enabled(struct clk_hw *hw)
+static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
{
struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
@@ -535,8 +538,8 @@ static int clk_sama5d4_slow_osc_is_enabled(struct clk_hw *hw)
}
static const struct clk_ops sama5d4_slow_osc_ops = {
- .enable = clk_sama5d4_slow_osc_enable,
- .is_enabled = clk_sama5d4_slow_osc_is_enabled,
+ .enable = clk_sama5d4_slow_osc_prepare,
+ .is_enabled = clk_sama5d4_slow_osc_is_prepared,
};
static const struct clk_slow_bits at91sama5d4_bits = {
@@ -546,32 +549,41 @@ static const struct clk_slow_bits at91sama5d4_bits = {
static void __init of_sama5d4_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
- struct clk *slow_rc, *slowck;
+ struct clk_hw *slow_rc, *slowck;
struct clk_sama5d4_slow_osc *osc;
+ struct clk_init_data init;
+ const char *xtal_name;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
int ret;
if (!regbase)
return;
- slow_rc = clk_fixed(parent_names[0], 32768);
+ slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
+ parent_names[0],
+ NULL, 0, 32768,
+ 250000000);
if (IS_ERR(slow_rc))
return;
- osc = xzalloc(sizeof(*osc));
- osc->parent_name = of_clk_get_parent_name(np, 0);
- osc->hw.clk.name = parent_names[1];
- osc->hw.clk.ops = &sama5d4_slow_osc_ops;
- osc->hw.clk.parent_names = &osc->parent_name;
- osc->hw.clk.num_parents = 1;
+ xtal_name = of_clk_get_parent_name(np, 0);
- /* osc->clk.flags = CLK_IGNORE_UNUSED; */
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ goto unregister_slow_rc;
+
+ init.name = parent_names[1];
+ init.ops = &sama5d4_slow_osc_ops;
+ init.parent_names = &xtal_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->sckcr = regbase;
osc->startup_usec = 1200000;
osc->bits = &at91sama5d4_bits;
- ret = bclk_register(&osc->hw.clk);
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret)
goto free_slow_osc_data;
@@ -581,7 +593,7 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
if (IS_ERR(slowck))
goto unregister_slow_osc;
- ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
if (WARN_ON(ret))
goto unregister_slowck;
@@ -590,10 +602,11 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
unregister_slowck:
at91_clk_unregister_sam9x5_slow(slowck);
unregister_slow_osc:
- clk_unregister(&osc->hw.clk);
+ clk_hw_unregister(&osc->hw);
free_slow_osc_data:
kfree(osc);
- clk_unregister(slow_rc);
+unregister_slow_rc:
+ clk_hw_unregister(slow_rc);
}
CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
of_sama5d4_sckc_setup);
diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c
index 385cfd5d3f..aabeb88f59 100644
--- a/drivers/clk/bcm/clk-bcm2835-aux.c
+++ b/drivers/clk/bcm/clk-bcm2835-aux.c
@@ -13,7 +13,7 @@
#define BCM2835_AUXIRQ 0x00
#define BCM2835_AUXENB 0x04
-static int bcm2835_aux_clk_probe(struct device_d *dev)
+static int bcm2835_aux_clk_probe(struct device *dev)
{
struct clk_hw_onecell_data *onecell;
const char *parent;
@@ -25,7 +25,7 @@ static int bcm2835_aux_clk_probe(struct device_d *dev)
return PTR_ERR(parent_clk);
parent = __clk_get_name(parent_clk);
- reg = of_iomap(dev->device_node, 0);
+ reg = of_iomap(dev->of_node, 0);
if (!reg)
return -ENOMEM;
@@ -45,7 +45,7 @@ static int bcm2835_aux_clk_probe(struct device_d *dev)
onecell->hws[BCM2835_AUX_CLOCK_SPI2] =
clk_hw_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL);
- return of_clk_add_hw_provider(dev->device_node, of_clk_hw_onecell_get,
+ return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
onecell);
}
@@ -53,8 +53,9 @@ static const struct of_device_id bcm2835_aux_clk_of_match[] = {
{ .compatible = "brcm,bcm2835-aux", },
{},
};
+MODULE_DEVICE_TABLE(of, bcm2835_aux_clk_of_match);
-static struct driver_d bcm2835_aux_clk_driver = {
+static struct driver bcm2835_aux_clk_driver = {
.name = "bcm2835-aux-clk",
.of_compatible = bcm2835_aux_clk_of_match,
.probe = bcm2835_aux_clk_probe,
diff --git a/drivers/clk/clk-ar933x.c b/drivers/clk/clk-ar933x.c
index c5e57f41ec..c97caaa37e 100644
--- a/drivers/clk/clk-ar933x.c
+++ b/drivers/clk/clk-ar933x.c
@@ -104,7 +104,7 @@ static void ar933x_pll_init(void __iomem *base)
AR933X_PLL_CLOCK_CTRL_AHB_DIV_MASK);
}
-static int ar933x_clk_probe(struct device_d *dev)
+static int ar933x_clk_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -118,7 +118,7 @@ static int ar933x_clk_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
return 0;
@@ -131,8 +131,9 @@ static __maybe_unused struct of_device_id ar933x_clk_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ar933x_clk_dt_ids);
-static struct driver_d ar933x_clk_driver = {
+static struct driver ar933x_clk_driver = {
.probe = ar933x_clk_probe,
.name = "ar933x_clk",
.of_compatible = DRV_OF_COMPAT(ar933x_clk_dt_ids),
diff --git a/drivers/clk/clk-ar9344.c b/drivers/clk/clk-ar9344.c
index d2f63f2608..43a9da2857 100644
--- a/drivers/clk/clk-ar9344.c
+++ b/drivers/clk/clk-ar9344.c
@@ -99,7 +99,7 @@ static void ar9344_pll_init(void __iomem *base)
clks[ATH79_CLK_CPU] = clk_ar9344("cpu", "ref", base);
}
-static int ar9344_clk_probe(struct device_d *dev)
+static int ar9344_clk_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -113,7 +113,7 @@ static int ar9344_clk_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
return 0;
@@ -126,8 +126,9 @@ static __maybe_unused struct of_device_id ar9344_clk_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ar9344_clk_dt_ids);
-static struct driver_d ar9344_clk_driver = {
+static struct driver ar9344_clk_driver = {
.probe = ar9344_clk_probe,
.name = "ar9344_clk",
.of_compatible = DRV_OF_COMPAT(ar9344_clk_dt_ids),
diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c
index 4510283962..db775dc40b 100644
--- a/drivers/clk/clk-bulk.c
+++ b/drivers/clk/clk-bulk.c
@@ -23,7 +23,7 @@ void clk_bulk_put(int num_clks, struct clk_bulk_data *clks)
}
EXPORT_SYMBOL_GPL(clk_bulk_put);
-static int __clk_bulk_get(struct device_d *dev, int num_clks,
+static int __clk_bulk_get(struct device *dev, int num_clks,
struct clk_bulk_data *clks,
bool optional)
{
@@ -58,14 +58,14 @@ err:
return ret;
}
-int __must_check clk_bulk_get(struct device_d *dev, int num_clks,
+int __must_check clk_bulk_get(struct device *dev, int num_clks,
struct clk_bulk_data *clks)
{
return __clk_bulk_get(dev, num_clks, clks, false);
}
EXPORT_SYMBOL(clk_bulk_get);
-int __must_check clk_bulk_get_optional(struct device_d *dev, int num_clks,
+int __must_check clk_bulk_get_optional(struct device *dev, int num_clks,
struct clk_bulk_data *clks)
{
return __clk_bulk_get(dev, num_clks, clks, true);
@@ -88,8 +88,9 @@ static int __must_check of_clk_bulk_get(struct device_node *np, int num_clks,
clks[i].clk = of_clk_get(np, i);
if (IS_ERR(clks[i].clk)) {
ret = PTR_ERR(clks[i].clk);
- pr_err("%s: Failed to get clk index: %d ret: %pe\n",
- np->name, i, clks[i].clk);
+ if (ret != -EPROBE_DEFER)
+ pr_err("%s: Failed to get clk index: %d ret: %pe\n",
+ np->name, i, clks[i].clk);
clks[i].clk = NULL;
goto err;
}
@@ -140,10 +141,10 @@ void clk_bulk_put_all(int num_clks, struct clk_bulk_data *clks)
}
EXPORT_SYMBOL(clk_bulk_put_all);
-int __must_check clk_bulk_get_all(struct device_d *dev,
+int __must_check clk_bulk_get_all(struct device *dev,
struct clk_bulk_data **clks)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
if (!np)
return 0;
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 4ebdd399b4..454bfaeb0c 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -89,8 +89,16 @@ static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
if (!(hw->clk.flags & CLK_SET_RATE_NO_REPARENT) &&
mux_clk &&
- mux_clk->ops->set_rate)
+ mux_clk->ops->set_rate) {
+ /*
+ * We'll call set_rate on the mux clk which in turn results
+ * in reparenting the mux clk. Make sure the enable count
+ * (which is stored in the composite clk, not the mux clk)
+ * is transferred correctly.
+ */
+ mux_clk->enable_count = hw->clk.enable_count;
return mux_clk->ops->set_rate(clk_to_clk_hw(mux_clk), rate, parent_rate);
+ }
return 0;
}
@@ -172,13 +180,17 @@ err:
return 0;
}
-struct clk_hw *clk_hw_register_composite(struct device_d *dev,
- const char *name, const char * const *parent_names,
- int num_parents,
- struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
- struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
- struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
- unsigned long flags)
+struct clk_hw *clk_hw_register_composite(struct device *dev,
+ const char *name,
+ const char * const *parent_names,
+ int num_parents,
+ struct clk_hw *mux_hw,
+ const struct clk_ops *mux_ops,
+ struct clk_hw *rate_hw,
+ const struct clk_ops *rate_ops,
+ struct clk_hw *gate_hw,
+ const struct clk_ops *gate_ops,
+ unsigned long flags)
{
struct clk *clk;
diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c
index 0bd99993cc..ca596f2cf3 100644
--- a/drivers/clk/clk-conf.c
+++ b/drivers/clk/clk-conf.c
@@ -22,8 +22,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells");
if (num_parents == -EINVAL)
- pr_err("clk: invalid value of clock-parents property at %s\n",
- node->full_name);
+ pr_err("clk: invalid value of clock-parents property at %pOF\n",
+ node);
for (index = 0; index < num_parents; index++) {
rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
@@ -39,8 +39,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
return 0;
pclk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(pclk)) {
- pr_warn("clk: couldn't get parent clock %d for %s\n",
- index, node->full_name);
+ pr_warn("clk: couldn't get parent clock %d for %pOF\n",
+ index, node);
return PTR_ERR(pclk);
}
@@ -54,8 +54,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
}
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
- pr_warn("clk: couldn't get parent clock %d for %s\n",
- index, node->full_name);
+ pr_warn("clk: couldn't get parent clock %d for %pOF\n",
+ index, node);
rc = PTR_ERR(clk);
goto err;
}
@@ -98,8 +98,8 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
- pr_warn("clk: couldn't get clock %d for %s\n",
- index, node->full_name);
+ pr_warn("clk: couldn't get clock %d for %pOF\n",
+ index, node);
return PTR_ERR(clk);
}
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index fed9ad9d24..ccab70aecc 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -7,7 +7,7 @@
#include <common.h>
#include <io.h>
#include <malloc.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/log2.h>
#include <linux/math64.h>
@@ -88,6 +88,12 @@ unsigned long divider_recalc_rate(struct clk *clk, unsigned long parent_rate,
unsigned int div;
div = _get_div(table, val, flags, width);
+ if (!div) {
+ WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ clk->name);
+ return parent_rate;
+ }
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
}
@@ -234,6 +240,39 @@ long divider_round_rate(struct clk *clk, unsigned long rate,
return DIV_ROUND_UP(*prate, div);
}
+long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
+ unsigned long rate, unsigned long *prate,
+ const struct clk_div_table *table,
+ u8 width, unsigned long flags)
+{
+ int div;
+
+ div = clk_divider_bestdiv(&hw->clk, rate, prate, table, width, flags);
+
+ return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+EXPORT_SYMBOL_GPL(divider_round_rate_parent);
+
+long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
+ unsigned long rate, unsigned long *prate,
+ const struct clk_div_table *table, u8 width,
+ unsigned long flags, unsigned int val)
+{
+ int div;
+
+ div = _get_div(table, val, flags, width);
+
+ /* Even a read-only clock can propagate a rate change */
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
+ if (!*prate)
+ return -EINVAL;
+
+ *prate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * div);
+ }
+
+ return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
@@ -288,7 +327,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
val &= ~(clk_div_mask(divider->width) << divider->shift);
val |= value << divider->shift;
- if (clk->flags & CLK_DIVIDER_HIWORD_MASK)
+ if (divider->flags & CLK_DIVIDER_HIWORD_MASK)
val |= clk_div_mask(divider->width) << (divider->shift + 16);
writel(val, divider->reg);
@@ -410,40 +449,48 @@ struct clk *clk_divider_table(const char *name, const char *parent,
return &div->hw.clk;
}
-struct clk *clk_register_divider_table(struct device_d *dev, const char *name,
- const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
- spinlock_t *lock)
+struct clk *clk_register_divider_table(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags,
+ const struct clk_div_table *table,
+ spinlock_t *lock)
{
return clk_divider_table(name, parent_name, flags, reg, shift, width,
table, clk_divider_flags);
}
-struct clk *clk_register_divider(struct device_d *dev, const char *name,
- const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, spinlock_t *lock)
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
{
return clk_divider(name, parent_name, flags, reg, shift, width,
clk_divider_flags);
}
-struct clk_hw *clk_hw_register_divider_table(struct device_d *dev,
- const char *name, const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
- spinlock_t *lock)
+struct clk_hw *clk_hw_register_divider_table(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *reg, u8 shift,
+ u8 width,
+ u8 clk_divider_flags,
+ const struct clk_div_table *table,
+ spinlock_t *lock)
{
return clk_to_clk_hw(clk_register_divider_table(dev, xstrdup(name),
xstrdup(parent_name), flags, reg, shift, width,
clk_divider_flags, table, lock));
}
-struct clk_hw *clk_hw_register_divider(struct device_d *dev,
- const char *name, const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, spinlock_t *lock)
+struct clk_hw *clk_hw_register_divider(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
{
return clk_to_clk_hw(clk_register_divider(dev, xstrdup(name),
xstrdup(parent_name), flags, reg, shift, width,
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index a6d3fdc6a1..d2c808d40c 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -77,16 +77,20 @@ struct clk *clk_fixed_factor(const char *name,
return &f->hw.clk;
}
-struct clk *clk_register_fixed_factor(struct device_d *dev, const char *name,
- const char *parent_name, unsigned long flags,
- unsigned int mult, unsigned int div)
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ unsigned int mult, unsigned int div)
{
return clk_fixed_factor(name, parent_name, mult, div, flags);
}
-struct clk_hw *clk_hw_register_fixed_factor(struct device_d *dev,
- const char *name, const char *parent_name, unsigned long flags,
- unsigned int mult, unsigned int div)
+struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ unsigned int mult,
+ unsigned int div)
{
return clk_to_clk_hw(clk_register_fixed_factor(dev, xstrdup(name),
xstrdup(parent_name),
diff --git a/drivers/clk/clk-fixed.c b/drivers/clk/clk-fixed.c
index 9e5a07817b..6ec2feb84f 100644
--- a/drivers/clk/clk-fixed.c
+++ b/drivers/clk/clk-fixed.c
@@ -63,7 +63,7 @@ struct clk *clk_register_fixed_rate(const char *name,
return &fix->hw.clk;
}
-struct clk_hw *clk_hw_register_fixed_rate(struct device_d *dev,
+struct clk_hw *clk_hw_register_fixed_rate(struct device *dev,
const char *name, const char *parent_name,
unsigned long flags, unsigned long rate)
{
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 3cfd707238..d31920fd0b 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -117,10 +117,10 @@ struct clk *clk_gate_inverted(const char *name, const char *parent,
return clk_gate(name, parent, reg, shift, flags, CLK_GATE_SET_TO_DISABLE);
}
-struct clk *clk_register_gate(struct device_d *dev, const char *name,
- const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 bit_idx,
- u8 clk_gate_flags, spinlock_t *lock)
+struct clk *clk_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
{
return clk_gate(name, parent_name, reg, bit_idx, flags, clk_gate_flags);
}
diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c
index 6ac2e820fa..940a20523e 100644
--- a/drivers/clk/clk-gpio.c
+++ b/drivers/clk/clk-gpio.c
@@ -50,8 +50,9 @@ static struct clk_ops clk_gpio_ops = {
.is_enabled = clk_gpio_is_enabled,
};
-static int of_gpio_clk_setup(struct device_node *node)
+static int of_gpio_clk_probe(struct device *dev)
{
+ struct device_node *node = dev->device_node;
struct clk_gpio *clk_gpio;
enum of_gpio_flags of_flags;
unsigned long flags;
@@ -105,16 +106,16 @@ no_parent:
return ret;
}
-/* Can't use OF_CLK_DECLARE due to need to run after GPIOcontrollers have
- * registrered */
-
static const struct of_device_id clk_gpio_device_id[] = {
- { .compatible = "gpio-gate-clock", .data = of_gpio_clk_setup, },
+ { .compatible = "gpio-gate-clock", },
{}
};
+MODULE_DEVICE_TABLE(of, clk_gpio_device_id);
-static int clk_gpio_init(void)
-{
- return of_clk_init(NULL, clk_gpio_device_id);
-}
-coredevice_initcall(clk_gpio_init);
+static struct driver gpio_gate_clock_driver = {
+ .probe = of_gpio_clk_probe,
+ .name = "gpio-gate-clock",
+ .of_compatible = DRV_OF_COMPAT(clk_gpio_device_id),
+};
+
+core_platform_driver(gpio_gate_clock_driver);
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 8463f1ee82..1d94e09167 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -118,7 +118,7 @@ long clk_mux_round_rate(struct clk_hw *hw, unsigned long rate,
struct clk *bestparent;
if (clk->flags & CLK_SET_RATE_NO_REPARENT)
- return *prate;
+ return clk_parent_round_rate(hw, rate, prate);
bestparent = clk_mux_best_parent(clk, rate, &rrate);
@@ -135,7 +135,7 @@ static int clk_mux_set_rate(struct clk_hw *hw, unsigned long rate,
int ret;
if (clk->flags & CLK_SET_RATE_NO_REPARENT)
- return 0;
+ return clk_parent_set_rate(hw, rate, parent_rate);
parent = clk_mux_best_parent(clk, rate, &rrate);
@@ -204,21 +204,23 @@ struct clk *clk_mux(const char *name, unsigned clk_flags, void __iomem *reg,
return m;
}
-struct clk *clk_register_mux(struct device_d *dev, const char *name,
- const char * const *parent_names, u8 num_parents,
- unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_mux_flags, spinlock_t *lock)
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock)
{
return clk_mux(name, flags, reg, shift, width, parent_names,
num_parents, clk_mux_flags);
}
-struct clk_hw *__clk_hw_register_mux(struct device_d *dev,
- const char *name, u8 num_parents,
- const char * const *parent_names,
- unsigned long flags, void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+struct clk_hw *__clk_hw_register_mux(struct device *dev,
+ const char *name, u8 num_parents,
+ const char * const *parent_names,
+ unsigned long flags, void __iomem *reg,
+ u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table,
+ spinlock_t *lock)
{
struct clk_mux *mux;
struct clk_hw *hw;
diff --git a/drivers/clk/clk-qoric.c b/drivers/clk/clk-qoric.c
index f7dbf7230d..44155692a8 100644
--- a/drivers/clk/clk-qoric.c
+++ b/drivers/clk/clk-qoric.c
@@ -27,6 +27,7 @@
#define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */
#define CGB_PLL1 4
#define CGB_PLL2 5
+#define MAX_PLL_DIV 32
struct clockgen_pll_div {
struct clk_hw *hw;
@@ -34,7 +35,7 @@ struct clockgen_pll_div {
};
struct clockgen_pll {
- struct clockgen_pll_div div[8];
+ struct clockgen_pll_div div[MAX_PLL_DIV];
};
#define CLKSEL_VALID 1
@@ -143,6 +144,58 @@ static const struct clockgen_muxinfo clockgen2_cmux_cgb = {
},
};
+static const struct clockgen_muxinfo ls1028a_hwa1 = {
+ {
+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1028a_hwa2 = {
+ {
+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 },
+ {},
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1028a_hwa3 = {
+ {
+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1028a_hwa4 = {
+ {
+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 },
+ {},
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
+ },
+};
+
static const struct clockgen_muxinfo ls1043a_hwa1 = {
{
{},
@@ -203,6 +256,13 @@ static void __init t2080_init_periph(struct clockgen *cg)
cg->fman[0] = cg->hwaccel[0];
}
+static const struct clockgen_chipinfo chipinfo_ls1012a = {
+ .compat = "fsl,ls1012a-clockgen",
+ .cmux_groups = { &ls1012a_cmux },
+ .cmux_to_group = { 0, -1 },
+ .pll_mask = 0x03,
+};
+
static const struct clockgen_chipinfo chipinfo_ls1021a = {
.compat = "fsl,ls1021a-clockgen",
.cmux_groups = { &t1023_cmux },
@@ -210,6 +270,15 @@ static const struct clockgen_chipinfo chipinfo_ls1021a = {
.pll_mask = 0x03,
};
+static const struct clockgen_chipinfo chipinfo_ls1028a = {
+ .compat = "fsl,ls1028a-clockgen",
+ .cmux_groups = { &clockgen2_cmux_cga12 },
+ .hwaccel = { &ls1028a_hwa1, &ls1028a_hwa2, &ls1028a_hwa3, &ls1028a_hwa4 },
+ .cmux_to_group = { 0, 0, 0, 0, -1 },
+ .pll_mask = 0x07,
+ .flags = CG_VER3 | CG_LITTLE_ENDIAN,
+};
+
static const struct clockgen_chipinfo chipinfo_ls1043a = {
.compat = "fsl,ls1043a-clockgen",
.init_periph = t2080_init_periph,
@@ -238,13 +307,6 @@ static const struct clockgen_chipinfo chipinfo_ls1088a = {
.flags = CG_VER3 | CG_LITTLE_ENDIAN,
};
-static const struct clockgen_chipinfo chipinfo_ls1012a = {
- .compat = "fsl,ls1012a-clockgen",
- .cmux_groups = { &ls1012a_cmux },
- .cmux_to_group = { 0, -1 },
- .pll_mask = 0x03,
-};
-
static const struct clockgen_chipinfo chipinfo_ls2080a = {
.compat = "fsl,ls2080a-clockgen",
.cmux_groups = { &clockgen2_cmux_cga12, &clockgen2_cmux_cgb },
@@ -581,7 +643,7 @@ static void __init clockgen_init(struct device_node *np,
clockgen.node = np;
clockgen.regs = of_iomap(np, 0);
if (!clockgen.regs) {
- pr_err("of_iomap failed for %s\n", np->full_name);
+ pr_err("of_iomap failed for %pOF\n", np);
return;
}
@@ -592,7 +654,7 @@ static void __init clockgen_init(struct device_node *np,
pr_err("sysclk not found: %pe\n", clockgen.sysclk);
return;
}
-
+
clockgen.coreclk = of_clk_get(clockgen.node, 1);
if (IS_ERR(clockgen.coreclk))
clockgen.coreclk = NULL;
@@ -605,8 +667,8 @@ static void __init clockgen_init(struct device_node *np,
ret = of_clk_add_provider(np, clockgen_clk_get, &clockgen);
if (ret) {
- pr_err("Couldn't register clk provider for node %s: %d\n",
- np->full_name, ret);
+ pr_err("Couldn't register clk provider for node %pOF: %d\n",
+ np, ret);
}
return;
@@ -622,6 +684,11 @@ static void __maybe_unused clockgen_init_ls1021a(struct device_node *np)
clockgen_init(np, &chipinfo_ls1021a);
}
+static void __maybe_unused clockgen_init_ls1028a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls1028a);
+}
+
static void __maybe_unused clockgen_init_ls1043a(struct device_node *np)
{
clockgen_init(np, &chipinfo_ls1043a);
@@ -648,6 +715,9 @@ CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init_ls1
#ifdef CONFIG_ARCH_LS1021
CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init_ls1021a);
#endif
+#ifdef CONFIG_ARCH_LS1028
+CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1028a-clockgen", clockgen_init_ls1028a);
+#endif
#ifdef CONFIG_ARCH_LS1043
CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init_ls1043a);
#endif
diff --git a/drivers/clk/clk-rpi.c b/drivers/clk/clk-rpi.c
index 71badc04c0..d93d96a953 100644
--- a/drivers/clk/clk-rpi.c
+++ b/drivers/clk/clk-rpi.c
@@ -8,9 +8,9 @@
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/core.h>
-#include <mach/mbox.h>
-#include <mach/platform.h>
+#include <mach/bcm283x/core.h>
+#include <mach/bcm283x/mbox.h>
+#include <mach/bcm283x/platform.h>
#include <dt-bindings/clock/bcm2835.h>
#define BCM2711_CLOCK_END (BCM2711_CLOCK_EMMC2 + 1)
@@ -40,7 +40,7 @@ static struct clk *rpi_register_firmware_clock(u32 clock_id, const char *name)
return clk_fixed(name, msg->get_clock_rate.body.resp.rate_hz);
}
-static int bcm2835_cprman_init(struct device_d *dev)
+static int bcm2835_cprman_init(struct device *dev)
{
struct clk *clk_cs;
@@ -50,9 +50,9 @@ static int bcm2835_cprman_init(struct device_d *dev)
return 0;
}
-static int rpi_cprman_probe(struct device_d *dev)
+static int rpi_cprman_probe(struct device *dev)
{
- int (*init)(struct device_d *dev);
+ int (*init)(struct device *dev);
init = device_get_match_data(dev);
if (init) {
@@ -86,7 +86,7 @@ static int rpi_cprman_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = BCM2711_CLOCK_END;
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
return 0;
}
@@ -96,8 +96,9 @@ static __maybe_unused struct of_device_id bcm2835_cprman_dt_ids[] = {
{ .compatible = "brcm,bcm2711-cprman" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, bcm2835_cprman_dt_ids);
-static struct driver_d bcm2835_cprman_driver = {
+static struct driver bcm2835_cprman_driver = {
.probe = rpi_cprman_probe,
.name = "raspberrypi-cprman",
.of_compatible = DRV_OF_COMPAT(bcm2835_cprman_dt_ids),
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index 9170dba393..5c9f61ae0b 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -2,16 +2,15 @@
/*
* System Control and Power Interface (SCMI) Protocol based clock driver
*
- * Copyright (C) 2018-2021 ARM Ltd.
+ * Copyright (C) 2018-2022 ARM Ltd.
*/
-#include <common.h>
#include <linux/clk.h>
-#include <driver.h>
+#include <linux/device.h>
#include <linux/err.h>
#include <of.h>
+#include <module.h>
#include <linux/scmi_protocol.h>
-#include <linux/overflow.h>
#include <linux/math64.h>
static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
@@ -89,24 +88,57 @@ static void scmi_clk_disable(struct clk_hw *hw)
scmi_proto_clk_ops->disable(clk->ph, clk->id);
}
+static int scmi_clk_atomic_enable(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ return scmi_proto_clk_ops->enable_atomic(clk->ph, clk->id);
+}
+
+static void scmi_clk_atomic_disable(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ scmi_proto_clk_ops->disable_atomic(clk->ph, clk->id);
+}
+
+/*
+ * We can provide enable/disable atomic callbacks only if the underlying SCMI
+ * transport for an SCMI instance is configured to handle SCMI commands in an
+ * atomic manner.
+ *
+ * When no SCMI atomic transport support is available we instead provide only
+ * the prepare/unprepare API, as allowed by the clock framework when atomic
+ * calls are not available.
+ *
+ * Two distinct sets of clk_ops are provided since we could have multiple SCMI
+ * instances with different underlying transport quality, so they cannot be
+ * shared.
+ */
static const struct clk_ops scmi_clk_ops = {
.recalc_rate = scmi_clk_recalc_rate,
.round_rate = scmi_clk_round_rate,
.set_rate = scmi_clk_set_rate,
- /*
- * Unlike Linux, we can provide enable/disable callback as everything
- * runs in atomic context.
- */
.enable = scmi_clk_enable,
.disable = scmi_clk_disable,
};
-static int scmi_clk_ops_init(struct device_d *dev, struct scmi_clk *sclk)
+static const struct clk_ops scmi_atomic_clk_ops = {
+ .recalc_rate = scmi_clk_recalc_rate,
+ .round_rate = scmi_clk_round_rate,
+ .set_rate = scmi_clk_set_rate,
+ .enable = scmi_clk_atomic_enable,
+ .disable = scmi_clk_atomic_disable,
+};
+
+static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
+ const struct clk_ops *scmi_ops)
{
struct clk_init_data init = {
.flags = CLK_GET_RATE_NOCACHE,
+
.num_parents = 0,
- .ops = &scmi_clk_ops,
+ .ops = scmi_ops,
.name = sclk->info->name,
};
@@ -117,10 +149,12 @@ static int scmi_clk_ops_init(struct device_d *dev, struct scmi_clk *sclk)
static int scmi_clocks_probe(struct scmi_device *sdev)
{
int idx, count, err;
- struct clk **clks;
- struct clk_onecell_data *clk_data;
- struct device_d *dev = &sdev->dev;
- struct device_node *np = dev->device_node;
+ unsigned int atomic_threshold;
+ bool is_atomic;
+ struct clk_hw **hws;
+ struct clk_hw_onecell_data *clk_data;
+ struct device *dev = &sdev->dev;
+ struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
struct scmi_protocol_handle *ph;
@@ -128,7 +162,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
return -ENODEV;
scmi_proto_clk_ops =
- handle->protocol_get(sdev, SCMI_PROTOCOL_CLOCK, &ph);
+ handle->dev_protocol_get(sdev, SCMI_PROTOCOL_CLOCK, &ph);
if (IS_ERR(scmi_proto_clk_ops))
return PTR_ERR(scmi_proto_clk_ops);
@@ -138,17 +172,21 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
return -EINVAL;
}
- clk_data = kzalloc(sizeof (*clk_data), GFP_KERNEL);
+ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, count),
+ GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
- clk_data->clk_num = count;
- clks = clk_data->clks = calloc(clk_data->clk_num, sizeof(struct clk *));
+ clk_data->num = count;
+ hws = clk_data->hws;
+
+ is_atomic = handle->is_transport_atomic(handle, &atomic_threshold);
for (idx = 0; idx < count; idx++) {
struct scmi_clk *sclk;
+ const struct clk_ops *scmi_ops;
- sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
+ sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
if (!sclk)
return -ENOMEM;
@@ -161,24 +199,39 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
sclk->id = idx;
sclk->ph = ph;
- err = scmi_clk_ops_init(dev, sclk);
+ /*
+ * Note that when transport is atomic but SCMI protocol did not
+ * specify (or support) an enable_latency associated with a
+ * clock, we default to use atomic operations mode.
+ */
+ if (is_atomic &&
+ sclk->info->enable_latency <= atomic_threshold)
+ scmi_ops = &scmi_atomic_clk_ops;
+ else
+ scmi_ops = &scmi_clk_ops;
+
+ err = scmi_clk_ops_init(dev, sclk, scmi_ops);
if (err) {
dev_err(dev, "failed to register clock %d\n", idx);
- kfree(sclk);
- clks[idx] = NULL;
+ devm_kfree(dev, sclk);
+ hws[idx] = NULL;
} else {
- dev_dbg(dev, "Registered clock:%s\n", sclk->info->name);
- clks[idx] = &sclk->hw.clk;
+ dev_dbg(dev, "Registered clock:%s%s\n",
+ sclk->info->name,
+ scmi_ops == &scmi_atomic_clk_ops ?
+ " (atomic ops)" : "");
+ hws[idx] = &sclk->hw;
}
}
- return of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, clk_data);
+ return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_CLOCK, "clocks" },
{ },
};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_clocks_driver = {
.name = "scmi-clocks",
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 4611038f4b..d6ccfa6d15 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -12,7 +12,7 @@
#include <of_address.h>
#include <linux/math64.h>
#include <linux/iopoll.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
/*
@@ -457,7 +457,7 @@ static const struct clk_ops clk_apb_mul_factor_ops = {
.recalc_rate = clk_apb_mul_recalc_rate,
};
-static struct clk *clk_register_apb_mul(struct device_d *dev, const char *name,
+static struct clk *clk_register_apb_mul(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, u8 bit_idx)
{
@@ -952,10 +952,12 @@ static const struct clk_ops rgclk_ops = {
.is_enabled = rgclk_is_enabled,
};
-static struct clk_hw *clk_register_rgate(struct device_d *dev, const char *name,
- const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 bit_idx, u8 bit_rdy_idx,
- u8 clk_gate_flags, spinlock_t *lock)
+static struct clk_hw *clk_register_rgate(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 bit_rdy_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
{
struct stm32_rgate *rgate;
struct clk_init_data init = { NULL };
@@ -1047,10 +1049,13 @@ static const struct clk_ops cclk_mux_ops = {
.set_parent = cclk_mux_set_parent,
};
-static struct clk_hw *stm32_register_cclk(struct device_d *dev, const char *name,
- const char * const *parent_names, int num_parents,
- void __iomem *reg, u8 bit_idx, u8 shift, unsigned long flags,
- spinlock_t *lock)
+static struct clk_hw *stm32_register_cclk(struct device *dev,
+ const char *name,
+ const char * const *parent_names,
+ int num_parents,
+ void __iomem *reg, u8 bit_idx,
+ u8 shift, unsigned long flags,
+ spinlock_t *lock)
{
struct clk_hw *hw;
struct clk_gate *gate;
@@ -1616,6 +1621,7 @@ static const struct of_device_id stm32f4_of_match[] = {
},
{}
};
+MODULE_DEVICE_TABLE(of, stm32f4_of_match);
static struct clk_hw *stm32_register_aux_clk(const char *name,
const char * const *parent_names, int num_parents,
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c
index c4b03e9f6d..9ea4c0b830 100644
--- a/drivers/clk/clk-stm32mp1.c
+++ b/drivers/clk/clk-stm32mp1.c
@@ -320,7 +320,7 @@ struct clock_config {
int num_parents;
unsigned long flags;
void *cfg;
- struct clk_hw * (*func)(struct device_d *dev,
+ struct clk_hw * (*func)(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg);
@@ -380,7 +380,7 @@ struct stm32_composite_cfg {
};
static struct clk_hw *
-_clk_hw_register_gate(struct device_d *dev,
+_clk_hw_register_gate(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -398,7 +398,7 @@ _clk_hw_register_gate(struct device_d *dev,
}
static struct clk_hw *
-_clk_hw_register_fixed_factor(struct device_d *dev,
+_clk_hw_register_fixed_factor(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -411,7 +411,7 @@ _clk_hw_register_fixed_factor(struct device_d *dev,
}
static struct clk_hw *
-_clk_hw_register_divider_table(struct device_d *dev,
+_clk_hw_register_divider_table(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -431,7 +431,7 @@ _clk_hw_register_divider_table(struct device_d *dev,
}
static struct clk_hw *
-_clk_hw_register_mux(struct device_d *dev,
+_clk_hw_register_mux(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -472,7 +472,7 @@ static const struct clk_ops mp1_gate_clk_ops = {
.is_enabled = clk_gate_is_enabled,
};
-static struct clk_hw *_get_stm32_mux(struct device_d *dev, void __iomem *base,
+static struct clk_hw *_get_stm32_mux(struct device *dev, void __iomem *base,
const struct stm32_mux_cfg *cfg,
spinlock_t *lock)
{
@@ -512,7 +512,7 @@ static struct clk_hw *_get_stm32_mux(struct device_d *dev, void __iomem *base,
return mux_hw;
}
-static struct clk_hw *_get_stm32_div(struct device_d *dev, void __iomem *base,
+static struct clk_hw *_get_stm32_div(struct device *dev, void __iomem *base,
const struct stm32_div_cfg *cfg,
spinlock_t *lock)
{
@@ -533,7 +533,7 @@ static struct clk_hw *_get_stm32_div(struct device_d *dev, void __iomem *base,
return &div->hw;
}
-static struct clk_hw *_get_stm32_gate(struct device_d *dev, void __iomem *base,
+static struct clk_hw *_get_stm32_gate(struct device *dev, void __iomem *base,
const struct stm32_gate_cfg *cfg,
spinlock_t *lock)
{
@@ -573,7 +573,7 @@ static struct clk_hw *_get_stm32_gate(struct device_d *dev, void __iomem *base,
}
static struct clk_hw *
-clk_stm32_register_gate_ops(struct device_d *dev,
+clk_stm32_register_gate_ops(struct device *dev,
const char *name,
const char *parent_name,
unsigned long flags,
@@ -609,7 +609,7 @@ clk_stm32_register_gate_ops(struct device_d *dev,
}
static struct clk_hw *
-clk_stm32_register_composite(struct device_d *dev,
+clk_stm32_register_composite(struct device *dev,
const char *name, const char * const *parent_names,
int num_parents, void __iomem *base,
const struct stm32_composite_cfg *cfg,
@@ -852,7 +852,7 @@ static const struct clk_ops pll_ops = {
.get_parent = pll_get_parent,
};
-static struct clk_hw *clk_register_pll(struct device_d *dev, const char *name,
+static struct clk_hw *clk_register_pll(struct device *dev, const char *name,
const char * const *parent_names,
int num_parents,
void __iomem *reg,
@@ -989,7 +989,7 @@ static const struct clk_ops timer_ker_ops = {
};
-static struct clk_hw *clk_register_cktim(struct device_d *dev, const char *name,
+static struct clk_hw *clk_register_cktim(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *apbdiv,
@@ -1030,7 +1030,7 @@ struct stm32_pll_cfg {
u32 muxoff;
};
-static struct clk_hw *_clk_register_pll(struct device_d *dev,
+static struct clk_hw *_clk_register_pll(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -1049,7 +1049,7 @@ struct stm32_cktim_cfg {
u32 offset_timpre;
};
-static struct clk_hw *_clk_register_cktim(struct device_d *dev,
+static struct clk_hw *_clk_register_cktim(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -1062,7 +1062,7 @@ static struct clk_hw *_clk_register_cktim(struct device_d *dev,
}
static struct clk_hw *
-_clk_stm32_register_gate(struct device_d *dev,
+_clk_stm32_register_gate(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -1077,7 +1077,7 @@ _clk_stm32_register_gate(struct device_d *dev,
}
static struct clk_hw *
-_clk_stm32_register_composite(struct device_d *dev,
+_clk_stm32_register_composite(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -2067,8 +2067,9 @@ static const struct of_device_id stm32mp1_match_data[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, stm32mp1_match_data);
-static int stm32_register_hw_clk(struct device_d *dev,
+static int stm32_register_hw_clk(struct device *dev,
struct clk_hw_onecell_data *clk_data,
void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
@@ -2178,7 +2179,7 @@ static const struct reset_control_ops stm32_reset_ops = {
.status = stm32_reset_status,
};
-static int stm32_rcc_reset_init(struct device_d *dev, void __iomem *base,
+static int stm32_rcc_reset_init(struct device *dev, void __iomem *base,
const struct of_device_id *match)
{
const struct stm32_rcc_match_data *data = match->data;
@@ -2197,7 +2198,7 @@ static int stm32_rcc_reset_init(struct device_d *dev, void __iomem *base,
return reset_controller_register(&reset_data->rcdev);
}
-static int stm32_rcc_clock_init(struct device_d *dev, void __iomem *base,
+static int stm32_rcc_clock_init(struct device *dev, void __iomem *base,
const struct of_device_id *match)
{
const struct stm32_rcc_match_data *data = match->data;
@@ -2236,7 +2237,7 @@ static int stm32_rcc_clock_init(struct device_d *dev, void __iomem *base,
return of_clk_add_hw_provider(dev_of_node(dev), of_clk_hw_onecell_get, clk_data);
}
-static int stm32_rcc_init(struct device_d *dev, void __iomem *base,
+static int stm32_rcc_init(struct device *dev, void __iomem *base,
const struct of_device_id *match_data)
{
const struct of_device_id *match;
@@ -2265,7 +2266,7 @@ static int stm32_rcc_init(struct device_d *dev, void __iomem *base,
return 0;
}
-static int stm32mp1_rcc_init(struct device_d *dev)
+static int stm32mp1_rcc_init(struct device *dev)
{
void __iomem *base;
int ret;
@@ -2280,11 +2281,11 @@ static int stm32mp1_rcc_init(struct device_d *dev)
if (ret)
return ret;
- stm32mp_system_restart_init(base);
+ stm32mp_system_restart_init(dev);
return 0;
}
-static int get_clock_deps(struct device_d *dev)
+static int get_clock_deps(struct device *dev)
{
static const char * const clock_deps_name[] = {
"hsi", "hse", "csi", "lsi", "lse",
@@ -2314,7 +2315,7 @@ static int get_clock_deps(struct device_d *dev)
return 0;
}
-static int stm32mp1_rcc_clocks_probe(struct device_d *dev)
+static int stm32mp1_rcc_clocks_probe(struct device *dev)
{
int ret = get_clock_deps(dev);
@@ -2324,7 +2325,7 @@ static int stm32mp1_rcc_clocks_probe(struct device_d *dev)
return ret;
}
-static void stm32mp1_rcc_clocks_remove(struct device_d *dev)
+static void stm32mp1_rcc_clocks_remove(struct device *dev)
{
struct device_node *child, *np = dev_of_node(dev);
@@ -2332,7 +2333,7 @@ static void stm32mp1_rcc_clocks_remove(struct device_d *dev)
of_clk_del_provider(child);
}
-static struct driver_d stm32mp1_rcc_clocks_driver = {
+static struct driver stm32mp1_rcc_clocks_driver = {
.name = "stm32mp1_rcc",
.of_compatible = stm32mp1_match_data,
.probe = stm32mp1_rcc_clocks_probe,
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 52e309e877..bd36878280 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -211,6 +211,47 @@ int clk_hw_set_rate(struct clk_hw *hw, unsigned long rate)
return clk_set_rate(&hw->clk, rate);
}
+static int clk_fetch_parent_index(struct clk *clk,
+ struct clk *parent)
+{
+ int i;
+
+ if (!parent)
+ return -EINVAL;
+
+ for (i = 0; i < clk->num_parents; i++) {
+ if (IS_ERR_OR_NULL(clk->parents[i]))
+ clk->parents[i] = clk_lookup(clk->parent_names[i]);
+
+ if (!IS_ERR_OR_NULL(clk->parents[i]))
+ if (clk->parents[i] == parent)
+ break;
+ }
+
+ if (i == clk->num_parents)
+ return -EINVAL;
+
+ return i;
+}
+
+/**
+ * clk_hw_get_parent_index - return the index of the parent clock
+ * @hw: clk_hw associated with the clk being consumed
+ *
+ * Fetches and returns the index of parent clock. Returns -EINVAL if the given
+ * clock does not have a current parent.
+ */
+int clk_hw_get_parent_index(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+
+ if (WARN_ON(parent == NULL))
+ return -EINVAL;
+
+ return clk_fetch_parent_index(clk_hw_to_clk(hw), clk_hw_to_clk(parent));
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_parent_index);
+
struct clk *clk_lookup(const char *name)
{
struct clk *c;
@@ -245,17 +286,9 @@ int clk_set_parent(struct clk *clk, struct clk *newparent)
if (!clk->ops->set_parent)
return -EINVAL;
- for (i = 0; i < clk->num_parents; i++) {
- if (IS_ERR_OR_NULL(clk->parents[i]))
- clk->parents[i] = clk_lookup(clk->parent_names[i]);
-
- if (!IS_ERR_OR_NULL(clk->parents[i]))
- if (clk->parents[i] == newparent)
- break;
- }
-
- if (i == clk->num_parents)
- return -EINVAL;
+ i = clk_fetch_parent_index(clk, newparent);
+ if (i < 0)
+ return i;
if (clk->enable_count)
clk_enable(newparent);
@@ -285,6 +318,26 @@ int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *newparent)
return clk_set_parent(&hw->clk, &newparent->clk);
}
+static struct clk *clk_get_parent_by_index(struct clk *clk, u8 idx)
+{
+ if (IS_ERR_OR_NULL(clk->parents[idx]))
+ clk->parents[idx] = clk_lookup(clk->parent_names[idx]);
+
+ return clk->parents[idx];
+}
+
+struct clk_hw *
+clk_hw_get_parent_by_index(const struct clk_hw *hw, unsigned int idx)
+{
+ struct clk *clk = clk_hw_to_clk(hw);
+
+ if (!clk || idx >= clk->num_parents || !clk->parents)
+ return NULL;
+
+ return clk_to_clk_hw(clk_get_parent_by_index(clk, idx));
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_parent_by_index);
+
struct clk *clk_get_parent(struct clk *clk)
{
struct clk_hw *hw;
@@ -310,10 +363,7 @@ struct clk *clk_get_parent(struct clk *clk)
idx = 0;
}
- if (IS_ERR_OR_NULL(clk->parents[idx]))
- clk->parents[idx] = clk_lookup(clk->parent_names[idx]);
-
- return clk->parents[idx];
+ return clk_get_parent_by_index(clk, idx);
}
struct clk_hw *clk_hw_get_parent(struct clk_hw *hw)
@@ -381,7 +431,7 @@ int clk_get_phase(struct clk *clk)
return ret;
}
-int bclk_register(struct clk *clk)
+static int __bclk_register(struct clk *clk)
{
struct clk_hw *hw = clk_to_clk_hw(clk);
struct clk *c;
@@ -395,8 +445,6 @@ int bclk_register(struct clk *clk)
}
}
- clk->parents = xzalloc(sizeof(struct clk *) * clk->num_parents);
-
list_add_tail(&clk->list, &clks);
if (clk->ops->init) {
@@ -411,16 +459,28 @@ int bclk_register(struct clk *clk)
return 0;
out:
list_del(&clk->list);
- free(clk->parents);
return ret;
}
-struct clk *clk_register(struct device_d *dev, struct clk_hw *hw)
+int bclk_register(struct clk *clk)
+{
+ int ret;
+
+ clk->parents = xzalloc(sizeof(struct clk *) * clk->num_parents);
+
+ ret = __bclk_register(clk);
+ if (ret)
+ free(clk->parents);
+
+ return ret;
+}
+
+struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
struct clk *clk;
const struct clk_init_data *init = hw->init;
- char **parent_names;
+ char **parent_names = NULL;
int i, ret;
if (!hw->init)
@@ -433,20 +493,32 @@ struct clk *clk_register(struct device_d *dev, struct clk_hw *hw)
clk->name = xstrdup(init->name);
clk->ops = init->ops;
clk->num_parents = init->num_parents;
- parent_names = xzalloc(init->num_parents * sizeof(char *));
- for (i = 0; i < init->num_parents; i++)
- parent_names[i] = xstrdup(init->parent_names[i]);
+ clk->parents = xzalloc(sizeof(struct clk *) * clk->num_parents);
+
+ if (init->parent_names) {
+ parent_names = xzalloc(init->num_parents * sizeof(char *));
+
+ for (i = 0; i < init->num_parents; i++)
+ parent_names[i] = xstrdup(init->parent_names[i]);
- clk->parent_names = (const char *const*)parent_names;
+ clk->parent_names = (const char *const*)parent_names;
+
+ } else {
+ for (i = 0; i < init->num_parents; i++)
+ clk->parents[i] = clk_hw_to_clk(init->parent_hws[i]);
+ }
clk->flags = init->flags;
- ret = bclk_register(clk);
+ ret = __bclk_register(clk);
if (ret) {
- for (i = 0; i < init->num_parents; i++)
- free(parent_names[i]);
- free(parent_names);
+ if (parent_names) {
+ for (i = 0; i < init->num_parents; i++)
+ free(parent_names[i]);
+ free(parent_names);
+ }
+ free(clk->parents);
return ERR_PTR(ret);
}
@@ -569,8 +641,7 @@ struct of_clk_provider {
};
extern struct of_device_id __clk_of_table_start[];
-const struct of_device_id __clk_of_table_sentinel
- __attribute__ ((unused,section (".__clk_of_table_end")));
+const struct of_device_id __clk_of_table_sentinel __ll_elem(.__clk_of_table_end);
static LIST_HEAD(of_clk_providers);
@@ -636,7 +707,7 @@ static int __of_clk_add_provider(struct device_node *np,
cp->get_hw = clk_hw_src_get;
list_add(&cp->link, &of_clk_providers);
- pr_debug("Added clock from %s\n", np ? np->full_name : "<none>");
+ pr_debug("Added clock from %pOF\n", np);
of_clk_set_defaults(np, true);
@@ -847,18 +918,30 @@ static int parent_ready(struct device_node *np)
}
}
+static LIST_HEAD(probed_clks);
+
+static bool of_clk_probed(struct device_node *np)
+{
+ struct clock_provider *clk_provider;
+
+ list_for_each_entry(clk_provider, &probed_clks, node)
+ if (clk_provider->np == np)
+ return true;
+ return false;
+}
+
/**
* of_clk_init() - Scan and init clock providers from the DT
- * @root: parent of the first level to probe or NULL for the root of the tree
- * @matches: array of compatible values and init functions for providers.
*
* This function scans the device tree for matching clock providers and
* calls their initialization functions
*
* Returns 0 on success, < 0 on failure.
*/
-int of_clk_init(struct device_node *root, const struct of_device_id *matches)
+int of_clk_init(void)
{
+ struct device_node *root = of_get_root_node();
+ const struct of_device_id *matches = __clk_of_table_start;
struct clock_provider *clk_provider, *next;
bool is_init_done;
bool force = false;
@@ -866,11 +949,7 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches)
const struct of_device_id *match;
if (!root)
- root = of_find_node_by_path("/");
- if (!root)
return -EINVAL;
- if (!matches)
- matches = __clk_of_table_start;
/* First prepare the list of the clocks providers */
for_each_matching_node_and_match(root, matches, &match) {
@@ -879,6 +958,11 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches)
if (!of_device_is_available(root))
continue;
+ if (of_clk_probed(root)) {
+ pr_debug("%s: already probed: %pOF\n", __func__, root);
+ continue;
+ }
+
parent = xzalloc(sizeof(*parent));
parent->clk_init_cb = match->data;
@@ -898,8 +982,7 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches)
clk_provider->clk_init_cb(np);
of_clk_set_defaults(np, true);
- list_del(&clk_provider->node);
- free(clk_provider);
+ list_move_tail(&clk_provider->node, &probed_clks);
is_init_done = true;
}
}
@@ -935,7 +1018,16 @@ static const char *clk_hw_stat(struct clk *clk)
return "unknown";
}
-static void dump_one(struct clk *clk, int verbose, int indent)
+static const char *clk_parent_name_by_index(struct clk *clk, u8 idx)
+{
+ if (clk->parent_names)
+ return clk->parent_names[idx];
+ if (clk->parents[idx])
+ return clk->parents[idx]->name;
+ return "unknown";
+}
+
+static void dump_one_summary(struct clk *clk, int flags, int indent)
{
int enabled = clk_is_enabled(clk);
const char *hwstat, *stat;
@@ -953,45 +1045,84 @@ static void dump_one(struct clk *clk, int verbose, int indent)
clk->enable_count,
hwstat);
- if (verbose) {
+ if (flags & CLK_DUMP_VERBOSE) {
if (clk->num_parents > 1) {
int i;
printf("%*s`---- possible parents: ", indent * 4, "");
for (i = 0; i < clk->num_parents; i++)
- printf("%s ", clk->parent_names[i]);
+ printf("%s ", clk_parent_name_by_index(clk, i));
printf("\n");
}
}
}
-static void dump_subtree(struct clk *clk, int verbose, int indent)
+static void dump_one_json(struct clk *clk, int flags, int indent)
+{
+ printf("\"%s\": { \"rate\": %lu,\"enable_count\": %d",
+ clk->name,
+ clk_get_rate(clk),
+ clk->enable_count);
+}
+
+static void dump_one(struct clk *clk, int flags, int indent)
+{
+ if (flags & CLK_DUMP_JSON)
+ dump_one_json(clk, flags, indent);
+ else
+ dump_one_summary(clk, flags, indent);
+}
+
+static inline bool json_puts(const char *str, int flags)
+{
+ if (flags & CLK_DUMP_JSON) {
+ puts(str);
+ return true;
+ }
+
+ return false;
+}
+
+static void dump_subtree(struct clk *clk, int flags, int indent)
{
struct clk *c;
- dump_one(clk, verbose, indent);
+ dump_one(clk, flags, indent);
list_for_each_entry(c, &clks, list) {
struct clk *parent = clk_get_parent(c);
- if (parent == clk)
- dump_subtree(c, verbose, indent + 1);
+ if (parent == clk) {
+ json_puts(",", flags);
+ dump_subtree(c, flags, indent + 1);
+ }
}
+
+ json_puts("}", flags);
}
-void clk_dump(int verbose)
+void clk_dump(int flags)
{
+ bool first_node = true;
struct clk *c;
+ json_puts("{", flags);
+
list_for_each_entry(c, &clks, list) {
struct clk *parent = clk_get_parent(c);
- if (IS_ERR_OR_NULL(parent))
- dump_subtree(c, verbose, 0);
+ if (IS_ERR_OR_NULL(parent)) {
+ if (!first_node)
+ json_puts(",", flags);
+ first_node = false;
+ dump_subtree(c, flags, 0);
+ }
}
+
+ json_puts("}\n", flags);
}
-static int clk_print_parent(struct clk *clk, int verbose)
+static int clk_print_parent(struct clk *clk, int flags)
{
struct clk *c;
int indent;
@@ -1000,30 +1131,38 @@ static int clk_print_parent(struct clk *clk, int verbose)
if (IS_ERR_OR_NULL(c))
return 0;
- indent = clk_print_parent(c, verbose);
+ indent = clk_print_parent(c, flags);
- dump_one(c, verbose, indent);
+ dump_one(c, flags, indent);
return indent + 1;
}
-void clk_dump_one(struct clk *clk, int verbose)
+void clk_dump_one(struct clk *clk, int flags)
{
- int indent;
+ int indent = 0;
struct clk *c;
- indent = clk_print_parent(clk, verbose);
+ if (json_puts("{", flags)) {
+ dump_one(clk, flags, indent);
+ } else {
+ indent = clk_print_parent(clk, flags);
- printf("\033[1m");
- dump_one(clk, verbose, indent);
- printf("\033[0m");
+ printf("\033[1m");
+ dump_one(clk, flags, indent);
+ printf("\033[0m");
+ }
list_for_each_entry(c, &clks, list) {
struct clk *parent = clk_get_parent(c);
- if (parent == clk)
- dump_subtree(c, verbose, indent + 1);
+ if (parent == clk) {
+ json_puts(",", flags);
+ dump_subtree(c, flags, indent + 1);
+ }
}
+
+ json_puts("}}\n", flags);
}
int clk_name_complete(struct string_list *sl, char *instr)
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 462a7e16ef..dbe998b6af 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -70,8 +70,8 @@ struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
if (!IS_ERR(clk))
break;
else if (name && index >= 0) {
- pr_err("ERROR: could not get clock %s:%s(%i)\n",
- np->full_name, name ? name : "", index);
+ pr_err("ERROR: could not get clock %pOF:%s(%i)\n",
+ np, name ? name : "", index);
return clk;
}
@@ -129,7 +129,7 @@ static struct clk *clk_find(const char *dev_id, const char *con_id)
return clk;
}
-static struct clk *clk_find_physbase(struct device_d *dev, const char *con_id)
+static struct clk *clk_find_physbase(struct device *dev, const char *con_id)
{
struct clk_lookup *p;
unsigned long start;
@@ -167,7 +167,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
}
EXPORT_SYMBOL(clk_get_sys);
-struct clk *clk_get(struct device_d *dev, const char *con_id)
+struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
struct clk *clk;
@@ -176,9 +176,9 @@ struct clk *clk_get(struct device_d *dev, const char *con_id)
if (!IS_ERR(clk))
return clk;
- if (dev) {
- clk = of_clk_get_by_name(dev->device_node, con_id);
- if (!IS_ERR(clk) || PTR_ERR(clk) != -ENOENT)
+ if (dev && dev->of_node) {
+ clk = of_clk_get_by_name(dev->of_node, con_id);
+ if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
return clk;
}
@@ -265,7 +265,7 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
}
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
- struct device_d *dev)
+ struct device *dev)
{
struct clk *r = clk_get(dev, id);
struct clk_lookup *l;
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 4a792422d5..eb9f8334c3 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -34,4 +34,5 @@ obj-$(CONFIG_ARCH_IMX8MM) += clk-imx8mm.o
obj-$(CONFIG_ARCH_IMX8MN) += clk-imx8mn.o
obj-$(CONFIG_ARCH_IMX8MP) += clk-imx8mp.o
obj-$(CONFIG_ARCH_IMX8MQ) += clk-imx8mq.o
+obj-$(CONFIG_ARCH_IMX93) += clk-imx93.o clk-composite-93.o clk-gate-93.o clk-fracn-gppll.o
obj-$(CONFIG_ARCH_VF610) += clk-vf610.o
diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c
index 72534117f2..04d83d208b 100644
--- a/drivers/clk/imx/clk-composite-8m.c
+++ b/drivers/clk/imx/clk-composite-8m.c
@@ -158,40 +158,43 @@ struct clk *imx8m_clk_composite_flags(const char *name,
u32 composite_flags,
unsigned long flags)
{
- struct clk *comp = ERR_PTR(-ENOMEM);
+ struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
+ struct clk_hw *div_hw, *gate_hw = NULL;
struct clk_divider *div = NULL;
struct clk_gate *gate = NULL;
struct clk_mux *mux = NULL;
+ const struct clk_ops *divider_ops;
const struct clk_ops *mux_ops;
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
goto fail;
+ mux_hw = &mux->hw;
mux->reg = reg;
mux->shift = PCG_PCS_SHIFT;
mux->width = PCG_PCS_WIDTH;
- mux->hw.clk.ops = &clk_mux_ops;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
goto fail;
+ div_hw = &div->hw;
div->reg = reg;
if (composite_flags & IMX_COMPOSITE_CORE) {
div->shift = PCG_DIV_SHIFT;
div->width = PCG_CORE_DIV_WIDTH;
- div->hw.clk.ops = &clk_divider_ops;
+ divider_ops = &clk_divider_ops;
mux_ops = &imx8m_clk_composite_mux_ops;
} else if (composite_flags & IMX_COMPOSITE_BUS) {
div->shift = PCG_PREDIV_SHIFT;
div->width = PCG_PREDIV_WIDTH;
- div->hw.clk.ops = &imx8m_clk_composite_divider_ops;
+ divider_ops = &imx8m_clk_composite_divider_ops;
mux_ops = &imx8m_clk_composite_mux_ops;
} else {
div->shift = PCG_PREDIV_SHIFT;
div->width = PCG_PREDIV_WIDTH;
- div->hw.clk.ops = &imx8m_clk_composite_divider_ops;
+ divider_ops = &imx8m_clk_composite_divider_ops;
mux_ops = &clk_mux_ops;
}
@@ -199,20 +202,21 @@ struct clk *imx8m_clk_composite_flags(const char *name,
if (!gate)
goto fail;
+ gate_hw = &gate->hw;
gate->reg = reg;
gate->shift = PCG_CGC_SHIFT;
- gate->hw.clk.ops = &clk_gate_ops;
- comp = clk_register_composite(name, parent_names, num_parents,
- &mux->hw.clk, &div->hw.clk, &gate->hw.clk, flags);
- if (IS_ERR(comp))
+ hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
+ mux_hw, mux_ops, div_hw,
+ divider_ops, gate_hw, &clk_gate_ops, flags);
+ if (IS_ERR(hw))
goto fail;
- return comp;
+ return clk_hw_to_clk(hw);
fail:
kfree(gate);
kfree(div);
kfree(mux);
- return ERR_CAST(comp);
+ return ERR_CAST(hw);
}
diff --git a/drivers/clk/imx/clk-composite-93.c b/drivers/clk/imx/clk-composite-93.c
new file mode 100644
index 0000000000..2b3753d569
--- /dev/null
+++ b/drivers/clk/imx/clk-composite-93.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <of_address.h>
+#include <linux/iopoll.h>
+
+#include "clk.h"
+
+#define TIMEOUT_US 500U
+
+#define CCM_DIV_SHIFT 0
+#define CCM_DIV_WIDTH 8
+#define CCM_MUX_SHIFT 8
+#define CCM_MUX_MASK 3
+#define CCM_OFF_SHIFT 24
+#define CCM_BUSY_SHIFT 28
+
+#define STAT_OFFSET 0x4
+#define AUTHEN_OFFSET 0x30
+#define TZ_NS_SHIFT 9
+#define TZ_NS_MASK BIT(9)
+
+#define WHITE_LIST_SHIFT 16
+
+static int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg)
+{
+ int ret;
+ u32 val;
+
+ ret = readl_poll_timeout(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)),
+ TIMEOUT_US);
+ if (ret)
+ pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw));
+
+ return ret;
+}
+
+static void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ u32 reg;
+
+ reg = readl(gate->reg);
+
+ if (enable)
+ reg &= ~BIT(gate->shift);
+ else
+ reg |= BIT(gate->shift);
+
+ writel(reg, gate->reg);
+
+ imx93_clk_composite_wait_ready(hw, gate->reg);
+}
+
+static int imx93_clk_composite_gate_enable(struct clk_hw *hw)
+{
+ imx93_clk_composite_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+static void imx93_clk_composite_gate_disable(struct clk_hw *hw)
+{
+ imx93_clk_composite_gate_endisable(hw, 0);
+}
+
+static const struct clk_ops imx93_clk_composite_gate_ops = {
+ .enable = imx93_clk_composite_gate_enable,
+ .disable = imx93_clk_composite_gate_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+
+static unsigned long
+imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ return clk_divider_ops.recalc_rate(hw, parent_rate);
+}
+
+static long
+imx93_clk_composite_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
+{
+ return clk_divider_ops.round_rate(hw, rate, prate);
+}
+
+static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ int value;
+ u32 val;
+ int ret;
+
+ value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags);
+ if (value < 0)
+ return value;
+
+ val = readl(divider->reg);
+ val &= ~(clk_div_mask(divider->width) << divider->shift);
+ val |= (u32)value << divider->shift;
+ writel(val, divider->reg);
+
+ ret = imx93_clk_composite_wait_ready(hw, divider->reg);
+
+ return ret;
+}
+
+static const struct clk_ops imx93_clk_composite_divider_ops = {
+ .recalc_rate = imx93_clk_composite_divider_recalc_rate,
+ .round_rate = imx93_clk_composite_divider_round_rate,
+ .set_rate = imx93_clk_composite_divider_set_rate,
+};
+
+static int imx93_clk_composite_mux_get_parent(struct clk_hw *hw)
+{
+ return clk_mux_ops.get_parent(hw);
+}
+
+static int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
+ u32 reg;
+ int ret;
+
+ reg = readl(mux->reg);
+ reg &= ~(((1 << mux->width) - 1) << mux->shift);
+ val = val << mux->shift;
+ reg |= val;
+ writel(reg, mux->reg);
+
+ ret = imx93_clk_composite_wait_ready(hw, mux->reg);
+
+ return ret;
+}
+
+static const struct clk_ops imx93_clk_composite_mux_ops = {
+ .get_parent = imx93_clk_composite_mux_get_parent,
+ .set_parent = imx93_clk_composite_mux_set_parent,
+};
+
+struct clk *imx93_clk_composite_flags(const char *name, const char * const *parent_names,
+ int num_parents, void __iomem *reg, u32 domain_id,
+ unsigned long flags)
+{
+ struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
+ struct clk_hw *div_hw, *gate_hw;
+ struct clk_divider *div = NULL;
+ struct clk_gate *gate = NULL;
+ struct clk_mux *mux = NULL;
+ bool clk_ro = false;
+ u32 authen;
+
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ goto fail;
+
+ mux_hw = &mux->hw;
+ mux->reg = reg;
+ mux->shift = CCM_MUX_SHIFT;
+ mux->width = 2;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ goto fail;
+
+ div_hw = &div->hw;
+ div->reg = reg;
+ div->shift = CCM_DIV_SHIFT;
+ div->width = CCM_DIV_WIDTH;
+// div->flags = CLK_DIVIDER_ROUND_CLOSEST;
+
+ authen = readl(reg + AUTHEN_OFFSET);
+ if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
+ clk_ro = true;
+
+ if (clk_ro) {
+ hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
+ mux_hw, &clk_mux_ro_ops, div_hw,
+ &clk_divider_ro_ops, NULL, NULL, flags);
+ } else {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ goto fail;
+
+ gate_hw = &gate->hw;
+ gate->reg = reg;
+ gate->shift = CCM_OFF_SHIFT;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+
+ hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
+ mux_hw, &imx93_clk_composite_mux_ops, div_hw,
+ &imx93_clk_composite_divider_ops, gate_hw,
+ &imx93_clk_composite_gate_ops,
+ flags | CLK_SET_RATE_NO_REPARENT);
+ }
+
+ if (IS_ERR(hw))
+ goto fail;
+
+ return &hw->clk;
+
+fail:
+ kfree(gate);
+ kfree(div);
+ kfree(mux);
+ return ERR_CAST(hw);
+}
+EXPORT_SYMBOL_GPL(imx93_clk_composite_flags);
diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c
new file mode 100644
index 0000000000..24e66fd65f
--- /dev/null
+++ b/drivers/clk/imx/clk-fracn-gppll.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <of_address.h>
+#include <linux/iopoll.h>
+#include <linux/bitfield.h>
+#include <soc/imx/clk-fracn-gppll.h>
+
+#include "clk.h"
+
+#define PLL_FRACN_GP(_rate, _mfi, _mfn, _mfd, _rdiv, _odiv) \
+ { \
+ .rate = (_rate), \
+ .mfi = (_mfi), \
+ .mfn = (_mfn), \
+ .mfd = (_mfd), \
+ .rdiv = (_rdiv), \
+ .odiv = (_odiv), \
+ }
+
+#define PLL_FRACN_GP_INTEGER(_rate, _mfi, _rdiv, _odiv) \
+ { \
+ .rate = (_rate), \
+ .mfi = (_mfi), \
+ .mfn = 0, \
+ .mfd = 0, \
+ .rdiv = (_rdiv), \
+ .odiv = (_odiv), \
+ }
+
+struct clk_fracn_gppll {
+ struct clk_hw hw;
+ void __iomem *base;
+ const struct imx_fracn_gppll_rate_table *rate_table;
+ int rate_count;
+ u32 flags;
+};
+
+/*
+ * Fvco = (Fref / rdiv) * (MFI + MFN / MFD)
+ * Fout = Fvco / odiv
+ * The (Fref / rdiv) should be in range 20MHz to 40MHz
+ * The Fvco should be in range 2.5Ghz to 5Ghz
+ */
+static const struct imx_fracn_gppll_rate_table fracn_tbl[] = {
+ PLL_FRACN_GP(650000000U, 162, 50, 100, 0, 6),
+ PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8),
+ PLL_FRACN_GP(560000000U, 140, 0, 1, 0, 6),
+ PLL_FRACN_GP(519750000U, 173, 25, 100, 1, 8),
+ PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8),
+ PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6),
+ PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9),
+ PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12),
+ PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10),
+ PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12)
+};
+
+struct imx_fracn_gppll_clk imx_fracn_gppll = {
+ .rate_table = fracn_tbl,
+ .rate_count = ARRAY_SIZE(fracn_tbl),
+};
+EXPORT_SYMBOL_GPL(imx_fracn_gppll);
+
+/*
+ * Fvco = (Fref / rdiv) * MFI
+ * Fout = Fvco / odiv
+ * The (Fref / rdiv) should be in range 20MHz to 40MHz
+ * The Fvco should be in range 2.5Ghz to 5Ghz
+ */
+static const struct imx_fracn_gppll_rate_table int_tbl[] = {
+ PLL_FRACN_GP_INTEGER(1700000000U, 141, 1, 2),
+ PLL_FRACN_GP_INTEGER(1400000000U, 175, 1, 3),
+ PLL_FRACN_GP_INTEGER(900000000U, 150, 1, 4),
+};
+
+struct imx_fracn_gppll_clk imx_fracn_gppll_integer = {
+ .rate_table = int_tbl,
+ .rate_count = ARRAY_SIZE(int_tbl),
+};
+EXPORT_SYMBOL_GPL(imx_fracn_gppll_integer);
+
+static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw)
+{
+ return container_of(hw, struct clk_fracn_gppll, hw);
+}
+
+static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
+ const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
+ int i;
+
+ /* Assuming rate_table is in descending order */
+ for (i = 0; i < pll->rate_count; i++)
+ if (rate >= rate_table[i].rate)
+ return rate_table[i].rate;
+
+ /* return minimum supported value */
+ return rate_table[pll->rate_count - 1].rate;
+}
+
+static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
+ const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
+ u32 pll_numerator, pll_denominator, pll_div;
+ u32 mfi, mfn, mfd, rdiv, odiv;
+ u64 fvco = parent_rate;
+ long rate = 0;
+ int i;
+
+ pll_numerator = readl_relaxed(pll->base + GPPLL_NUMERATOR);
+ mfn = FIELD_GET(GPPLL_MFN_MASK, pll_numerator);
+
+ pll_denominator = readl_relaxed(pll->base + GPPLL_DENOMINATOR);
+ mfd = FIELD_GET(GPPLL_MFD_MASK, pll_denominator);
+
+ pll_div = readl_relaxed(pll->base + GPPLL_DIV);
+ mfi = FIELD_GET(GPPLL_MFI_MASK, pll_div);
+
+ rdiv = FIELD_GET(GPPLL_RDIV_MASK, pll_div);
+ odiv = FIELD_GET(GPPLL_ODIV_MASK, pll_div);
+
+ /*
+ * Sometimes, the recalculated rate has deviation due to
+ * the frac part. So find the accurate pll rate from the table
+ * first, if no match rate in the table, use the rate calculated
+ * from the equation below.
+ */
+ for (i = 0; i < pll->rate_count; i++) {
+ if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi &&
+ rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv &&
+ rate_table[i].odiv == odiv)
+ rate = rate_table[i].rate;
+ }
+
+ if (rate)
+ return (unsigned long)rate;
+
+ if (!rdiv)
+ rdiv = rdiv + 1;
+
+ switch (odiv) {
+ case 0:
+ odiv = 2;
+ break;
+ case 1:
+ odiv = 3;
+ break;
+ default:
+ break;
+ }
+
+ if (pll->flags & CLK_FRACN_GPPLL_INTEGER) {
+ /* Fvco = (Fref / rdiv) * MFI */
+ fvco = fvco * mfi;
+ do_div(fvco, rdiv * odiv);
+ } else {
+ /* Fvco = (Fref / rdiv) * (MFI + MFN / MFD) */
+ fvco = fvco * mfi * mfd + fvco * mfn;
+ do_div(fvco, mfd * rdiv * odiv);
+ }
+
+ return (unsigned long)fvco;
+}
+
+static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll)
+{
+ return fracn_gppll_wait_lock(pll->base);
+}
+
+static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
+
+ return fracn_gppll_set_rate(pll->base, pll->flags, pll->rate_table,
+ pll->rate_count, drate);
+}
+
+static int clk_fracn_gppll_prepare(struct clk_hw *hw)
+{
+ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
+ u32 val;
+ int ret;
+
+ val = readl_relaxed(pll->base + GPPLL_CTRL);
+ if (val & POWERUP_MASK)
+ return 0;
+
+ val |= CLKMUX_BYPASS;
+ writel_relaxed(val, pll->base + GPPLL_CTRL);
+
+ val |= POWERUP_MASK;
+ writel_relaxed(val, pll->base + GPPLL_CTRL);
+
+ val |= CLKMUX_EN;
+ writel_relaxed(val, pll->base + GPPLL_CTRL);
+
+ ret = clk_fracn_gppll_wait_lock(pll);
+ if (ret)
+ return ret;
+
+ val &= ~CLKMUX_BYPASS;
+ writel_relaxed(val, pll->base + GPPLL_CTRL);
+
+ return 0;
+}
+
+static int clk_fracn_gppll_is_prepared(struct clk_hw *hw)
+{
+ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
+ u32 val;
+
+ val = readl_relaxed(pll->base + GPPLL_CTRL);
+
+ return (val & POWERUP_MASK) ? 1 : 0;
+}
+
+static void clk_fracn_gppll_unprepare(struct clk_hw *hw)
+{
+ struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
+ u32 val;
+
+ val = readl_relaxed(pll->base + GPPLL_CTRL);
+ val &= ~POWERUP_MASK;
+ writel_relaxed(val, pll->base + GPPLL_CTRL);
+}
+
+static const struct clk_ops clk_fracn_gppll_ops = {
+ .enable = clk_fracn_gppll_prepare,
+ .disable = clk_fracn_gppll_unprepare,
+ .is_enabled = clk_fracn_gppll_is_prepared,
+ .recalc_rate = clk_fracn_gppll_recalc_rate,
+ .round_rate = clk_fracn_gppll_round_rate,
+ .set_rate = clk_fracn_gppll_set_rate,
+};
+
+static struct clk *_imx_clk_fracn_gppll(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk,
+ u32 pll_flags)
+{
+ struct clk_fracn_gppll *pll;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = pll_clk->flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.ops = &clk_fracn_gppll_ops;
+
+ pll->base = base;
+ pll->hw.init = &init;
+ pll->rate_table = pll_clk->rate_table;
+ pll->rate_count = pll_clk->rate_count;
+ pll->flags = pll_flags;
+
+ hw = &pll->hw;
+
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ pr_err("%s: failed to register pll %s %d\n", __func__, name, ret);
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return &hw->clk;
+}
+
+struct clk *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk)
+{
+ return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_FRACN);
+}
+EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll);
+
+struct clk *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk)
+{
+ return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_INTEGER);
+}
+EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll_integer);
diff --git a/drivers/clk/imx/clk-gate-93.c b/drivers/clk/imx/clk-gate-93.c
new file mode 100644
index 0000000000..ed2714a03c
--- /dev/null
+++ b/drivers/clk/imx/clk-gate-93.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <of_address.h>
+#include <linux/iopoll.h>
+
+#include "clk.h"
+
+#define DIRECT_OFFSET 0x0
+
+/*
+ * 0b000 - LPCG will be OFF in any CPU mode.
+ * 0b100 - LPCG will be ON in any CPU mode.
+ */
+#define LPM_SETTING_OFF 0x0
+#define LPM_SETTING_ON 0x4
+
+#define LPM_CUR_OFFSET 0x1c
+
+#define AUTHEN_OFFSET 0x30
+#define CPULPM_EN BIT(2)
+#define TZ_NS_SHIFT 9
+#define TZ_NS_MASK BIT(9)
+
+#define WHITE_LIST_SHIFT 16
+
+struct imx93_clk_gate {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u32 bit_idx;
+ u32 val;
+ u32 mask;
+ spinlock_t *lock;
+ unsigned int *share_count;
+};
+
+#define to_imx93_clk_gate(_hw) container_of(_hw, struct imx93_clk_gate, hw)
+
+static void imx93_clk_gate_do_hardware(struct clk_hw *hw, bool enable)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ u32 val;
+
+ val = readl(gate->reg + AUTHEN_OFFSET);
+ if (val & CPULPM_EN) {
+ val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF;
+ writel(val, gate->reg + LPM_CUR_OFFSET);
+ } else {
+ val = readl(gate->reg + DIRECT_OFFSET);
+ val &= ~(gate->mask << gate->bit_idx);
+ if (enable)
+ val |= (gate->val & gate->mask) << gate->bit_idx;
+ writel(val, gate->reg + DIRECT_OFFSET);
+ }
+}
+
+static int imx93_clk_gate_enable(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (gate->share_count && (*gate->share_count)++ > 0)
+ goto out;
+
+ imx93_clk_gate_do_hardware(hw, true);
+out:
+ spin_unlock_irqrestore(gate->lock, flags);
+
+ return 0;
+}
+
+static void imx93_clk_gate_disable(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (gate->share_count) {
+ if (WARN_ON(*gate->share_count == 0))
+ goto out;
+ else if (--(*gate->share_count) > 0)
+ goto out;
+ }
+
+ imx93_clk_gate_do_hardware(hw, false);
+out:
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int imx93_clk_gate_reg_is_enabled(struct imx93_clk_gate *gate)
+{
+ u32 val = readl(gate->reg + AUTHEN_OFFSET);
+
+ if (val & CPULPM_EN) {
+ val = readl(gate->reg + LPM_CUR_OFFSET);
+ if (val == LPM_SETTING_ON)
+ return 1;
+ } else {
+ val = readl(gate->reg);
+ if (((val >> gate->bit_idx) & gate->mask) == gate->val)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int imx93_clk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ ret = imx93_clk_gate_reg_is_enabled(gate);
+
+ spin_unlock_irqrestore(gate->lock, flags);
+
+ return ret;
+}
+
+static const struct clk_ops imx93_clk_gate_ops = {
+ .set_rate = clk_parent_set_rate,
+ .round_rate = clk_parent_round_rate,
+ .enable = imx93_clk_gate_enable,
+ .disable = imx93_clk_gate_disable,
+ .is_enabled = imx93_clk_gate_is_enabled,
+};
+
+static const struct clk_ops imx93_clk_gate_ro_ops = {
+ .is_enabled = imx93_clk_gate_is_enabled,
+};
+
+struct clk *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
+ u32 mask, u32 domain_id, unsigned int *share_count)
+{
+ struct imx93_clk_gate *gate;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+ u32 authen;
+
+ gate = kzalloc(sizeof(struct imx93_clk_gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ gate->val = val;
+ gate->mask = mask;
+ gate->share_count = share_count;
+
+ init.name = name;
+ init.ops = &imx93_clk_gate_ops;
+ init.flags = flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+
+ gate->hw.init = &init;
+ hw = &gate->hw;
+
+ authen = readl(reg + AUTHEN_OFFSET);
+ if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
+ init.ops = &imx93_clk_gate_ro_ops;
+
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(gate);
+ return ERR_PTR(ret);
+ }
+
+ return &hw->clk;
+}
+EXPORT_SYMBOL_GPL(imx93_clk_gate);
diff --git a/drivers/clk/imx/clk-imx1.c b/drivers/clk/imx/clk-imx1.c
index cff32c0f99..3b97fbcc6d 100644
--- a/drivers/clk/imx/clk-imx1.c
+++ b/drivers/clk/imx/clk-imx1.c
@@ -10,7 +10,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx1-regs.h>
+#include <mach/imx/imx1-regs.h>
#include "clk.h"
@@ -73,7 +73,7 @@ static int __init mx1_clocks_init(void __iomem *regs, unsigned long fref)
return 0;
}
-static int imx1_ccm_probe(struct device_d *dev)
+static int imx1_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -95,8 +95,9 @@ static __maybe_unused struct of_device_id imx1_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx1_ccm_dt_ids);
-static struct driver_d imx1_ccm_driver = {
+static struct driver imx1_ccm_driver = {
.probe = imx1_ccm_probe,
.name = "imx1-ccm",
.of_compatible = DRV_OF_COMPAT(imx1_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx21.c b/drivers/clk/imx/clk-imx21.c
index 7abd82eeb1..6f2386e7d2 100644
--- a/drivers/clk/imx/clk-imx21.c
+++ b/drivers/clk/imx/clk-imx21.c
@@ -12,7 +12,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx21-regs.h>
+#include <mach/imx/imx21-regs.h>
#include "clk.h"
@@ -92,7 +92,7 @@ static const char *spll_sel_clks[] = {
"ckih",
};
-static int imx21_ccm_probe(struct device_d *dev)
+static int imx21_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -169,8 +169,9 @@ static __maybe_unused struct of_device_id imx21_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx21_ccm_dt_ids);
-static struct driver_d imx21_ccm_driver = {
+static struct driver imx21_ccm_driver = {
.probe = imx21_ccm_probe,
.name = "imx21-ccm",
.of_compatible = DRV_OF_COMPAT(imx21_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c
index 2bd0d6004b..cbaca348e2 100644
--- a/drivers/clk/imx/clk-imx25.c
+++ b/drivers/clk/imx/clk-imx25.c
@@ -10,7 +10,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx25-regs.h>
+#include <mach/imx/imx25-regs.h>
#include "clk.h"
@@ -70,7 +70,7 @@ static const char *per_sel_clks[] = {
"upll",
};
-static int imx25_ccm_probe(struct device_d *dev)
+static int imx25_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -183,8 +183,9 @@ static __maybe_unused struct of_device_id imx25_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx25_ccm_dt_ids);
-static struct driver_d imx25_ccm_driver = {
+static struct driver imx25_ccm_driver = {
.probe = imx25_ccm_probe,
.name = "imx25-ccm",
.of_compatible = DRV_OF_COMPAT(imx25_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx27.c b/drivers/clk/imx/clk-imx27.c
index 54894d1032..3f03705634 100644
--- a/drivers/clk/imx/clk-imx27.c
+++ b/drivers/clk/imx/clk-imx27.c
@@ -6,9 +6,9 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx27-regs.h>
-#include <mach/generic.h>
-#include <mach/revision.h>
+#include <mach/imx/imx27-regs.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/revision.h>
#include "clk.h"
@@ -155,7 +155,7 @@ static const char *clko_sel_clks[] = {
NULL,
};
-static int imx27_ccm_probe(struct device_d *dev)
+static int imx27_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -221,6 +221,7 @@ static int imx27_ccm_probe(struct device_d *dev)
clks[per3_gate] = imx_clk_gate("per3_gate", "per3_div", base + CCM_PCCR1, 8);
clks[lcdc_ahb_gate] = imx_clk_gate("lcdc_ahb_gate", "ahb", base + CCM_PCCR1, 15);
clks[lcdc_ipg_gate] = imx_clk_gate("lcdc_ipg_gate", "ipg", base + CCM_PCCR0, 14);
+ clks[wdog_ipg_gate] = imx_clk_gate("wdog_ipg_gate", "ipg", base + CCM_PCCR1, 24);
clkdev_add_physbase(clks[per1_div], MX27_GPT1_BASE_ADDR, NULL);
clkdev_add_physbase(clks[per1_div], MX27_GPT2_BASE_ADDR, NULL);
@@ -246,6 +247,8 @@ static int imx27_ccm_probe(struct device_d *dev)
clkdev_add_physbase(clks[lcdc_ahb_gate], MX27_LCDC_BASE_ADDR, "ahb");
clkdev_add_physbase(clks[lcdc_ipg_gate], MX27_LCDC_BASE_ADDR, "ipg");
clkdev_add_physbase(clks[ipg], MX27_FEC_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[nfc_div], MX27_NFC_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[wdog_ipg_gate], MX27_WDOG_BASE_ADDR, NULL);
return 0;
}
@@ -257,8 +260,9 @@ static __maybe_unused struct of_device_id imx27_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx27_ccm_dt_ids);
-static struct driver_d imx27_ccm_driver = {
+static struct driver imx27_ccm_driver = {
.probe = imx27_ccm_probe,
.name = "imx27-ccm",
.of_compatible = DRV_OF_COMPAT(imx27_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c
index fe241cba5f..47189f7814 100644
--- a/drivers/clk/imx/clk-imx31.c
+++ b/drivers/clk/imx/clk-imx31.c
@@ -10,7 +10,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx31-regs.h>
+#include <mach/imx/imx31-regs.h>
#include "clk.h"
@@ -79,7 +79,7 @@ static const char *per_sel[] = {
"ipg",
};
-static int imx31_ccm_probe(struct device_d *dev)
+static int imx31_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -138,8 +138,9 @@ static __maybe_unused struct of_device_id imx31_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx31_ccm_dt_ids);
-static struct driver_d imx31_ccm_driver = {
+static struct driver imx31_ccm_driver = {
.probe = imx31_ccm_probe,
.name = "imx31-ccm",
.of_compatible = DRV_OF_COMPAT(imx31_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c
index 9af149f68e..7ea823c6c9 100644
--- a/drivers/clk/imx/clk-imx35.c
+++ b/drivers/clk/imx/clk-imx35.c
@@ -9,7 +9,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx35-regs.h>
+#include <mach/imx/imx35-regs.h>
#include <reset_source.h>
#include "clk.h"
@@ -85,7 +85,7 @@ static const char *ipg_per_sel[] = {
"arm_per_div",
};
-static int imx35_ccm_probe(struct device_d *dev)
+static int imx35_ccm_probe(struct device *dev)
{
struct resource *iores;
u32 pdr0, consumer_sel, hsp_sel;
@@ -201,8 +201,9 @@ static __maybe_unused struct of_device_id imx35_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx35_ccm_dt_ids);
-static struct driver_d imx35_ccm_driver = {
+static struct driver imx35_ccm_driver = {
.probe = imx35_ccm_probe,
.name = "imx35-ccm",
.of_compatible = DRV_OF_COMPAT(imx35_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx5.c b/drivers/clk/imx/clk-imx5.c
index c7a1818bd7..b78611b0d4 100644
--- a/drivers/clk/imx/clk-imx5.c
+++ b/drivers/clk/imx/clk-imx5.c
@@ -10,9 +10,9 @@
#include <of.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx50-regs.h>
-#include <mach/imx51-regs.h>
-#include <mach/imx53-regs.h>
+#include <mach/imx/imx50-regs.h>
+#include <mach/imx/imx51-regs.h>
+#include <mach/imx/imx53-regs.h>
#include <dt-bindings/clock/imx5-clock.h>
#include "clk.h"
@@ -194,7 +194,8 @@ static const char *ipu_sel[] = {
"ahb",
};
-static void __init mx5_clocks_common_init(struct device_d *dev, void __iomem *base)
+static void __init mx5_clocks_common_init(struct device *dev,
+ void __iomem *base)
{
writel(0xffffffff, base + CCM_CCGR0);
writel(0xffffffff, base + CCM_CCGR1);
@@ -205,7 +206,9 @@ static void __init mx5_clocks_common_init(struct device_d *dev, void __iomem *ba
writel(0xffffffff, base + CCM_CCGR6);
writel(0xffffffff, base + CCM_CCGR7);
- if (!IS_ENABLED(CONFIG_COMMON_CLK_OF_PROVIDER) || !dev->device_node) {
+ clks[IMX5_CLK_DUMMY] = clk_fixed("dummy", 0);
+
+ if (!IS_ENABLED(CONFIG_COMMON_CLK_OF_PROVIDER) || !dev->of_node) {
clks[IMX5_CLK_CKIL] = clk_fixed("ckil", 32768);
clks[IMX5_CLK_OSC] = clk_fixed("osc", 24000000);
}
@@ -277,7 +280,7 @@ static void mx5_clocks_ipu_init(void __iomem *regs)
clks[IMX5_CLK_IPU_SEL] = imx_clk_mux("ipu_sel", regs + CCM_CBCMR, 6, 2, ipu_sel, ARRAY_SIZE(ipu_sel));
}
-static int __init mx50_clocks_init(struct device_d *dev, void __iomem *regs)
+static int __init mx50_clocks_init(struct device *dev, void __iomem *regs)
{
clks[IMX5_CLK_PLL1_SW] = imx_clk_pllv2("pll1_sw", "osc",
(void *)MX50_PLL1_BASE_ADDR);
@@ -312,11 +315,12 @@ static int __init mx50_clocks_init(struct device_d *dev, void __iomem *regs)
clkdev_add_physbase(clks[IMX5_CLK_PER_ROOT], MX50_PWM1_BASE_ADDR, "per");
clkdev_add_physbase(clks[IMX5_CLK_PER_ROOT], MX50_PWM2_BASE_ADDR, "per");
clkdev_add_physbase(clks[IMX5_CLK_AHB], MX50_OTG_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[IMX5_CLK_DUMMY], MX50_WDOG1_BASE_ADDR, NULL);
return 0;
}
-static int imx50_ccm_probe(struct device_d *dev)
+static int imx50_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -338,8 +342,9 @@ static __maybe_unused struct of_device_id imx50_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx50_ccm_dt_ids);
-static __maybe_unused struct driver_d imx50_ccm_driver = {
+static __maybe_unused struct driver imx50_ccm_driver = {
.probe = imx50_ccm_probe,
.name = "imx50-ccm",
.of_compatible = DRV_OF_COMPAT(imx50_ccm_dt_ids),
@@ -366,7 +371,7 @@ static void mx51_clocks_ipu_init(void __iomem *regs)
clkdev_add_physbase(clks[IMX5_CLK_IPU_DI1_SEL], MX51_IPU_BASE_ADDR, "di1");
}
-static int __init mx51_clocks_init(struct device_d *dev, void __iomem *regs)
+static int __init mx51_clocks_init(struct device *dev, void __iomem *regs)
{
clks[IMX5_CLK_PLL1_SW] = imx_clk_pllv2("pll1_sw", "osc", (void *)MX51_PLL1_BASE_ADDR);
clks[IMX5_CLK_PLL2_SW] = imx_clk_pllv2("pll2_sw", "osc", (void *)MX51_PLL2_BASE_ADDR);
@@ -392,6 +397,8 @@ static int __init mx51_clocks_init(struct device_d *dev, void __iomem *regs)
clkdev_add_physbase(clks[IMX5_CLK_IPG], MX51_ATA_BASE_ADDR, NULL);
clkdev_add_physbase(clks[IMX5_CLK_PER_ROOT], MX51_PWM1_BASE_ADDR, "per");
clkdev_add_physbase(clks[IMX5_CLK_PER_ROOT], MX51_PWM2_BASE_ADDR, "per");
+ clkdev_add_physbase(clks[IMX5_CLK_DUMMY], MX51_WDOG_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[IMX5_CLK_DUMMY], MX51_WDOG2_BASE_ADDR, NULL);
if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
mx51_clocks_ipu_init(regs);
@@ -399,7 +406,7 @@ static int __init mx51_clocks_init(struct device_d *dev, void __iomem *regs)
return 0;
}
-static int imx51_ccm_probe(struct device_d *dev)
+static int imx51_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -413,7 +420,7 @@ static int imx51_ccm_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = IMX5_CLK_END;
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
return 0;
}
@@ -425,8 +432,9 @@ static __maybe_unused struct of_device_id imx51_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx51_ccm_dt_ids);
-static __maybe_unused struct driver_d imx51_ccm_driver = {
+static __maybe_unused struct driver imx51_ccm_driver = {
.probe = imx51_ccm_probe,
.name = "imx51-ccm",
.of_compatible = DRV_OF_COMPAT(imx51_ccm_dt_ids),
@@ -458,7 +466,7 @@ static void mx53_clocks_ipu_init(void __iomem *regs)
clkdev_add_physbase(clks[IMX5_CLK_IPU_DI1_SEL], MX53_IPU_BASE_ADDR, "di1");
}
-static int __init mx53_clocks_init(struct device_d *dev, void __iomem *regs)
+static int __init mx53_clocks_init(struct device *dev, void __iomem *regs)
{
clks[IMX5_CLK_PLL1_SW] = imx_clk_pllv2("pll1_sw", "osc", (void *)MX53_PLL1_BASE_ADDR);
clks[IMX5_CLK_PLL2_SW] = imx_clk_pllv2("pll2_sw", "osc", (void *)MX53_PLL2_BASE_ADDR);
@@ -488,6 +496,8 @@ static int __init mx53_clocks_init(struct device_d *dev, void __iomem *regs)
clkdev_add_physbase(clks[IMX5_CLK_AHB], MX53_SATA_BASE_ADDR, NULL);
clkdev_add_physbase(clks[IMX5_CLK_PER_ROOT], MX53_PWM1_BASE_ADDR, "per");
clkdev_add_physbase(clks[IMX5_CLK_PER_ROOT], MX53_PWM2_BASE_ADDR, "per");
+ clkdev_add_physbase(clks[IMX5_CLK_DUMMY], MX53_WDOG1_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[IMX5_CLK_DUMMY], MX53_WDOG2_BASE_ADDR, NULL);
if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
mx53_clocks_ipu_init(regs);
@@ -495,7 +505,7 @@ static int __init mx53_clocks_init(struct device_d *dev, void __iomem *regs)
return 0;
}
-static int imx53_ccm_probe(struct device_d *dev)
+static int imx53_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -509,7 +519,7 @@ static int imx53_ccm_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = IMX5_CLK_END;
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
return 0;
}
@@ -521,8 +531,9 @@ static __maybe_unused struct of_device_id imx53_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx53_ccm_dt_ids);
-static __maybe_unused struct driver_d imx53_ccm_driver = {
+static __maybe_unused struct driver imx53_ccm_driver = {
.probe = imx53_ccm_probe,
.name = "imx53-ccm",
.of_compatible = DRV_OF_COMPAT(imx53_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c
index 06cc992b72..bac0c73d21 100644
--- a/drivers/clk/imx/clk-imx6.c
+++ b/drivers/clk/imx/clk-imx6.c
@@ -12,9 +12,9 @@
#include <of.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx6-regs.h>
-#include <mach/revision.h>
-#include <mach/imx6.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/revision.h>
+#include <mach/imx/imx6.h>
#include <dt-bindings/clock/imx6qdl-clock.h>
#include "clk.h"
@@ -647,7 +647,7 @@ static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb, struct dev
clk_set_parent(clks[IMX6QDL_CLK_IPU2_DI1_PRE_SEL], clks[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
}
-static int imx6_ccm_probe(struct device_d *dev)
+static int imx6_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base, *anatop_base, *ccm_base;
@@ -808,7 +808,7 @@ static int imx6_ccm_probe(struct device_d *dev)
imx6q_mmdc_ch1_mask_handshake(ccm_base);
if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
- imx6_add_video_clks(anatop_base, ccm_base, dev->device_node);
+ imx6_add_video_clks(anatop_base, ccm_base, dev->of_node);
writel(0xffffffff, ccm_base + CCGR0);
writel(0xf0ffffff, ccm_base + CCGR1); /* gate GPU3D, GPU2D */
@@ -824,7 +824,7 @@ static int imx6_ccm_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = IMX6QDL_CLK_END;
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
clk_enable(clks[IMX6QDL_CLK_MMDC_CH0_AXI_PODF]);
clk_enable(clks[IMX6QDL_CLK_PLL6_ENET]);
@@ -849,8 +849,9 @@ static __maybe_unused struct of_device_id imx6_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx6_ccm_dt_ids);
-static struct driver_d imx6_ccm_driver = {
+static struct driver imx6_ccm_driver = {
.probe = imx6_ccm_probe,
.name = "imx6-ccm",
.of_compatible = DRV_OF_COMPAT(imx6_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c
index 466893f82f..93edf24c1d 100644
--- a/drivers/clk/imx/clk-imx6sl.c
+++ b/drivers/clk/imx/clk-imx6sl.c
@@ -12,9 +12,9 @@
#include <linux/clkdev.h>
#include <linux/err.h>
#include <linux/clk.h>
-#include <mach/imx6-regs.h>
-#include <mach/revision.h>
-#include <mach/imx6.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/revision.h>
+#include <mach/imx/imx6.h>
#include "clk.h"
#include "common.h"
@@ -81,11 +81,11 @@ static struct clk_div_table video_div_table[] = {
{ }
};
-static int imx6sl_ccm_probe(struct device_d *dev)
+static int imx6sl_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base, *anatop_base, *ccm_base;
- struct device_node *ccm_node = dev->device_node;
+ struct device_node *ccm_node = dev->of_node;
clks[IMX6SL_CLK_DUMMY] = clk_fixed("dummy", 0);
@@ -308,8 +308,9 @@ static __maybe_unused struct of_device_id imx6sl_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx6sl_ccm_dt_ids);
-static struct driver_d imx6sl_ccm_driver = {
+static struct driver imx6sl_ccm_driver = {
.probe = imx6sl_ccm_probe,
.name = "imx6-ccm",
.of_compatible = DRV_OF_COMPAT(imx6sl_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c
index c11829259e..bebe1ed685 100644
--- a/drivers/clk/imx/clk-imx6sx.c
+++ b/drivers/clk/imx/clk-imx6sx.c
@@ -12,9 +12,9 @@
#include <linux/clkdev.h>
#include <linux/err.h>
#include <linux/clk.h>
-#include <mach/imx6-regs.h>
-#include <mach/revision.h>
-#include <mach/imx6.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/revision.h>
+#include <mach/imx/imx6.h>
#include "clk.h"
#include "common.h"
@@ -109,11 +109,11 @@ static struct clk_div_table video_div_table[] = {
{ }
};
-static int imx6sx_ccm_probe(struct device_d *dev)
+static int imx6sx_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base, *anatop_base, *ccm_base;
- struct device_node *ccm_node = dev->device_node;
+ struct device_node *ccm_node = dev->of_node;
clks[IMX6SX_CLK_DUMMY] = clk_fixed("dummy", 0);
@@ -462,8 +462,9 @@ static __maybe_unused struct of_device_id imx6sx_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx6sx_ccm_dt_ids);
-static struct driver_d imx6sx_ccm_driver = {
+static struct driver imx6sx_ccm_driver = {
.probe = imx6sx_ccm_probe,
.name = "imx6-ccm",
.of_compatible = DRV_OF_COMPAT(imx6sx_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c
index af5d582ffc..e60267d8fb 100644
--- a/drivers/clk/imx/clk-imx6ul.c
+++ b/drivers/clk/imx/clk-imx6ul.c
@@ -11,9 +11,9 @@
#include <of.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx6-regs.h>
-#include <mach/revision.h>
-#include <mach/imx6.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/revision.h>
+#include <mach/imx/imx6.h>
#include <dt-bindings/clock/imx6ul-clock.h>
#include "clk.h"
@@ -92,12 +92,12 @@ static struct clk_div_table clk_enet_ref_table[] = {
{ }
};
-static int imx6_ccm_probe(struct device_d *dev)
+static int imx6_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base, *anatop_base, *ccm_base;
int i;
- struct device_node *ccm_node = dev->device_node;
+ struct device_node *ccm_node = dev->of_node;
struct clk_hw *hw;
anatop_base = IOMEM(MX6_ANATOP_BASE_ADDR);
@@ -461,8 +461,9 @@ static __maybe_unused struct of_device_id imx6_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx6_ccm_dt_ids);
-static struct driver_d imx6_ccm_driver = {
+static struct driver imx6_ccm_driver = {
.probe = imx6_ccm_probe,
.name = "imx6-ccm",
.of_compatible = DRV_OF_COMPAT(imx6_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx7.c b/drivers/clk/imx/clk-imx7.c
index ffa39d17b0..224471a982 100644
--- a/drivers/clk/imx/clk-imx7.c
+++ b/drivers/clk/imx/clk-imx7.c
@@ -6,13 +6,14 @@
#include <common.h>
#include <init.h>
#include <driver.h>
+#include <deep-probe.h>
#include <linux/clk.h>
#include <io.h>
#include <of.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx7-regs.h>
-#include <mach/revision.h>
+#include <mach/imx/imx7-regs.h>
+#include <mach/imx/revision.h>
#include <dt-bindings/clock/imx7d-clock.h>
#include "clk.h"
@@ -358,9 +359,11 @@ static int const clks_init_on[] __initconst = {
static struct clk_onecell_data clk_data;
-static int imx7_clk_initialized;
+static struct device_node *ccm_np;
-static int imx7_ccm_probe(struct device_d *dev)
+static int imx7_clk_setup(void);
+
+static int imx7_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base, *anatop_base, *ccm_base;
@@ -804,21 +807,37 @@ static int imx7_ccm_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
+
+ ccm_np = dev->of_node;
- imx7_clk_initialized = 1;
+ /*
+ * imx7_clk_setup() requires both the CCM and fixed-clock osc devices
+ * to be available.
+ * With deep probe enabled, we can instead just directly call
+ * imx7_clk_setup because the osc fixed-clock will just be probed
+ * on demand if not yet available. Otherwise, the imx7_clk_setup
+ * will run at postcore_initcall level.
+ */
+ if (deep_probe_is_supported())
+ return imx7_clk_setup();
return 0;
}
static int imx7_clk_setup(void)
{
+ struct clk *clk;
int i;
- if (!imx7_clk_initialized)
+ if (!ccm_np)
return 0;
- clks[IMX7D_OSC_24M_CLK] = clk_lookup("osc");
+ clk = of_clk_get_by_name(ccm_np, "osc");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ clks[IMX7D_OSC_24M_CLK] = clk;
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_enable(clks[clks_init_on[i]]);
@@ -840,6 +859,8 @@ static int imx7_clk_setup(void)
clk_set_rate(clks[IMX7D_ENET1_TIME_ROOT_CLK], 25000000);
clk_set_rate(clks[IMX7D_ENET2_TIME_ROOT_CLK], 25000000);
+ ccm_np = NULL;
+
return 0;
}
postcore_initcall(imx7_clk_setup);
@@ -851,8 +872,9 @@ static __maybe_unused struct of_device_id imx7_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx7_ccm_dt_ids);
-static struct driver_d imx7_ccm_driver = {
+static struct driver imx7_ccm_driver = {
.probe = imx7_ccm_probe,
.name = "imx6-ccm",
.of_compatible = DRV_OF_COMPAT(imx7_ccm_dt_ids),
diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index a1611be183..ca1066a4dc 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -12,6 +12,11 @@
#include "clk.h"
+static u32 share_count_nand;
+static u32 share_count_media;
+static u32 share_count_usb;
+static u32 share_count_audio;
+
static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", };
static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
@@ -146,10 +151,6 @@ static const char * const imx8mp_can2_sels[] = {"osc_24m", "sys_pll2_200m", "sys
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mp_memrepair_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
- "sys_pll3_out", "audio_pll1_out", "video_pll1_out",
- "audio_pll2_out", "sys_pll1_133m", };
-
static const char * const imx8mp_pcie_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m",
"sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m",
"sys_pll1_160m", "sys_pll1_200m", };
@@ -174,10 +175,6 @@ static const char * const imx8mp_sai3_sels[] = {"osc_24m", "audio_pll1_out", "au
"video_pll1_out", "sys_pll1_133m", "osc_hdmi",
"clk_ext3", "clk_ext4", };
-static const char * const imx8mp_sai4_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
- "clk_ext1", "clk_ext2", };
-
static const char * const imx8mp_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
"video_pll1_out", "sys_pll1_133m", "osc_hdmi",
"clk_ext2", "clk_ext3", };
@@ -356,7 +353,7 @@ static const char * const imx8mp_media_mipi_phy1_ref_sels[] = {"osc_24m", "sys_p
"clk_ext2", "audio_pll2_out",
"video_pll1_out", };
-static const char * const imx8mp_media_disp1_pix_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out",
+static const char * const imx8mp_media_disp_pix_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out",
"audio_pll1_out", "sys_pll1_800m",
"sys_pll2_1000m", "sys_pll3_out", "clk_ext4", };
@@ -366,14 +363,13 @@ static const char * const imx8mp_media_cam2_pix_sels[] = {"osc_24m", "sys_pll1_2
"video_pll1_out", };
static const char * const imx8mp_media_ldb_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m",
- "sys_pll1_800m", "sys_pll2_1000m",
- "clk_ext2", "audio_pll2_out",
- "video_pll1_out", };
+ "sys_pll1_800m", "sys_pll2_1000m",
+ "clk_ext2", "audio_pll2_out",
+ "video_pll1_out", };
-static const char * const imx8mp_media_mipi_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m",
- "sys_pll1_800m", "sys_pll2_1000m",
- "sys_pll3_out", "clk_ext3",
- "audio_pll2_out", };
+static const char * const imx8mp_memrepair_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m",
+ "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out",
+ "clk_ext3", "audio_pll2_out", };
static const char * const imx8mp_media_mipi_test_byte_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m",
"sys_pll3_out", "sys_pll2_100m",
@@ -398,6 +394,11 @@ static const char * const imx8mp_sai7_sels[] = {"osc_24m", "audio_pll1_out", "au
static const char * const imx8mp_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", };
+static const char * const imx8mp_clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "dummy", "dummy", "gpu_pll_out", "vpu_pll_out",
+ "arm_pll_out", "sys_pll1", "sys_pll2", "sys_pll3",
+ "dummy", "dummy", "osc_24m", "dummy", "osc_32k"};
+
static struct clk_onecell_data clk_data;
static struct clk *clks[IMX8MP_CLK_END];
@@ -510,6 +511,15 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_SYS_PLL2_500M] = imx_clk_hw_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2);
hws[IMX8MP_SYS_PLL2_1000M] = imx_clk_hw_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1);
+ hws[IMX8MP_CLK_CLKOUT1_SEL] = imx_clk_hw_mux2("clkout1_sel", anatop_base + 0x128, 4, 4,
+ imx8mp_clkout_sels, ARRAY_SIZE(imx8mp_clkout_sels));
+ hws[IMX8MP_CLK_CLKOUT1_DIV] = imx_clk_hw_divider("clkout1_div", "clkout1_sel", anatop_base + 0x128, 0, 4);
+ hws[IMX8MP_CLK_CLKOUT1] = imx_clk_hw_gate("clkout1", "clkout1_div", anatop_base + 0x128, 8);
+ hws[IMX8MP_CLK_CLKOUT2_SEL] = imx_clk_hw_mux2("clkout2_sel", anatop_base + 0x128, 20, 4,
+ imx8mp_clkout_sels, ARRAY_SIZE(imx8mp_clkout_sels));
+ hws[IMX8MP_CLK_CLKOUT2_DIV] = imx_clk_hw_divider("clkout2_div", "clkout2_sel", anatop_base + 0x128, 16, 4);
+ hws[IMX8MP_CLK_CLKOUT2] = imx_clk_hw_gate("clkout2", "clkout2_div", anatop_base + 0x128, 24);
+
hws[IMX8MP_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mp_a53_sels, ccm_base + 0x8000);
hws[IMX8MP_CLK_A53_SRC] = hws[IMX8MP_CLK_A53_DIV];
hws[IMX8MP_CLK_A53_CG] = hws[IMX8MP_CLK_A53_DIV];
@@ -526,9 +536,9 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
/* CORE SEL */
hws[IMX8MP_CLK_A53_CORE] = imx_clk_hw_mux2("arm_a53_core", ccm_base + 0x9880, 24, 1, imx8mp_a53_core_sels, ARRAY_SIZE(imx8mp_a53_core_sels));
- hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800);
+ hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800);
hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880);
- hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900);
+ hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900);
hws[IMX8MP_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mp_vpu_bus_sels, ccm_base + 0x8980);
hws[IMX8MP_CLK_MEDIA_AXI] = imx8m_clk_hw_composite_bus("media_axi", imx8mp_media_axi_sels, ccm_base + 0x8a00);
hws[IMX8MP_CLK_MEDIA_APB] = imx8m_clk_hw_composite_bus("media_apb", imx8mp_media_apb_sels, ccm_base + 0x8a80);
@@ -536,14 +546,15 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_HDMI_AXI] = imx8m_clk_hw_composite_bus("hdmi_axi", imx8mp_media_axi_sels, ccm_base + 0x8b80);
hws[IMX8MP_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mp_gpu_axi_sels, ccm_base + 0x8c00);
hws[IMX8MP_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mp_gpu_ahb_sels, ccm_base + 0x8c80);
- hws[IMX8MP_CLK_NOC] = imx8m_clk_hw_composite_critical("noc", imx8mp_noc_sels, ccm_base + 0x8d00);
- hws[IMX8MP_CLK_NOC_IO] = imx8m_clk_hw_composite_critical("noc_io", imx8mp_noc_io_sels, ccm_base + 0x8d80);
+ hws[IMX8MP_CLK_NOC] = imx8m_clk_hw_composite_bus_critical("noc", imx8mp_noc_sels, ccm_base + 0x8d00);
+ hws[IMX8MP_CLK_NOC_IO] = imx8m_clk_hw_composite_bus_critical("noc_io", imx8mp_noc_io_sels, ccm_base + 0x8d80);
hws[IMX8MP_CLK_ML_AXI] = imx8m_clk_hw_composite_bus("ml_axi", imx8mp_ml_axi_sels, ccm_base + 0x8e00);
hws[IMX8MP_CLK_ML_AHB] = imx8m_clk_hw_composite_bus("ml_ahb", imx8mp_ml_ahb_sels, ccm_base + 0x8e80);
- hws[IMX8MP_CLK_AHB] = imx8m_clk_hw_composite_critical("ahb_root", imx8mp_ahb_sels, ccm_base + 0x9000);
+ hws[IMX8MP_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb_root", imx8mp_ahb_sels, ccm_base + 0x9000);
hws[IMX8MP_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mp_audio_ahb_sels, ccm_base + 0x9100);
hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200);
+ hws[IMX8MP_CLK_MEDIA_DISP2_PIX] = imx8m_clk_hw_composite_bus("media_disp2_pix", imx8mp_media_disp_pix_sels, ccm_base + 0x9300);
hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1);
@@ -553,14 +564,12 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_VPU_G2] = imx8m_clk_hw_composite("vpu_g2", imx8mp_vpu_g2_sels, ccm_base + 0xa180);
hws[IMX8MP_CLK_CAN1] = imx8m_clk_hw_composite("can1", imx8mp_can1_sels, ccm_base + 0xa200);
hws[IMX8MP_CLK_CAN2] = imx8m_clk_hw_composite("can2", imx8mp_can2_sels, ccm_base + 0xa280);
- hws[IMX8MP_CLK_MEMREPAIR] = imx8m_clk_hw_composite("memrepair", imx8mp_memrepair_sels, ccm_base + 0xa300);
hws[IMX8MP_CLK_PCIE_AUX] = imx8m_clk_hw_composite("pcie_aux", imx8mp_pcie_aux_sels, ccm_base + 0xa400);
hws[IMX8MP_CLK_I2C5] = imx8m_clk_hw_composite("i2c5", imx8mp_i2c5_sels, ccm_base + 0xa480);
hws[IMX8MP_CLK_I2C6] = imx8m_clk_hw_composite("i2c6", imx8mp_i2c6_sels, ccm_base + 0xa500);
hws[IMX8MP_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mp_sai1_sels, ccm_base + 0xa580);
hws[IMX8MP_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mp_sai2_sels, ccm_base + 0xa600);
hws[IMX8MP_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mp_sai3_sels, ccm_base + 0xa680);
- hws[IMX8MP_CLK_SAI4] = imx8m_clk_hw_composite("sai4", imx8mp_sai4_sels, ccm_base + 0xa700);
hws[IMX8MP_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mp_sai5_sels, ccm_base + 0xa780);
hws[IMX8MP_CLK_SAI6] = imx8m_clk_hw_composite("sai6", imx8mp_sai6_sels, ccm_base + 0xa800);
hws[IMX8MP_CLK_ENET_QOS] = imx8m_clk_hw_composite("enet_qos", imx8mp_enet_qos_sels, ccm_base + 0xa880);
@@ -607,10 +616,10 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_USDHC3] = imx8m_clk_hw_composite("usdhc3", imx8mp_usdhc3_sels, ccm_base + 0xbc80);
hws[IMX8MP_CLK_MEDIA_CAM1_PIX] = imx8m_clk_hw_composite("media_cam1_pix", imx8mp_media_cam1_pix_sels, ccm_base + 0xbd00);
hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF] = imx8m_clk_hw_composite("media_mipi_phy1_ref", imx8mp_media_mipi_phy1_ref_sels, ccm_base + 0xbd80);
- hws[IMX8MP_CLK_MEDIA_DISP1_PIX] = imx8m_clk_hw_composite("media_disp1_pix", imx8mp_media_disp1_pix_sels, ccm_base + 0xbe00);
+ hws[IMX8MP_CLK_MEDIA_DISP1_PIX] = imx8m_clk_hw_composite("media_disp1_pix", imx8mp_media_disp_pix_sels, ccm_base + 0xbe00);
hws[IMX8MP_CLK_MEDIA_CAM2_PIX] = imx8m_clk_hw_composite("media_cam2_pix", imx8mp_media_cam2_pix_sels, ccm_base + 0xbe80);
hws[IMX8MP_CLK_MEDIA_LDB] = imx8m_clk_hw_composite("media_ldb", imx8mp_media_ldb_sels, ccm_base + 0xbf00);
- hws[IMX8MP_CLK_MEDIA_MIPI_CSI2_ESC] = imx8m_clk_hw_composite("media_mipi_csi2_esc", imx8mp_media_mipi_csi2_esc_sels, ccm_base + 0xbf80);
+ hws[IMX8MP_CLK_MEMREPAIR] = imx8m_clk_hw_composite_critical("mem_repair", imx8mp_memrepair_sels, ccm_base + 0xbf80);
hws[IMX8MP_CLK_MEDIA_MIPI_TEST_BYTE] = imx8m_clk_hw_composite("media_mipi_test_byte", imx8mp_media_mipi_test_byte_sels, ccm_base + 0xc100);
hws[IMX8MP_CLK_ECSPI3] = imx8m_clk_hw_composite("ecspi3", imx8mp_ecspi3_sels, ccm_base + 0xc180);
hws[IMX8MP_CLK_PDM] = imx8m_clk_hw_composite("pdm", imx8mp_pdm_sels, ccm_base + 0xc200);
@@ -640,6 +649,7 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_I2C2_ROOT] = imx_clk_hw_gate4("i2c2_root_clk", "i2c2", ccm_base + 0x4180, 0);
hws[IMX8MP_CLK_I2C3_ROOT] = imx_clk_hw_gate4("i2c3_root_clk", "i2c3", ccm_base + 0x4190, 0);
hws[IMX8MP_CLK_I2C4_ROOT] = imx_clk_hw_gate4("i2c4_root_clk", "i2c4", ccm_base + 0x41a0, 0);
+ hws[IMX8MP_CLK_MU_ROOT] = imx_clk_hw_gate4("mu_root_clk", "ipg_root", ccm_base + 0x4210, 0);
hws[IMX8MP_CLK_OCOTP_ROOT] = imx_clk_hw_gate4("ocotp_root_clk", "ipg_root", ccm_base + 0x4220, 0);
hws[IMX8MP_CLK_PCIE_ROOT] = imx_clk_hw_gate4("pcie_root_clk", "pcie_aux", ccm_base + 0x4250, 0);
hws[IMX8MP_CLK_PWM1_ROOT] = imx_clk_hw_gate4("pwm1_root_clk", "pwm1", ccm_base + 0x4280, 0);
@@ -649,23 +659,23 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_QOS_ROOT] = imx_clk_hw_gate4("qos_root_clk", "ipg_root", ccm_base + 0x42c0, 0);
hws[IMX8MP_CLK_QOS_ENET_ROOT] = imx_clk_hw_gate4("qos_enet_root_clk", "ipg_root", ccm_base + 0x42e0, 0);
hws[IMX8MP_CLK_QSPI_ROOT] = imx_clk_hw_gate4("qspi_root_clk", "qspi", ccm_base + 0x42f0, 0);
- hws[IMX8MP_CLK_NAND_ROOT] = imx_clk_hw_gate2_shared2("nand_root_clk", "nand", ccm_base + 0x4300, 0);
- hws[IMX8MP_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", ccm_base + 0x4300, 0);
+ hws[IMX8MP_CLK_NAND_ROOT] = imx_clk_hw_gate2_shared2("nand_root_clk", "nand", ccm_base + 0x4300, 0, &share_count_nand);
+ hws[IMX8MP_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", ccm_base + 0x4300, 0, &share_count_nand);
hws[IMX8MP_CLK_I2C5_ROOT] = imx_clk_hw_gate2("i2c5_root_clk", "i2c5", ccm_base + 0x4330, 0);
hws[IMX8MP_CLK_I2C6_ROOT] = imx_clk_hw_gate2("i2c6_root_clk", "i2c6", ccm_base + 0x4340, 0);
hws[IMX8MP_CLK_CAN1_ROOT] = imx_clk_hw_gate2("can1_root_clk", "can1", ccm_base + 0x4350, 0);
hws[IMX8MP_CLK_CAN2_ROOT] = imx_clk_hw_gate2("can2_root_clk", "can2", ccm_base + 0x4360, 0);
hws[IMX8MP_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_root_clk", "ipg_root", ccm_base + 0x43a0, 0);
- hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
hws[IMX8MP_CLK_SIM_ENET_ROOT] = imx_clk_hw_gate4("sim_enet_root_clk", "enet_axi", ccm_base + 0x4400, 0);
+ hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
hws[IMX8MP_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", ccm_base + 0x4450, 0);
hws[IMX8MP_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", ccm_base + 0x4460, 0);
- hws[IMX8MP_CLK_SNVS_ROOT] = imx_clk_hw_gate4("snvs_root_clk", "ipg_root", ccm_base + 0x4470, 0);
hws[IMX8MP_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", ccm_base + 0x4490, 0);
hws[IMX8MP_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", ccm_base + 0x44a0, 0);
hws[IMX8MP_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", ccm_base + 0x44b0, 0);
hws[IMX8MP_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", ccm_base + 0x44c0, 0);
- hws[IMX8MP_CLK_USB_ROOT] = imx_clk_hw_gate4("usb_root_clk", "osc_32k", ccm_base + 0x44d0, 0);
+ hws[IMX8MP_CLK_USB_ROOT] = imx_clk_hw_gate2_shared2("usb_root_clk", "hsio_axi", ccm_base + 0x44d0, 0, &share_count_usb);
+ hws[IMX8MP_CLK_USB_SUSP] = imx_clk_hw_gate2_shared2("usb_suspend_clk", "osc_32k", ccm_base + 0x44d0, 0, &share_count_usb);
hws[IMX8MP_CLK_USB_PHY_ROOT] = imx_clk_hw_gate4("usb_phy_root_clk", "usb_phy_ref", ccm_base + 0x44f0, 0);
hws[IMX8MP_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", ccm_base + 0x4510, 0);
hws[IMX8MP_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", ccm_base + 0x4520, 0);
@@ -678,19 +688,30 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
hws[IMX8MP_CLK_VPU_G2_ROOT] = imx_clk_hw_gate4("vpu_g2_root_clk", "vpu_g2", ccm_base + 0x45a0, 0);
hws[IMX8MP_CLK_NPU_ROOT] = imx_clk_hw_gate4("npu_root_clk", "ml_core", ccm_base + 0x45b0, 0);
hws[IMX8MP_CLK_HSIO_ROOT] = imx_clk_hw_gate4("hsio_root_clk", "ipg_root", ccm_base + 0x45c0, 0);
- hws[IMX8MP_CLK_MEDIA_APB_ROOT] = imx_clk_hw_gate2_shared2("media_apb_root_clk", "media_apb", ccm_base + 0x45d0, 0);
- hws[IMX8MP_CLK_MEDIA_AXI_ROOT] = imx_clk_hw_gate2_shared2("media_axi_root_clk", "media_axi", ccm_base + 0x45d0, 0);
- hws[IMX8MP_CLK_MEDIA_CAM1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam1_pix_root_clk", "media_cam1_pix", ccm_base + 0x45d0, 0);
- hws[IMX8MP_CLK_MEDIA_CAM2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam2_pix_root_clk", "media_cam2_pix", ccm_base + 0x45d0, 0);
- hws[IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp1_pix_root_clk", "media_disp1_pix", ccm_base + 0x45d0, 0);
- hws[IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp2_pix_root_clk", "media_disp2_pix", ccm_base + 0x45d0, 0);
- hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp", ccm_base + 0x45d0, 0);
+ hws[IMX8MP_CLK_MEDIA_APB_ROOT] = imx_clk_hw_gate2_shared2("media_apb_root_clk", "media_apb", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_AXI_ROOT] = imx_clk_hw_gate2_shared2("media_axi_root_clk", "media_axi", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_CAM1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam1_pix_root_clk", "media_cam1_pix", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_CAM2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam2_pix_root_clk", "media_cam2_pix", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp1_pix_root_clk", "media_disp1_pix", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp2_pix_root_clk", "media_disp2_pix", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF_ROOT] = imx_clk_hw_gate2_shared2("media_mipi_phy1_ref_root", "media_mipi_phy1_ref", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_LDB_ROOT] = imx_clk_hw_gate2_shared2("media_ldb_root_clk", "media_ldb", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp", ccm_base + 0x45d0, 0, &share_count_media);
hws[IMX8MP_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", ccm_base + 0x45e0, 0);
hws[IMX8MP_CLK_HDMI_ROOT] = imx_clk_hw_gate4("hdmi_root_clk", "hdmi_axi", ccm_base + 0x45f0, 0);
hws[IMX8MP_CLK_TSENSOR_ROOT] = imx_clk_hw_gate4("tsensor_root_clk", "ipg_root", ccm_base + 0x4620, 0);
hws[IMX8MP_CLK_VPU_ROOT] = imx_clk_hw_gate4("vpu_root_clk", "vpu_bus", ccm_base + 0x4630, 0);
- hws[IMX8MP_CLK_AUDIO_ROOT] = imx_clk_hw_gate4("audio_root_clk", "ipg_root", ccm_base + 0x4650, 0);
+
+ hws[IMX8MP_CLK_AUDIO_AHB_ROOT] = imx_clk_hw_gate2_shared2("audio_ahb_root", "audio_ahb", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_AUDIO_AXI_ROOT] = imx_clk_hw_gate2_shared2("audio_axi_root", "audio_axi", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI1_ROOT] = imx_clk_hw_gate2_shared2("sai1_root", "sai1", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI2_ROOT] = imx_clk_hw_gate2_shared2("sai2_root", "sai2", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI3_ROOT] = imx_clk_hw_gate2_shared2("sai3_root", "sai3", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI5_ROOT] = imx_clk_hw_gate2_shared2("sai5_root", "sai5", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI6_ROOT] = imx_clk_hw_gate2_shared2("sai6_root", "sai6", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI7_ROOT] = imx_clk_hw_gate2_shared2("sai7_root", "sai7", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_PDM_ROOT] = imx_clk_hw_gate2_shared2("pdm_root", "pdm", ccm_base + 0x4650, 0, &share_count_audio);
hws[IMX8MP_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core",
hws[IMX8MP_CLK_A53_CORE],
diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c
new file mode 100644
index 0000000000..e460091ba6
--- /dev/null
+++ b/drivers/clk/imx/clk-imx93.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 NXP.
+ */
+
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <of_address.h>
+#include <dt-bindings/clock/imx93-clock.h>
+
+#include "clk.h"
+
+enum clk_sel {
+ LOW_SPEED_IO_SEL,
+ NON_IO_SEL,
+ FAST_SEL,
+ AUDIO_SEL,
+ VIDEO_SEL,
+ TPM_SEL,
+ CKO1_SEL,
+ CKO2_SEL,
+ MISC_SEL,
+ MAX_SEL
+};
+
+static u32 share_count_sai1;
+static u32 share_count_sai2;
+static u32 share_count_sai3;
+static u32 share_count_mub;
+static u32 share_count_pdm;
+
+static const char * const a55_core_sels[] = {"a55_alt", "arm_pll"};
+static const char *parent_names[MAX_SEL][4] = {
+ {"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "video_pll"},
+ {"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "sys_pll_pfd2_div2"},
+ {"osc_24m", "sys_pll_pfd0", "sys_pll_pfd1", "sys_pll_pfd2"},
+ {"osc_24m", "audio_pll", "video_pll", "clk_ext1"},
+ {"osc_24m", "audio_pll", "video_pll", "sys_pll_pfd0"},
+ {"osc_24m", "sys_pll_pfd0", "audio_pll", "clk_ext1"},
+ {"osc_24m", "sys_pll_pfd0", "sys_pll_pfd1", "audio_pll"},
+ {"osc_24m", "sys_pll_pfd0", "sys_pll_pfd1", "video_pll"},
+ {"osc_24m", "audio_pll", "video_pll", "sys_pll_pfd2"},
+};
+
+static const struct imx93_clk_root {
+ u32 clk;
+ char *name;
+ u32 off;
+ enum clk_sel sel;
+ unsigned long flags;
+} root_array[] = {
+ /* a55/m33/bus critical clk for system run */
+ { IMX93_CLK_A55_PERIPH, "a55_periph_root", 0x0000, FAST_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_A55_MTR_BUS, "a55_mtr_bus_root", 0x0080, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_A55, "a55_alt_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_M33, "m33_root", 0x0180, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_BUS_WAKEUP, "bus_wakeup_root", 0x0280, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_BUS_AON, "bus_aon_root", 0x0300, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_WAKEUP_AXI, "wakeup_axi_root", 0x0380, FAST_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_SWO_TRACE, "swo_trace_root", 0x0400, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_M33_SYSTICK, "m33_systick_root", 0x0480, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_FLEXIO1, "flexio1_root", 0x0500, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_FLEXIO2, "flexio2_root", 0x0580, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPTMR1, "lptmr1_root", 0x0700, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPTMR2, "lptmr2_root", 0x0780, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_TPM2, "tpm2_root", 0x0880, TPM_SEL, },
+ { IMX93_CLK_TPM4, "tpm4_root", 0x0980, TPM_SEL, },
+ { IMX93_CLK_TPM5, "tpm5_root", 0x0a00, TPM_SEL, },
+ { IMX93_CLK_TPM6, "tpm6_root", 0x0a80, TPM_SEL, },
+ { IMX93_CLK_FLEXSPI1, "flexspi1_root", 0x0b00, FAST_SEL, },
+ { IMX93_CLK_CAN1, "can1_root", 0x0b80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_CAN2, "can2_root", 0x0c00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART1, "lpuart1_root", 0x0c80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART2, "lpuart2_root", 0x0d00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART3, "lpuart3_root", 0x0d80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART4, "lpuart4_root", 0x0e00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART5, "lpuart5_root", 0x0e80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART6, "lpuart6_root", 0x0f00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART7, "lpuart7_root", 0x0f80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPUART8, "lpuart8_root", 0x1000, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C1, "lpi2c1_root", 0x1080, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C2, "lpi2c2_root", 0x1100, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C3, "lpi2c3_root", 0x1180, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C4, "lpi2c4_root", 0x1200, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C5, "lpi2c5_root", 0x1280, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C6, "lpi2c6_root", 0x1300, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C7, "lpi2c7_root", 0x1380, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPI2C8, "lpi2c8_root", 0x1400, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI1, "lpspi1_root", 0x1480, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI2, "lpspi2_root", 0x1500, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI3, "lpspi3_root", 0x1580, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI4, "lpspi4_root", 0x1600, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI5, "lpspi5_root", 0x1680, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI6, "lpspi6_root", 0x1700, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI7, "lpspi7_root", 0x1780, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_LPSPI8, "lpspi8_root", 0x1800, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_I3C1, "i3c1_root", 0x1880, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_I3C2, "i3c2_root", 0x1900, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_USDHC1, "usdhc1_root", 0x1980, FAST_SEL, },
+ { IMX93_CLK_USDHC2, "usdhc2_root", 0x1a00, FAST_SEL, },
+ { IMX93_CLK_USDHC3, "usdhc3_root", 0x1a80, FAST_SEL, },
+ { IMX93_CLK_SAI1, "sai1_root", 0x1b00, AUDIO_SEL, },
+ { IMX93_CLK_SAI2, "sai2_root", 0x1b80, AUDIO_SEL, },
+ { IMX93_CLK_SAI3, "sai3_root", 0x1c00, AUDIO_SEL, },
+ { IMX93_CLK_CCM_CKO1, "ccm_cko1_root", 0x1c80, CKO1_SEL, },
+ { IMX93_CLK_CCM_CKO2, "ccm_cko2_root", 0x1d00, CKO2_SEL, },
+ { IMX93_CLK_CCM_CKO3, "ccm_cko3_root", 0x1d80, CKO1_SEL, },
+ { IMX93_CLK_CCM_CKO4, "ccm_cko4_root", 0x1e00, CKO2_SEL, },
+ /*
+ * Critical because clk is used for handshake between HSIOMIX and NICMIX when
+ * NICMIX power down/on during system suspend/resume
+ */
+ { IMX93_CLK_HSIO, "hsio_root", 0x1e80, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL},
+ { IMX93_CLK_HSIO_USB_TEST_60M, "hsio_usb_test_60m_root", 0x1f00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_HSIO_ACSCAN_80M, "hsio_acscan_80m_root", 0x1f80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_HSIO_ACSCAN_480M, "hsio_acscan_480m_root", 0x2000, MISC_SEL, },
+ { IMX93_CLK_NIC_AXI, "nic_axi_root", 0x2080, FAST_SEL, CLK_IS_CRITICAL, },
+ { IMX93_CLK_ML_APB, "ml_apb_root", 0x2180, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_ML, "ml_root", 0x2200, FAST_SEL, },
+ { IMX93_CLK_MEDIA_AXI, "media_axi_root", 0x2280, FAST_SEL, },
+ { IMX93_CLK_MEDIA_APB, "media_apb_root", 0x2300, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_MEDIA_LDB, "media_ldb_root", 0x2380, VIDEO_SEL, },
+ { IMX93_CLK_MEDIA_DISP_PIX, "media_disp_pix_root", 0x2400, VIDEO_SEL, },
+ { IMX93_CLK_CAM_PIX, "cam_pix_root", 0x2480, VIDEO_SEL, },
+ { IMX93_CLK_MIPI_TEST_BYTE, "mipi_test_byte_root", 0x2500, VIDEO_SEL, },
+ { IMX93_CLK_MIPI_PHY_CFG, "mipi_phy_cfg_root", 0x2580, VIDEO_SEL, },
+ { IMX93_CLK_ADC, "adc_root", 0x2700, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_PDM, "pdm_root", 0x2780, AUDIO_SEL, },
+ { IMX93_CLK_TSTMR1, "tstmr1_root", 0x2800, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_TSTMR2, "tstmr2_root", 0x2880, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_MQS1, "mqs1_root", 0x2900, AUDIO_SEL, },
+ { IMX93_CLK_MQS2, "mqs2_root", 0x2980, AUDIO_SEL, },
+ { IMX93_CLK_AUDIO_XCVR, "audio_xcvr_root", 0x2a00, NON_IO_SEL, },
+ { IMX93_CLK_SPDIF, "spdif_root", 0x2a80, AUDIO_SEL, },
+ { IMX93_CLK_ENET, "enet_root", 0x2b00, NON_IO_SEL, },
+ { IMX93_CLK_ENET_TIMER1, "enet_timer1_root", 0x2b80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_ENET_TIMER2, "enet_timer2_root", 0x2c00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_ENET_REF, "enet_ref_root", 0x2c80, NON_IO_SEL, },
+ { IMX93_CLK_ENET_REF_PHY, "enet_ref_phy_root", 0x2d00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_I3C1_SLOW, "i3c1_slow_root", 0x2d80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_I3C2_SLOW, "i3c2_slow_root", 0x2e00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_USB_PHY_BURUNIN, "usb_phy_root", 0x2e80, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_PAL_CAME_SCAN, "pal_came_scan_root", 0x2f00, MISC_SEL, }
+};
+
+static const struct imx93_clk_ccgr {
+ u32 clk;
+ char *name;
+ char *parent_name;
+ u32 off;
+ unsigned long flags;
+ u32 *shared_count;
+} ccgr_array[] = {
+ { IMX93_CLK_A55_GATE, "a55_alt", "a55_alt_root", 0x8000, },
+ /* M33 critical clk for system run */
+ { IMX93_CLK_CM33_GATE, "cm33", "m33_root", 0x8040, CLK_IS_CRITICAL },
+ { IMX93_CLK_ADC1_GATE, "adc1", "adc_root", 0x82c0, },
+ { IMX93_CLK_WDOG1_GATE, "wdog1", "osc_24m", 0x8300, },
+ { IMX93_CLK_WDOG2_GATE, "wdog2", "osc_24m", 0x8340, },
+ { IMX93_CLK_WDOG3_GATE, "wdog3", "osc_24m", 0x8380, },
+ { IMX93_CLK_WDOG4_GATE, "wdog4", "osc_24m", 0x83c0, },
+ { IMX93_CLK_WDOG5_GATE, "wdog5", "osc_24m", 0x8400, },
+ { IMX93_CLK_SEMA1_GATE, "sema1", "bus_aon_root", 0x8440, },
+ { IMX93_CLK_SEMA2_GATE, "sema2", "bus_wakeup_root", 0x8480, },
+ { IMX93_CLK_MU1_A_GATE, "mu1_a", "bus_aon_root", 0x84c0, CLK_IGNORE_UNUSED },
+ { IMX93_CLK_MU2_A_GATE, "mu2_a", "bus_wakeup_root", 0x84c0, CLK_IGNORE_UNUSED },
+ { IMX93_CLK_MU1_B_GATE, "mu1_b", "bus_aon_root", 0x8500, 0, &share_count_mub },
+ { IMX93_CLK_MU2_B_GATE, "mu2_b", "bus_wakeup_root", 0x8500, 0, &share_count_mub },
+ { IMX93_CLK_EDMA1_GATE, "edma1", "m33_root", 0x8540, },
+ { IMX93_CLK_EDMA2_GATE, "edma2", "wakeup_axi_root", 0x8580, },
+ { IMX93_CLK_FLEXSPI1_GATE, "flexspi1", "flexspi1_root", 0x8640, },
+ { IMX93_CLK_GPIO1_GATE, "gpio1", "m33_root", 0x8880, },
+ { IMX93_CLK_GPIO2_GATE, "gpio2", "bus_wakeup_root", 0x88c0, },
+ { IMX93_CLK_GPIO3_GATE, "gpio3", "bus_wakeup_root", 0x8900, },
+ { IMX93_CLK_GPIO4_GATE, "gpio4", "bus_wakeup_root", 0x8940, },
+ { IMX93_CLK_FLEXIO1_GATE, "flexio1", "flexio1_root", 0x8980, },
+ { IMX93_CLK_FLEXIO2_GATE, "flexio2", "flexio2_root", 0x89c0, },
+ { IMX93_CLK_LPIT1_GATE, "lpit1", "bus_aon_root", 0x8a00, },
+ { IMX93_CLK_LPIT2_GATE, "lpit2", "bus_wakeup_root", 0x8a40, },
+ { IMX93_CLK_LPTMR1_GATE, "lptmr1", "lptmr1_root", 0x8a80, },
+ { IMX93_CLK_LPTMR2_GATE, "lptmr2", "lptmr2_root", 0x8ac0, },
+ { IMX93_CLK_TPM1_GATE, "tpm1", "bus_aon_root", 0x8b00, },
+ { IMX93_CLK_TPM2_GATE, "tpm2", "tpm2_root", 0x8b40, },
+ { IMX93_CLK_TPM3_GATE, "tpm3", "bus_wakeup_root", 0x8b80, },
+ { IMX93_CLK_TPM4_GATE, "tpm4", "tpm4_root", 0x8bc0, },
+ { IMX93_CLK_TPM5_GATE, "tpm5", "tpm5_root", 0x8c00, },
+ { IMX93_CLK_TPM6_GATE, "tpm6", "tpm6_root", 0x8c40, },
+ { IMX93_CLK_CAN1_GATE, "can1", "can1_root", 0x8c80, },
+ { IMX93_CLK_CAN2_GATE, "can2", "can2_root", 0x8cc0, },
+ { IMX93_CLK_LPUART1_GATE, "lpuart1", "lpuart1_root", 0x8d00, },
+ { IMX93_CLK_LPUART2_GATE, "lpuart2", "lpuart2_root", 0x8d40, },
+ { IMX93_CLK_LPUART3_GATE, "lpuart3", "lpuart3_root", 0x8d80, },
+ { IMX93_CLK_LPUART4_GATE, "lpuart4", "lpuart4_root", 0x8dc0, },
+ { IMX93_CLK_LPUART5_GATE, "lpuart5", "lpuart5_root", 0x8e00, },
+ { IMX93_CLK_LPUART6_GATE, "lpuart6", "lpuart6_root", 0x8e40, },
+ { IMX93_CLK_LPUART7_GATE, "lpuart7", "lpuart7_root", 0x8e80, },
+ { IMX93_CLK_LPUART8_GATE, "lpuart8", "lpuart8_root", 0x8ec0, },
+ { IMX93_CLK_LPI2C1_GATE, "lpi2c1", "lpi2c1_root", 0x8f00, },
+ { IMX93_CLK_LPI2C2_GATE, "lpi2c2", "lpi2c2_root", 0x8f40, },
+ { IMX93_CLK_LPI2C3_GATE, "lpi2c3", "lpi2c3_root", 0x8f80, },
+ { IMX93_CLK_LPI2C4_GATE, "lpi2c4", "lpi2c4_root", 0x8fc0, },
+ { IMX93_CLK_LPI2C5_GATE, "lpi2c5", "lpi2c5_root", 0x9000, },
+ { IMX93_CLK_LPI2C6_GATE, "lpi2c6", "lpi2c6_root", 0x9040, },
+ { IMX93_CLK_LPI2C7_GATE, "lpi2c7", "lpi2c7_root", 0x9080, },
+ { IMX93_CLK_LPI2C8_GATE, "lpi2c8", "lpi2c8_root", 0x90c0, },
+ { IMX93_CLK_LPSPI1_GATE, "lpspi1", "lpspi1_root", 0x9100, },
+ { IMX93_CLK_LPSPI2_GATE, "lpspi2", "lpspi2_root", 0x9140, },
+ { IMX93_CLK_LPSPI3_GATE, "lpspi3", "lpspi3_root", 0x9180, },
+ { IMX93_CLK_LPSPI4_GATE, "lpspi4", "lpspi4_root", 0x91c0, },
+ { IMX93_CLK_LPSPI5_GATE, "lpspi5", "lpspi5_root", 0x9200, },
+ { IMX93_CLK_LPSPI6_GATE, "lpspi6", "lpspi6_root", 0x9240, },
+ { IMX93_CLK_LPSPI7_GATE, "lpspi7", "lpspi7_root", 0x9280, },
+ { IMX93_CLK_LPSPI8_GATE, "lpspi8", "lpspi8_root", 0x92c0, },
+ { IMX93_CLK_I3C1_GATE, "i3c1", "i3c1_root", 0x9300, },
+ { IMX93_CLK_I3C2_GATE, "i3c2", "i3c2_root", 0x9340, },
+ { IMX93_CLK_USDHC1_GATE, "usdhc1", "usdhc1_root", 0x9380, },
+ { IMX93_CLK_USDHC2_GATE, "usdhc2", "usdhc2_root", 0x93c0, },
+ { IMX93_CLK_USDHC3_GATE, "usdhc3", "usdhc3_root", 0x9400, },
+ { IMX93_CLK_SAI1_GATE, "sai1", "sai1_root", 0x9440, 0, &share_count_sai1},
+ { IMX93_CLK_SAI1_IPG, "sai1_ipg_clk", "bus_aon_root", 0x9440, 0, &share_count_sai1},
+ { IMX93_CLK_SAI2_GATE, "sai2", "sai2_root", 0x9480, 0, &share_count_sai2},
+ { IMX93_CLK_SAI2_IPG, "sai2_ipg_clk", "bus_wakeup_root", 0x9480, 0, &share_count_sai2},
+ { IMX93_CLK_SAI3_GATE, "sai3", "sai3_root", 0x94c0, 0, &share_count_sai3},
+ { IMX93_CLK_SAI3_IPG, "sai3_ipg_clk", "bus_wakeup_root", 0x94c0, 0, &share_count_sai3},
+ { IMX93_CLK_MIPI_CSI_GATE, "mipi_csi", "media_apb_root", 0x9580, },
+ { IMX93_CLK_MIPI_DSI_GATE, "mipi_dsi", "media_apb_root", 0x95c0, },
+ { IMX93_CLK_LVDS_GATE, "lvds", "media_ldb_root", 0x9600, },
+ { IMX93_CLK_LCDIF_GATE, "lcdif", "media_apb_root", 0x9640, },
+ { IMX93_CLK_PXP_GATE, "pxp", "media_apb_root", 0x9680, },
+ { IMX93_CLK_ISI_GATE, "isi", "media_apb_root", 0x96c0, },
+ { IMX93_CLK_NIC_MEDIA_GATE, "nic_media", "media_axi_root", 0x9700, },
+ { IMX93_CLK_USB_CONTROLLER_GATE, "usb_controller", "hsio_root", 0x9a00, },
+ { IMX93_CLK_USB_TEST_60M_GATE, "usb_test_60m", "hsio_usb_test_60m_root", 0x9a40, },
+ { IMX93_CLK_HSIO_TROUT_24M_GATE, "hsio_trout_24m", "osc_24m", 0x9a80, },
+ { IMX93_CLK_PDM_GATE, "pdm", "pdm_root", 0x9ac0, 0, &share_count_pdm},
+ { IMX93_CLK_PDM_IPG, "pdm_ipg_clk", "bus_aon_root", 0x9ac0, 0, &share_count_pdm},
+ { IMX93_CLK_MQS1_GATE, "mqs1", "sai1_root", 0x9b00, },
+ { IMX93_CLK_MQS2_GATE, "mqs2", "sai3_root", 0x9b40, },
+ { IMX93_CLK_AUD_XCVR_GATE, "aud_xcvr", "audio_xcvr_root", 0x9b80, },
+ { IMX93_CLK_SPDIF_GATE, "spdif", "spdif_root", 0x9c00, },
+ { IMX93_CLK_HSIO_32K_GATE, "hsio_32k", "osc_32k", 0x9dc0, },
+ { IMX93_CLK_ENET1_GATE, "enet1", "wakeup_axi_root", 0x9e00, },
+ { IMX93_CLK_ENET_QOS_GATE, "enet_qos", "wakeup_axi_root", 0x9e40, },
+ /* Critical because clk accessed during CPU idle */
+ { IMX93_CLK_SYS_CNT_GATE, "sys_cnt", "osc_24m", 0x9e80, CLK_IS_CRITICAL},
+ { IMX93_CLK_TSTMR1_GATE, "tstmr1", "bus_aon_root", 0x9ec0, },
+ { IMX93_CLK_TSTMR2_GATE, "tstmr2", "bus_wakeup_root", 0x9f00, },
+ { IMX93_CLK_TMC_GATE, "tmc", "osc_24m", 0x9f40, },
+ { IMX93_CLK_PMRO_GATE, "pmro", "osc_24m", 0x9f80, }
+};
+
+static struct clk_onecell_data clk_data;
+static struct clk *clks[IMX93_CLK_END];
+
+static int imx93_clocks_probe(struct device_node *np)
+{
+ struct device_node *anatop_np;
+ const struct imx93_clk_root *root;
+ const struct imx93_clk_ccgr *ccgr;
+ void __iomem *base, *anatop_base;
+ int i, ret;
+
+ clk_data.clk_num = IMX93_CLK_END;
+ clk_data.clks = clks;
+
+ clks[IMX93_CLK_DUMMY] = clk_fixed("dummy", 0);
+ clks[IMX93_CLK_24M] = of_clk_get_by_name(np, "osc_24m");
+ clks[IMX93_CLK_32K] = of_clk_get_by_name(np, "osc_32k");
+ clks[IMX93_CLK_EXT1] = of_clk_get_by_name(np, "clk_ext1");
+
+ clks[IMX93_CLK_SYS_PLL_PFD0] = clk_fixed("sys_pll_pfd0", 1000000000);
+ clks[IMX93_CLK_SYS_PLL_PFD0_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd0_div2",
+ "sys_pll_pfd0", 1, 2);
+ clks[IMX93_CLK_SYS_PLL_PFD1] = clk_fixed("sys_pll_pfd1", 800000000);
+ clks[IMX93_CLK_SYS_PLL_PFD1_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd1_div2",
+ "sys_pll_pfd1", 1, 2);
+ clks[IMX93_CLK_SYS_PLL_PFD2] = clk_fixed("sys_pll_pfd2", 625000000);
+ clks[IMX93_CLK_SYS_PLL_PFD2_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd2_div2",
+ "sys_pll_pfd2", 1, 2);
+
+ anatop_np = of_find_compatible_node(NULL, NULL, "fsl,imx93-anatop");
+ anatop_base = of_iomap(anatop_np, 0);
+ if (WARN_ON(IS_ERR(anatop_base)))
+ return PTR_ERR(anatop_base);
+
+ clks[IMX93_CLK_ARM_PLL] = imx_clk_fracn_gppll_integer("arm_pll", "osc_24m",
+ anatop_base + 0x1000,
+ &imx_fracn_gppll_integer);
+ clks[IMX93_CLK_AUDIO_PLL] = imx_clk_fracn_gppll("audio_pll", "osc_24m", anatop_base + 0x1200,
+ &imx_fracn_gppll);
+ clks[IMX93_CLK_VIDEO_PLL] = imx_clk_fracn_gppll("video_pll", "osc_24m", anatop_base + 0x1400,
+ &imx_fracn_gppll);
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(IS_ERR(base)))
+ return PTR_ERR(base);
+
+ for (i = 0; i < ARRAY_SIZE(root_array); i++) {
+ root = &root_array[i];
+ clks[root->clk] = imx93_clk_composite_flags(root->name,
+ parent_names[root->sel],
+ 4, base + root->off, 3,
+ root->flags);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ccgr_array); i++) {
+ ccgr = &ccgr_array[i];
+ clks[ccgr->clk] = imx93_clk_gate(NULL, ccgr->name, ccgr->parent_name,
+ ccgr->flags, base + ccgr->off, 0, 1, 1, 3,
+ ccgr->shared_count);
+ }
+
+ clks[IMX93_CLK_A55_SEL] = imx_clk_hw_mux2("a55_sel", base + 0x4820, 0, 1, a55_core_sels,
+ ARRAY_SIZE(a55_core_sels));
+ clks[IMX93_CLK_A55_CORE] = imx_clk_hw_cpu("a55_core", "a55_sel",
+ clks[IMX93_CLK_A55_SEL],
+ clks[IMX93_CLK_A55_SEL],
+ clks[IMX93_CLK_ARM_PLL],
+ clks[IMX93_CLK_A55_GATE]);
+
+ imx_check_clocks(clks, IMX93_CLK_END);
+
+ ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+CLK_OF_DECLARE(imx93, "fsl,imx93-ccm", imx93_clocks_probe);
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 1bd1fe5c44..112f64df9b 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -13,8 +13,8 @@
#include <linux/clk.h>
#include <notifier.h>
#include <dt-bindings/clock/vf610-clock.h>
-#include <mach/vf610-regs.h>
-#include <mach/vf610-fusemap.h>
+#include <mach/imx/vf610-regs.h>
+#include <mach/imx/vf610-fusemap.h>
#include "clk.h"
@@ -568,13 +568,16 @@ static int vf610_switch_cpu_clock_to_400mhz(void)
static int vf610_switch_cpu_clock(void)
{
int ret;
- bool sense_enable;
+ int sense_enable;
uint32_t speed_grading;
if (!of_machine_is_compatible("fsl,vf610"))
return 0;
sense_enable = imx_ocotp_sense_enable(true);
+ if (sense_enable < 0)
+ return sense_enable;
+
ret = imx_ocotp_read_field(VF610_OCOTP_SPEED_GRADING, &speed_grading);
imx_ocotp_sense_enable(sense_enable);
if (ret < 0)
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index bc44d0c739..32e4903837 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -40,11 +40,19 @@ static inline struct clk *imx_clk_divider_table(const char *name,
width, table, 0);
}
+static inline struct clk *__imx_clk_mux(const char *name, void __iomem *reg,
+ u8 shift, u8 width, const char * const *parents,
+ u8 num_parents, unsigned flags, unsigned long clk_mux_flags)
+{
+ return clk_mux(name, CLK_SET_RATE_NO_REPARENT | flags, reg,
+ shift, width, parents, num_parents, clk_mux_flags);
+}
+
static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents, int num_parents)
{
- return clk_mux(name, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg,
- shift, width, parents, num_parents, CLK_MUX_READ_ONLY);
+ return __imx_clk_mux(name, reg, shift, width, parents, num_parents,
+ CLK_SET_RATE_PARENT, CLK_MUX_READ_ONLY);
}
@@ -59,36 +67,36 @@ static inline struct clk *imx_clk_mux_flags(const char *name, void __iomem *reg,
const char * const *parents, u8 num_parents,
unsigned long clk_flags)
{
- return clk_mux(name, clk_flags, reg, shift, width, parents, num_parents,
- 0);
+ return __imx_clk_mux(name, reg, shift, width, parents, num_parents,
+ clk_flags, 0);
}
static inline struct clk *imx_clk_mux2_flags(const char *name,
void __iomem *reg, u8 shift, u8 width, const char * const *parents,
int num_parents, unsigned long clk_flags)
{
- return clk_mux(name, clk_flags | CLK_OPS_PARENT_ENABLE, reg, shift,
- width, parents, num_parents, 0);
+ return __imx_clk_mux(name,reg, shift, width, parents, num_parents,
+ clk_flags | CLK_OPS_PARENT_ENABLE, 0);
}
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents, u8 num_parents)
{
- return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
+ return __imx_clk_mux(name, reg, shift, width, parents, num_parents, 0, 0);
}
static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents, u8 num_parents)
{
- return clk_mux(name, CLK_OPS_PARENT_ENABLE, reg, shift, width, parents,
- num_parents, 0);
+ return __imx_clk_mux(name, reg, shift, width, parents,
+ num_parents, CLK_OPS_PARENT_ENABLE, 0);
}
static inline struct clk *imx_clk_mux_p(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents, u8 num_parents)
{
- return clk_mux(name, CLK_SET_RATE_PARENT, reg, shift, width, parents,
- num_parents, 0);
+ return __imx_clk_mux(name, reg, shift, width, parents, num_parents,
+ CLK_SET_RATE_PARENT, 0);
}
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
@@ -268,6 +276,11 @@ struct clk *imx_clk_cpu(const char *name, const char *parent_name,
#define IMX_COMPOSITE_CORE BIT(0)
#define IMX_COMPOSITE_BUS BIT(1)
+#define IMX_COMPOSITE_CLK_FLAGS_DEFAULT \
+ (CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
+#define IMX_COMPOSITE_CLK_FLAGS_CRITICAL \
+ (IMX_COMPOSITE_CLK_FLAGS_DEFAULT | CLK_IS_CRITICAL)
+
struct clk *imx8m_clk_composite_flags(const char *name,
const char * const *parent_names, int num_parents, void __iomem *reg,
u32 composite_flags,
@@ -277,13 +290,19 @@ struct clk *imx8m_clk_composite_flags(const char *name,
imx8m_clk_hw_composite_flags(name, parent_names, \
ARRAY_SIZE(parent_names), reg, \
IMX_COMPOSITE_CORE, \
- CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
+ IMX_COMPOSITE_CLK_FLAGS_DEFAULT)
#define imx8m_clk_hw_composite_bus(name, parent_names, reg) \
imx8m_clk_hw_composite_flags(name, parent_names, \
ARRAY_SIZE(parent_names), reg, \
IMX_COMPOSITE_BUS, \
- CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
+ IMX_COMPOSITE_CLK_FLAGS_DEFAULT)
+
+#define imx8m_clk_hw_composite_bus_critical(name, parent_names, reg) \
+ imx8m_clk_hw_composite_flags(name, parent_names, \
+ ARRAY_SIZE(parent_names), reg, \
+ IMX_COMPOSITE_BUS, \
+ IMX_COMPOSITE_CLK_FLAGS_CRITICAL)
#define __imx8m_clk_composite(name, parent_names, reg, flags) \
imx8m_clk_composite_flags(name, parent_names, \
@@ -296,6 +315,22 @@ struct clk *imx8m_clk_composite_flags(const char *name,
#define imx8m_clk_composite_critical(name, parent_names, reg) \
__imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL)
+#include <soc/imx/clk-fracn-gppll.h>
+
+struct clk *imx93_clk_composite_flags(const char *name,
+ const char * const *parent_names,
+ int num_parents,
+ void __iomem *reg,
+ u32 domain_id,
+ unsigned long flags);
+#define imx93_clk_composite(name, parent_names, num_parents, reg, domain_id) \
+ imx93_clk_composite_flags(name, parent_names, num_parents, reg, domain_id \
+ CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
+
+struct clk *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
+ u32 mask, u32 domain_id, unsigned int *share_count);
+
/*
* Names of the above functions used in the Linux Kernel. Added here
* to be able to use the same names in barebox to reduce the diffs
@@ -306,6 +341,7 @@ struct clk *imx8m_clk_composite_flags(const char *name,
#define imx_clk_hw_gate imx_clk_gate
#define imx_clk_hw_fixed_factor imx_clk_fixed_factor
#define imx_clk_hw_mux_flags imx_clk_mux_flags
+#define imx_clk_hw_divider imx_clk_divider
#define imx_clk_hw_divider2 imx_clk_divider2
#define imx_clk_hw_mux2_flags imx_clk_mux2_flags
#define imx_clk_hw_gate4_flags imx_clk_gate4_flags
@@ -315,7 +351,8 @@ struct clk *imx8m_clk_composite_flags(const char *name,
#define imx8m_clk_hw_composite_flags imx8m_clk_composite_flags
#define imx8m_clk_hw_composite imx8m_clk_composite
#define imx8m_clk_hw_composite_critical imx8m_clk_composite_critical
-#define imx_clk_hw_gate2_shared2 imx_clk_gate2_shared2
+#define imx_clk_hw_gate2_shared2(name, parent, reg, shift, shared_count) \
+ ({ (void)shared_count; imx_clk_gate2_shared2(name, parent, reg, shift); })
#define imx_clk_hw_mux2 imx_clk_mux2
#endif /* __IMX_CLK_H */
diff --git a/drivers/clk/loongson/clk-ls1b200.c b/drivers/clk/loongson/clk-ls1b200.c
index 6ac545224f..4e6aa94d52 100644
--- a/drivers/clk/loongson/clk-ls1b200.c
+++ b/drivers/clk/loongson/clk-ls1b200.c
@@ -114,7 +114,7 @@ static void ls1b200_pll_init(void __iomem *base)
10, 1, dc_mux, ARRAY_SIZE(dc_mux), 0);
}
-static int ls1b200_clk_probe(struct device_d *dev)
+static int ls1b200_clk_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -129,7 +129,7 @@ static int ls1b200_clk_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
return 0;
}
@@ -141,8 +141,9 @@ static __maybe_unused struct of_device_id ls1b200_clk_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ls1b200_clk_dt_ids);
-static struct driver_d ls1b200_clk_driver = {
+static struct driver ls1b200_clk_driver = {
.probe = ls1b200_clk_probe,
.name = "ls1b-clk",
.of_compatible = DRV_OF_COMPAT(ls1b200_clk_dt_ids),
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 3d924ccf4d..4ed2193e58 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -38,11 +38,12 @@ static struct of_device_id mvebu_coreclk_ids[] = {
.data = &mv88f6180_coreclks },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_coreclk_ids);
-static int mvebu_coreclk_probe(struct device_d *dev)
+static int mvebu_coreclk_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct of_device_id *match;
const struct coreclk_soc_desc *desc;
const char *tclk_name = "tclk";
@@ -96,7 +97,7 @@ static int mvebu_coreclk_probe(struct device_d *dev)
return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
-static struct driver_d mvebu_coreclk_driver = {
+static struct driver mvebu_coreclk_driver = {
.probe = mvebu_coreclk_probe,
.name = "mvebu-core-clk",
.of_compatible = DRV_OF_COMPAT(mvebu_coreclk_ids),
@@ -147,11 +148,12 @@ static struct of_device_id mvebu_clk_gating_ids[] = {
.data = &kirkwood_gating_desc },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_clk_gating_ids);
-static int mvebu_clk_gating_probe(struct device_d *dev)
+static int mvebu_clk_gating_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct of_device_id *match;
const struct clk_gating_soc_desc *desc;
struct clk_gating_ctrl *ctrl;
@@ -197,7 +199,7 @@ static int mvebu_clk_gating_probe(struct device_d *dev)
return of_clk_add_provider(np, clk_gating_get_src, ctrl);
}
-static struct driver_d mvebu_clk_gating_driver = {
+static struct driver mvebu_clk_gating_driver = {
.probe = mvebu_clk_gating_probe,
.name = "mvebu-clk-gating",
.of_compatible = DRV_OF_COMPAT(mvebu_clk_gating_ids),
diff --git a/drivers/clk/mvebu/corediv.c b/drivers/clk/mvebu/corediv.c
index 1b7fa12701..7ca53faca4 100644
--- a/drivers/clk/mvebu/corediv.c
+++ b/drivers/clk/mvebu/corediv.c
@@ -193,11 +193,12 @@ static struct of_device_id mvebu_corediv_clk_ids[] = {
.data = &armada370_corediv_soc },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_corediv_clk_ids);
-static int mvebu_corediv_clk_probe(struct device_d *dev)
+static int mvebu_corediv_clk_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct of_device_id *match;
const struct clk_corediv_soc_desc *soc_desc;
struct clk_corediv *corediv;
@@ -248,7 +249,7 @@ static int mvebu_corediv_clk_probe(struct device_d *dev)
return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
-static struct driver_d mvebu_corediv_clk_driver = {
+static struct driver mvebu_corediv_clk_driver = {
.probe = mvebu_corediv_clk_probe,
.name = "mvebu-corediv-clk",
.of_compatible = DRV_OF_COMPAT(mvebu_corediv_clk_ids),
diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c
index a211b64f2c..d931adda38 100644
--- a/drivers/clk/mxs/clk-imx23.c
+++ b/drivers/clk/mxs/clk-imx23.c
@@ -10,7 +10,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx23-regs.h>
+#include <mach/mxs/imx23-regs.h>
#include "clk.h"
@@ -112,7 +112,7 @@ static int __init mx23_clocks_init(void __iomem *regs)
return 0;
}
-static int imx23_ccm_probe(struct device_d *dev)
+static int imx23_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -134,8 +134,9 @@ static __maybe_unused struct of_device_id imx23_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx23_ccm_dt_ids);
-static struct driver_d imx23_ccm_driver = {
+static struct driver imx23_ccm_driver = {
.probe = imx23_ccm_probe,
.name = "imx23-clkctrl",
.of_compatible = DRV_OF_COMPAT(imx23_ccm_dt_ids),
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index 382021e49c..c2faddb6f4 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -10,7 +10,7 @@
#include <io.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/imx28-regs.h>
+#include <mach/mxs/imx28-regs.h>
#include <of_address.h>
#include "clk.h"
@@ -67,7 +67,7 @@ enum imx28_clk {
static struct clk *clks[clk_max];
static struct clk_onecell_data clk_data;
-static int __init mx28_clocks_init(struct device_d *dev, void __iomem *regs)
+static int __init mx28_clocks_init(struct device *dev, void __iomem *regs)
{
struct device_node *dcnp;
@@ -147,10 +147,11 @@ static int __init mx28_clocks_init(struct device_d *dev, void __iomem *regs)
clk_set_parent(clks[lcdif_sel], clks[ref_pix]);
clk_set_parent(clks[gpmi_sel], clks[ref_gpmi]);
- if (dev->device_node) {
+ if (dev->of_node) {
clk_data.clks = clks;
clk_data.clk_num = clk_max;
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+ &clk_data);
} else {
clkdev_add_physbase(clks[ssp0], IMX_SSP0_BASE, NULL);
clkdev_add_physbase(clks[ssp1], IMX_SSP1_BASE, NULL);
@@ -174,7 +175,7 @@ static int __init mx28_clocks_init(struct device_d *dev, void __iomem *regs)
return 0;
}
-static int imx28_ccm_probe(struct device_d *dev)
+static int imx28_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -196,8 +197,9 @@ static __maybe_unused struct of_device_id imx28_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx28_ccm_dt_ids);
-static struct driver_d imx28_ccm_driver = {
+static struct driver imx28_ccm_driver = {
.probe = imx28_ccm_probe,
.name = "imx28-clkctrl",
.of_compatible = DRV_OF_COMPAT(imx28_ccm_dt_ids),
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 7adbaf266f..f01014da0c 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_ARCH_RK3188) += clk-rk3188.o
obj-$(CONFIG_ARCH_RK3288) += clk-rk3288.o
obj-$(CONFIG_ARCH_RK3399) += clk-rk3399.o
obj-$(CONFIG_ARCH_RK3568) += clk-rk3568.o
+obj-$(CONFIG_ARCH_RK3588) += clk-rk3588.o rst-rk3588.o
diff --git a/drivers/clk/rockchip/clk-inverter.c b/drivers/clk/rockchip/clk-inverter.c
index 7cdcf15fd8..ea72d8c6b2 100644
--- a/drivers/clk/rockchip/clk-inverter.c
+++ b/drivers/clk/rockchip/clk-inverter.c
@@ -10,7 +10,7 @@
#include <xfuncs.h>
#include <linux/barebox-wrapper.h>
#include <linux/clk.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/spinlock.h>
#include "clk.h"
diff --git a/drivers/clk/rockchip/clk-muxgrf.c b/drivers/clk/rockchip/clk-muxgrf.c
index f06fa69514..e81761422f 100644
--- a/drivers/clk/rockchip/clk-muxgrf.c
+++ b/drivers/clk/rockchip/clk-muxgrf.c
@@ -7,7 +7,7 @@
#include <xfuncs.h>
#include <linux/barebox-wrapper.h>
#include <linux/clk.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/spinlock.h>
#include "clk.h"
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index fdbb016e7f..b4152b03b1 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -17,7 +17,7 @@
#include <linux/barebox-wrapper.h>
#include "clk.h"
#include <xfuncs.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/iopoll.h>
#define PLL_MODE_WIDTH 2
@@ -842,6 +842,213 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
};
/*
+ * PLL used in RK3588
+ */
+
+#define RK3588_PLLCON(i) (i * 0x4)
+#define RK3588_PLLCON0_M_MASK 0x3ff
+#define RK3588_PLLCON0_M_SHIFT 0
+#define RK3588_PLLCON1_P_MASK 0x3f
+#define RK3588_PLLCON1_P_SHIFT 0
+#define RK3588_PLLCON1_S_MASK 0x7
+#define RK3588_PLLCON1_S_SHIFT 6
+#define RK3588_PLLCON2_K_MASK 0xffff
+#define RK3588_PLLCON2_K_SHIFT 0
+#define RK3588_PLLCON1_PWRDOWN BIT(13)
+#define RK3588_PLLCON6_LOCK_STATUS BIT(15)
+
+static int rockchip_rk3588_pll_wait_lock(struct rockchip_clk_pll *pll)
+{
+ u32 pllcon;
+ int ret;
+
+ /*
+ * Lock time typical 250, max 500 input clock cycles @24MHz
+ * So define a very safe maximum of 1000us, meaning 24000 cycles.
+ */
+ ret = readl_poll_timeout(pll->reg_base + RK3588_PLLCON(6),
+ pllcon,
+ pllcon & RK3588_PLLCON6_LOCK_STATUS,
+ 1000);
+ if (ret)
+ pr_err("%s: timeout waiting for pll to lock\n", __func__);
+
+ return ret;
+}
+
+static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll,
+ struct rockchip_pll_rate_table *rate)
+{
+ u32 pllcon;
+
+ pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(0));
+ rate->m = ((pllcon >> RK3588_PLLCON0_M_SHIFT) & RK3588_PLLCON0_M_MASK);
+
+ pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
+ rate->p = ((pllcon >> RK3588_PLLCON1_P_SHIFT) & RK3588_PLLCON1_P_MASK);
+ rate->s = ((pllcon >> RK3588_PLLCON1_S_SHIFT) & RK3588_PLLCON1_S_MASK);
+
+ pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(2));
+ rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT) & RK3588_PLLCON2_K_MASK);
+}
+
+static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ struct rockchip_pll_rate_table cur;
+ u64 rate64 = prate, postdiv;
+
+ rockchip_rk3588_pll_get_params(pll, &cur);
+
+ rate64 *= cur.m;
+ do_div(rate64, cur.p);
+
+ if (cur.k) {
+ /* fractional mode */
+ u64 frac_rate64 = prate * cur.k;
+
+ postdiv = cur.p * 65535;
+ do_div(frac_rate64, postdiv);
+ rate64 += frac_rate64;
+ }
+ rate64 = rate64 >> cur.s;
+
+ return (unsigned long)rate64;
+}
+
+static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll,
+ const struct rockchip_pll_rate_table *rate)
+{
+ const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
+ struct clk_mux *pll_mux = &pll->pll_mux;
+ struct rockchip_pll_rate_table cur;
+ int rate_change_remuxed = 0;
+ int cur_parent;
+ int ret;
+
+ pr_debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
+ __func__, rate->rate, rate->p, rate->m, rate->s, rate->k);
+
+ rockchip_rk3588_pll_get_params(pll, &cur);
+ cur.rate = 0;
+
+ if (pll->type == pll_rk3588) {
+ cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
+ if (cur_parent == PLL_MODE_NORM) {
+ pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
+ rate_change_remuxed = 1;
+ }
+ }
+
+ /* set pll power down */
+ writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
+ RK3588_PLLCON1_PWRDOWN, 0),
+ pll->reg_base + RK3399_PLLCON(1));
+
+ /* update pll values */
+ writel_relaxed(HIWORD_UPDATE(rate->m, RK3588_PLLCON0_M_MASK, RK3588_PLLCON0_M_SHIFT),
+ pll->reg_base + RK3399_PLLCON(0));
+
+ writel_relaxed(HIWORD_UPDATE(rate->p, RK3588_PLLCON1_P_MASK, RK3588_PLLCON1_P_SHIFT) |
+ HIWORD_UPDATE(rate->s, RK3588_PLLCON1_S_MASK, RK3588_PLLCON1_S_SHIFT),
+ pll->reg_base + RK3399_PLLCON(1));
+
+ writel_relaxed(HIWORD_UPDATE(rate->k, RK3588_PLLCON2_K_MASK, RK3588_PLLCON2_K_SHIFT),
+ pll->reg_base + RK3399_PLLCON(2));
+
+ /* set pll power up */
+ writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
+ pll->reg_base + RK3588_PLLCON(1));
+
+ /* wait for the pll to lock */
+ ret = rockchip_rk3588_pll_wait_lock(pll);
+ if (ret) {
+ pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
+ __func__);
+ rockchip_rk3588_pll_set_params(pll, &cur);
+ }
+
+ if ((pll->type == pll_rk3588) && rate_change_remuxed)
+ pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
+
+ return ret;
+}
+
+static int rockchip_rk3588_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ const struct rockchip_pll_rate_table *rate;
+
+ pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
+ __func__, clk_hw_get_name(hw), drate, prate);
+
+ /* Get required rate settings from table */
+ rate = rockchip_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
+ return rockchip_rk3588_pll_set_params(pll, rate);
+}
+
+static int rockchip_rk3588_pll_enable(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+ writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
+ pll->reg_base + RK3588_PLLCON(1));
+ rockchip_rk3588_pll_wait_lock(pll);
+
+ return 0;
+}
+
+static void rockchip_rk3588_pll_disable(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+ writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN, RK3588_PLLCON1_PWRDOWN, 0),
+ pll->reg_base + RK3588_PLLCON(1));
+}
+
+static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ u32 pllcon = readl(pll->reg_base + RK3588_PLLCON(1));
+
+ return !(pllcon & RK3588_PLLCON1_PWRDOWN);
+}
+
+static int rockchip_rk3588_pll_init(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+ if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
+ return 0;
+
+ return 0;
+}
+
+static const struct clk_ops rockchip_rk3588_pll_clk_norate_ops = {
+ .recalc_rate = rockchip_rk3588_pll_recalc_rate,
+ .enable = rockchip_rk3588_pll_enable,
+ .disable = rockchip_rk3588_pll_disable,
+ .is_enabled = rockchip_rk3588_pll_is_enabled,
+};
+
+static const struct clk_ops rockchip_rk3588_pll_clk_ops = {
+ .recalc_rate = rockchip_rk3588_pll_recalc_rate,
+ .round_rate = rockchip_pll_round_rate,
+ .set_rate = rockchip_rk3588_pll_set_rate,
+ .enable = rockchip_rk3588_pll_enable,
+ .disable = rockchip_rk3588_pll_disable,
+ .is_enabled = rockchip_rk3588_pll_is_enabled,
+ .init = rockchip_rk3588_pll_init,
+};
+
+/*
* Common registering of pll clocks
*/
@@ -889,22 +1096,26 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
if (pll_type == pll_rk3036 ||
pll_type == pll_rk3066 ||
pll_type == pll_rk3328 ||
- pll_type == pll_rk3399)
+ pll_type == pll_rk3399 ||
+ pll_type == pll_rk3588 ||
+ pll_type == pll_rk3588_core)
pll_mux->flags |= CLK_MUX_HIWORD_MASK;
- /* the actual muxing is xin24m, pll-output, xin32k */
- pll_parents[0] = parent_names[0];
- pll_parents[1] = pll_name;
- pll_parents[2] = parent_names[1];
-
init.name = name;
init.flags = CLK_SET_RATE_PARENT;
init.ops = pll->pll_mux_ops;
init.parent_names = pll_parents;
- if (pll_type == pll_rk3328)
+
+ /* the actual muxing is xin24m, pll-output, xin32k */
+ pll_parents[0] = parent_names[0];
+ pll_parents[1] = pll_name;
+
+ if (pll_type == pll_rk3328) {
init.num_parents = 2;
- else
+ } else {
+ pll_parents[2] = parent_names[1];
init.num_parents = ARRAY_SIZE(pll_parents);
+ }
mux_clk = clk_register(NULL, &pll_mux->hw);
if (IS_ERR(mux_clk))
@@ -939,7 +1150,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
switch (pll_type) {
case pll_rk3036:
case pll_rk3328:
- if (!pll->rate_table || IS_ERR(ctx->grf))
+ if (!pll->rate_table)
init.ops = &rockchip_rk3036_pll_clk_norate_ops;
else
init.ops = &rockchip_rk3036_pll_clk_ops;
@@ -956,6 +1167,14 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
else
init.ops = &rockchip_rk3399_pll_clk_ops;
break;
+ case pll_rk3588:
+ case pll_rk3588_core:
+ if (!pll->rate_table)
+ init.ops = &rockchip_rk3588_pll_clk_norate_ops;
+ else
+ init.ops = &rockchip_rk3588_pll_clk_ops;
+ init.flags = flags;
+ break;
default:
pr_warn("%s: Unknown pll type for pll clk %s\n",
__func__, name);
@@ -980,6 +1199,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
return mux_clk;
err_pll:
+ kfree(pll->rate_table);
clk_unregister(mux_clk);
mux_clk = pll_clk;
err_mux:
diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 399d6006c6..d6c2a04711 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -1626,10 +1626,11 @@ static const struct of_device_id clk_rk3399_match_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, clk_rk3399_match_table);
-static int __init clk_rk3399_probe(struct device_d *dev)
+static int __init clk_rk3399_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct of_device_id *match;
const struct clk_rk3399_inits *init_data;
@@ -1644,7 +1645,7 @@ static int __init clk_rk3399_probe(struct device_d *dev)
return 0;
}
-static struct driver_d clk_rk3399_driver = {
+static struct driver clk_rk3399_driver = {
.probe = clk_rk3399_probe,
.name = "clk-rk3399",
.of_compatible = DRV_OF_COMPAT(clk_rk3399_match_table),
diff --git a/drivers/clk/rockchip/clk-rk3568.c b/drivers/clk/rockchip/clk-rk3568.c
index 7f9c29316a..50fbf66d23 100644
--- a/drivers/clk/rockchip/clk-rk3568.c
+++ b/drivers/clk/rockchip/clk-rk3568.c
@@ -73,11 +73,21 @@ static struct rockchip_pll_rate_table rk3568_pll_rates[] = {
RK3036_PLL_RATE(500000000, 1, 125, 6, 1, 1, 0),
RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0),
RK3036_PLL_RATE(312000000, 1, 78, 6, 1, 1, 0),
+ RK3036_PLL_RATE(297000000, 2, 99, 4, 1, 1, 0),
+ RK3036_PLL_RATE(292500000, 1, 195, 4, 4, 1, 0),
+ RK3036_PLL_RATE(241500000, 2, 161, 4, 2, 1, 0),
RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0),
RK3036_PLL_RATE(200000000, 1, 100, 3, 4, 1, 0),
RK3036_PLL_RATE(148500000, 1, 99, 4, 4, 1, 0),
+ RK3036_PLL_RATE(135000000, 2, 45, 4, 1, 1, 0),
+ RK3036_PLL_RATE(126400000, 1, 79, 5, 3, 1, 0),
+ RK3036_PLL_RATE(119000000, 3, 119, 4, 2, 1, 0),
+ RK3036_PLL_RATE(115200000, 1, 24, 5, 1, 1, 0),
+ RK3036_PLL_RATE(108000000, 2, 45, 5, 1, 1, 0),
+ RK3036_PLL_RATE(101000000, 1, 101, 6, 4, 1, 0),
RK3036_PLL_RATE(100000000, 1, 150, 6, 6, 1, 0),
RK3036_PLL_RATE(96000000, 1, 96, 6, 4, 1, 0),
+ RK3036_PLL_RATE(78750000, 4, 315, 6, 4, 1, 0),
RK3036_PLL_RATE(74250000, 2, 99, 4, 4, 1, 0),
{ /* sentinel */ },
};
@@ -1718,10 +1728,11 @@ static const struct of_device_id clk_rk3568_match_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, clk_rk3568_match_table);
-static int __init clk_rk3568_probe(struct device_d *dev)
+static int __init clk_rk3568_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct clk_rk3568_inits *init_data;
init_data = of_device_get_match_data(dev);
@@ -1731,7 +1742,7 @@ static int __init clk_rk3568_probe(struct device_d *dev)
return 0;
}
-static struct driver_d clk_rk3568_driver = {
+static struct driver clk_rk3568_driver = {
.probe = clk_rk3568_probe,
.name = "clk-rk3568",
.of_compatible = DRV_OF_COMPAT(clk_rk3568_match_table),
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
new file mode 100644
index 0000000000..68565a188c
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rk3588.c
@@ -0,0 +1,2533 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ * Author: Elaine Zhang <zhangqing@rock-chips.com>
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <of.h>
+#include <of_address.h>
+#include <linux/barebox-wrapper.h>
+#include <init.h>
+#include <linux/spinlock.h>
+#include <of_device.h>
+#include <dt-bindings/clock/rockchip,rk3588-cru.h>
+#include "clk.h"
+
+/*
+ * GATE with additional linked clock. Downstream enables the linked clock
+ * (via runtime PM) whenever the gate is enabled. The downstream implementation
+ * does this via separate clock nodes for each of the linked gate clocks,
+ * which leaks parts of the clock tree into DT. It is unclear why this is
+ * actually needed and things work without it for simple use cases. Thus
+ * the linked clock is ignored for now.
+ */
+#define GATE_LINK(_id, cname, pname, linkname, f, o, b, gf) \
+ GATE(_id, cname, pname, f, o, b, gf)
+
+
+#define RK3588_GRF_SOC_STATUS0 0x600
+#define RK3588_PHYREF_ALT_GATE 0xc38
+
+enum rk3588_plls {
+ b0pll, b1pll, lpll, v0pll, aupll, cpll, gpll, npll, ppll,
+};
+
+static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
+ /* _mhz, _p, _m, _s, _k */
+ RK3588_PLL_RATE(2520000000, 2, 210, 0, 0),
+ RK3588_PLL_RATE(2496000000, 2, 208, 0, 0),
+ RK3588_PLL_RATE(2472000000, 2, 206, 0, 0),
+ RK3588_PLL_RATE(2448000000, 2, 204, 0, 0),
+ RK3588_PLL_RATE(2424000000, 2, 202, 0, 0),
+ RK3588_PLL_RATE(2400000000, 2, 200, 0, 0),
+ RK3588_PLL_RATE(2376000000, 2, 198, 0, 0),
+ RK3588_PLL_RATE(2352000000, 2, 196, 0, 0),
+ RK3588_PLL_RATE(2328000000, 2, 194, 0, 0),
+ RK3588_PLL_RATE(2304000000, 2, 192, 0, 0),
+ RK3588_PLL_RATE(2280000000, 2, 190, 0, 0),
+ RK3588_PLL_RATE(2256000000, 2, 376, 1, 0),
+ RK3588_PLL_RATE(2232000000, 2, 372, 1, 0),
+ RK3588_PLL_RATE(2208000000, 2, 368, 1, 0),
+ RK3588_PLL_RATE(2184000000, 2, 364, 1, 0),
+ RK3588_PLL_RATE(2160000000, 2, 360, 1, 0),
+ RK3588_PLL_RATE(2136000000, 2, 356, 1, 0),
+ RK3588_PLL_RATE(2112000000, 2, 352, 1, 0),
+ RK3588_PLL_RATE(2088000000, 2, 348, 1, 0),
+ RK3588_PLL_RATE(2064000000, 2, 344, 1, 0),
+ RK3588_PLL_RATE(2040000000, 2, 340, 1, 0),
+ RK3588_PLL_RATE(2016000000, 2, 336, 1, 0),
+ RK3588_PLL_RATE(1992000000, 2, 332, 1, 0),
+ RK3588_PLL_RATE(1968000000, 2, 328, 1, 0),
+ RK3588_PLL_RATE(1944000000, 2, 324, 1, 0),
+ RK3588_PLL_RATE(1920000000, 2, 320, 1, 0),
+ RK3588_PLL_RATE(1896000000, 2, 316, 1, 0),
+ RK3588_PLL_RATE(1872000000, 2, 312, 1, 0),
+ RK3588_PLL_RATE(1848000000, 2, 308, 1, 0),
+ RK3588_PLL_RATE(1824000000, 2, 304, 1, 0),
+ RK3588_PLL_RATE(1800000000, 2, 300, 1, 0),
+ RK3588_PLL_RATE(1776000000, 2, 296, 1, 0),
+ RK3588_PLL_RATE(1752000000, 2, 292, 1, 0),
+ RK3588_PLL_RATE(1728000000, 2, 288, 1, 0),
+ RK3588_PLL_RATE(1704000000, 2, 284, 1, 0),
+ RK3588_PLL_RATE(1680000000, 2, 280, 1, 0),
+ RK3588_PLL_RATE(1656000000, 2, 276, 1, 0),
+ RK3588_PLL_RATE(1632000000, 2, 272, 1, 0),
+ RK3588_PLL_RATE(1608000000, 2, 268, 1, 0),
+ RK3588_PLL_RATE(1584000000, 2, 264, 1, 0),
+ RK3588_PLL_RATE(1560000000, 2, 260, 1, 0),
+ RK3588_PLL_RATE(1536000000, 2, 256, 1, 0),
+ RK3588_PLL_RATE(1512000000, 2, 252, 1, 0),
+ RK3588_PLL_RATE(1488000000, 2, 248, 1, 0),
+ RK3588_PLL_RATE(1464000000, 2, 244, 1, 0),
+ RK3588_PLL_RATE(1440000000, 2, 240, 1, 0),
+ RK3588_PLL_RATE(1416000000, 2, 236, 1, 0),
+ RK3588_PLL_RATE(1392000000, 2, 232, 1, 0),
+ RK3588_PLL_RATE(1320000000, 2, 220, 1, 0),
+ RK3588_PLL_RATE(1200000000, 2, 200, 1, 0),
+ RK3588_PLL_RATE(1188000000, 2, 198, 1, 0),
+ RK3588_PLL_RATE(1100000000, 3, 550, 2, 0),
+ RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
+ RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
+ RK3588_PLL_RATE(983040000, 4, 655, 2, 23592),
+ RK3588_PLL_RATE(955520000, 3, 477, 2, 49806),
+ RK3588_PLL_RATE(903168000, 6, 903, 2, 11009),
+ RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
+ RK3588_PLL_RATE(850000000, 3, 425, 2, 0),
+ RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
+ RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
+ RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
+ RK3588_PLL_RATE(785560000, 3, 392, 2, 51117),
+ RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
+ RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
+ RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
+ RK3588_PLL_RATE(408000000, 2, 272, 3, 0),
+ RK3588_PLL_RATE(312000000, 2, 208, 3, 0),
+ RK3588_PLL_RATE(216000000, 2, 288, 4, 0),
+ RK3588_PLL_RATE(100000000, 3, 400, 5, 0),
+ RK3588_PLL_RATE(96000000, 2, 256, 5, 0),
+ { /* sentinel */ },
+};
+
+#define RK3588_CLK_CORE_B0_SEL_CLEAN_MASK 0x3
+#define RK3588_CLK_CORE_B0_SEL_CLEAN_SHIFT 13
+#define RK3588_CLK_CORE_B1_SEL_CLEAN_MASK 0x3
+#define RK3588_CLK_CORE_B1_SEL_CLEAN_SHIFT 5
+#define RK3588_CLK_CORE_B0_GPLL_DIV_MASK 0x1f
+#define RK3588_CLK_CORE_B0_GPLL_DIV_SHIFT 1
+#define RK3588_CLK_CORE_L_SEL_CLEAN_MASK 0x3
+#define RK3588_CLK_CORE_L1_SEL_CLEAN_SHIFT 12
+#define RK3588_CLK_CORE_L0_SEL_CLEAN_SHIFT 5
+#define RK3588_CLK_DSU_SEL_DF_MASK 0x1
+#define RK3588_CLK_DSU_SEL_DF_SHIFT 15
+#define RK3588_CLK_DSU_DF_SRC_MASK 0x3
+#define RK3588_CLK_DSU_DF_SRC_SHIFT 12
+#define RK3588_CLK_DSU_DF_DIV_MASK 0x1f
+#define RK3588_CLK_DSU_DF_DIV_SHIFT 7
+#define RK3588_ACLKM_DSU_DIV_MASK 0x1f
+#define RK3588_ACLKM_DSU_DIV_SHIFT 1
+#define RK3588_ACLKS_DSU_DIV_MASK 0x1f
+#define RK3588_ACLKS_DSU_DIV_SHIFT 6
+#define RK3588_ACLKMP_DSU_DIV_MASK 0x1f
+#define RK3588_ACLKMP_DSU_DIV_SHIFT 11
+#define RK3588_PERIPH_DSU_DIV_MASK 0x1f
+#define RK3588_PERIPH_DSU_DIV_SHIFT 0
+#define RK3588_ATCLK_DSU_DIV_MASK 0x1f
+#define RK3588_ATCLK_DSU_DIV_SHIFT 0
+#define RK3588_GICCLK_DSU_DIV_MASK 0x1f
+#define RK3588_GICCLK_DSU_DIV_SHIFT 5
+
+#define RK3588_CORE_B0_SEL(_apllcore) \
+{ \
+ .reg = RK3588_BIGCORE0_CLKSEL_CON(0), \
+ .val = HIWORD_UPDATE(_apllcore, RK3588_CLK_CORE_B0_SEL_CLEAN_MASK, \
+ RK3588_CLK_CORE_B0_SEL_CLEAN_SHIFT) | \
+ HIWORD_UPDATE(0, RK3588_CLK_CORE_B0_GPLL_DIV_MASK, \
+ RK3588_CLK_CORE_B0_GPLL_DIV_SHIFT), \
+}
+
+#define RK3588_CORE_B1_SEL(_apllcore) \
+{ \
+ .reg = RK3588_BIGCORE0_CLKSEL_CON(1), \
+ .val = HIWORD_UPDATE(_apllcore, RK3588_CLK_CORE_B1_SEL_CLEAN_MASK, \
+ RK3588_CLK_CORE_B1_SEL_CLEAN_SHIFT), \
+}
+
+#define RK3588_CORE_B2_SEL(_apllcore) \
+{ \
+ .reg = RK3588_BIGCORE1_CLKSEL_CON(0), \
+ .val = HIWORD_UPDATE(_apllcore, RK3588_CLK_CORE_B0_SEL_CLEAN_MASK, \
+ RK3588_CLK_CORE_B0_SEL_CLEAN_SHIFT) | \
+ HIWORD_UPDATE(0, RK3588_CLK_CORE_B0_GPLL_DIV_MASK, \
+ RK3588_CLK_CORE_B0_GPLL_DIV_SHIFT), \
+}
+
+#define RK3588_CORE_B3_SEL(_apllcore) \
+{ \
+ .reg = RK3588_BIGCORE1_CLKSEL_CON(1), \
+ .val = HIWORD_UPDATE(_apllcore, RK3588_CLK_CORE_B1_SEL_CLEAN_MASK, \
+ RK3588_CLK_CORE_B1_SEL_CLEAN_SHIFT), \
+}
+
+#define RK3588_CORE_L_SEL0(_offs, _apllcore) \
+{ \
+ .reg = RK3588_DSU_CLKSEL_CON(6 + _offs), \
+ .val = HIWORD_UPDATE(_apllcore, RK3588_CLK_CORE_L_SEL_CLEAN_MASK, \
+ RK3588_CLK_CORE_L0_SEL_CLEAN_SHIFT) | \
+ HIWORD_UPDATE(_apllcore, RK3588_CLK_CORE_L_SEL_CLEAN_MASK, \
+ RK3588_CLK_CORE_L1_SEL_CLEAN_SHIFT), \
+}
+
+#define RK3588_CORE_L_SEL1(_seldsu, _divdsu) \
+{ \
+ .reg = RK3588_DSU_CLKSEL_CON(0), \
+ .val = HIWORD_UPDATE(_seldsu, RK3588_CLK_DSU_DF_SRC_MASK, \
+ RK3588_CLK_DSU_DF_SRC_SHIFT) | \
+ HIWORD_UPDATE(_divdsu - 1, RK3588_CLK_DSU_DF_DIV_MASK, \
+ RK3588_CLK_DSU_DF_DIV_SHIFT), \
+}
+
+#define RK3588_CORE_L_SEL2(_aclkm, _aclkmp, _aclks) \
+{ \
+ .reg = RK3588_DSU_CLKSEL_CON(1), \
+ .val = HIWORD_UPDATE(_aclkm - 1, RK3588_ACLKM_DSU_DIV_MASK, \
+ RK3588_ACLKM_DSU_DIV_SHIFT) | \
+ HIWORD_UPDATE(_aclkmp - 1, RK3588_ACLKMP_DSU_DIV_MASK, \
+ RK3588_ACLKMP_DSU_DIV_SHIFT) | \
+ HIWORD_UPDATE(_aclks - 1, RK3588_ACLKS_DSU_DIV_MASK, \
+ RK3588_ACLKS_DSU_DIV_SHIFT), \
+}
+
+#define RK3588_CORE_L_SEL3(_periph) \
+{ \
+ .reg = RK3588_DSU_CLKSEL_CON(2), \
+ .val = HIWORD_UPDATE(_periph - 1, RK3588_PERIPH_DSU_DIV_MASK, \
+ RK3588_PERIPH_DSU_DIV_SHIFT), \
+}
+
+#define RK3588_CORE_L_SEL4(_gicclk, _atclk) \
+{ \
+ .reg = RK3588_DSU_CLKSEL_CON(3), \
+ .val = HIWORD_UPDATE(_gicclk - 1, RK3588_GICCLK_DSU_DIV_MASK, \
+ RK3588_GICCLK_DSU_DIV_SHIFT) | \
+ HIWORD_UPDATE(_atclk - 1, RK3588_ATCLK_DSU_DIV_MASK, \
+ RK3588_ATCLK_DSU_DIV_SHIFT), \
+}
+
+#define RK3588_CPUB01CLK_RATE(_prate, _apllcore) \
+{ \
+ .prate = _prate##U, \
+ .pre_muxs = { \
+ RK3588_CORE_B0_SEL(0), \
+ RK3588_CORE_B1_SEL(0), \
+ }, \
+ .post_muxs = { \
+ RK3588_CORE_B0_SEL(_apllcore), \
+ RK3588_CORE_B1_SEL(_apllcore), \
+ }, \
+}
+
+#define RK3588_CPUB23CLK_RATE(_prate, _apllcore) \
+{ \
+ .prate = _prate##U, \
+ .pre_muxs = { \
+ RK3588_CORE_B2_SEL(0), \
+ RK3588_CORE_B3_SEL(0), \
+ }, \
+ .post_muxs = { \
+ RK3588_CORE_B2_SEL(_apllcore), \
+ RK3588_CORE_B3_SEL(_apllcore), \
+ }, \
+}
+
+#define RK3588_CPULCLK_RATE(_prate, _apllcore, _seldsu, _divdsu) \
+{ \
+ .prate = _prate##U, \
+ .pre_muxs = { \
+ RK3588_CORE_L_SEL0(0, 0), \
+ RK3588_CORE_L_SEL0(1, 0), \
+ RK3588_CORE_L_SEL1(3, 2), \
+ RK3588_CORE_L_SEL2(2, 3, 3), \
+ RK3588_CORE_L_SEL3(4), \
+ RK3588_CORE_L_SEL4(4, 4), \
+ }, \
+ .post_muxs = { \
+ RK3588_CORE_L_SEL0(0, _apllcore), \
+ RK3588_CORE_L_SEL0(1, _apllcore), \
+ RK3588_CORE_L_SEL1(_seldsu, _divdsu), \
+ }, \
+}
+
+static struct rockchip_cpuclk_rate_table rk3588_cpub0clk_rates[] __initdata = {
+ RK3588_CPUB01CLK_RATE(2496000000, 1),
+ RK3588_CPUB01CLK_RATE(2400000000, 1),
+ RK3588_CPUB01CLK_RATE(2304000000, 1),
+ RK3588_CPUB01CLK_RATE(2208000000, 1),
+ RK3588_CPUB01CLK_RATE(2184000000, 1),
+ RK3588_CPUB01CLK_RATE(2088000000, 1),
+ RK3588_CPUB01CLK_RATE(2040000000, 1),
+ RK3588_CPUB01CLK_RATE(2016000000, 1),
+ RK3588_CPUB01CLK_RATE(1992000000, 1),
+ RK3588_CPUB01CLK_RATE(1896000000, 1),
+ RK3588_CPUB01CLK_RATE(1800000000, 1),
+ RK3588_CPUB01CLK_RATE(1704000000, 0),
+ RK3588_CPUB01CLK_RATE(1608000000, 0),
+ RK3588_CPUB01CLK_RATE(1584000000, 0),
+ RK3588_CPUB01CLK_RATE(1560000000, 0),
+ RK3588_CPUB01CLK_RATE(1536000000, 0),
+ RK3588_CPUB01CLK_RATE(1512000000, 0),
+ RK3588_CPUB01CLK_RATE(1488000000, 0),
+ RK3588_CPUB01CLK_RATE(1464000000, 0),
+ RK3588_CPUB01CLK_RATE(1440000000, 0),
+ RK3588_CPUB01CLK_RATE(1416000000, 0),
+ RK3588_CPUB01CLK_RATE(1392000000, 0),
+ RK3588_CPUB01CLK_RATE(1368000000, 0),
+ RK3588_CPUB01CLK_RATE(1344000000, 0),
+ RK3588_CPUB01CLK_RATE(1320000000, 0),
+ RK3588_CPUB01CLK_RATE(1296000000, 0),
+ RK3588_CPUB01CLK_RATE(1272000000, 0),
+ RK3588_CPUB01CLK_RATE(1248000000, 0),
+ RK3588_CPUB01CLK_RATE(1224000000, 0),
+ RK3588_CPUB01CLK_RATE(1200000000, 0),
+ RK3588_CPUB01CLK_RATE(1104000000, 0),
+ RK3588_CPUB01CLK_RATE(1008000000, 0),
+ RK3588_CPUB01CLK_RATE(912000000, 0),
+ RK3588_CPUB01CLK_RATE(816000000, 0),
+ RK3588_CPUB01CLK_RATE(696000000, 0),
+ RK3588_CPUB01CLK_RATE(600000000, 0),
+ RK3588_CPUB01CLK_RATE(408000000, 0),
+ RK3588_CPUB01CLK_RATE(312000000, 0),
+ RK3588_CPUB01CLK_RATE(216000000, 0),
+ RK3588_CPUB01CLK_RATE(96000000, 0),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3588_cpub0clk_data = {
+ .core_reg[0] = RK3588_BIGCORE0_CLKSEL_CON(0),
+ .div_core_shift[0] = 8,
+ .div_core_mask[0] = 0x1f,
+ .core_reg[1] = RK3588_BIGCORE0_CLKSEL_CON(1),
+ .div_core_shift[1] = 0,
+ .div_core_mask[1] = 0x1f,
+ .num_cores = 2,
+ .mux_core_alt = 1,
+ .mux_core_main = 2,
+ .mux_core_shift = 6,
+ .mux_core_mask = 0x3,
+};
+
+static struct rockchip_cpuclk_rate_table rk3588_cpub1clk_rates[] __initdata = {
+ RK3588_CPUB23CLK_RATE(2496000000, 1),
+ RK3588_CPUB23CLK_RATE(2400000000, 1),
+ RK3588_CPUB23CLK_RATE(2304000000, 1),
+ RK3588_CPUB23CLK_RATE(2208000000, 1),
+ RK3588_CPUB23CLK_RATE(2184000000, 1),
+ RK3588_CPUB23CLK_RATE(2088000000, 1),
+ RK3588_CPUB23CLK_RATE(2040000000, 1),
+ RK3588_CPUB23CLK_RATE(2016000000, 1),
+ RK3588_CPUB23CLK_RATE(1992000000, 1),
+ RK3588_CPUB23CLK_RATE(1896000000, 1),
+ RK3588_CPUB23CLK_RATE(1800000000, 1),
+ RK3588_CPUB23CLK_RATE(1704000000, 0),
+ RK3588_CPUB23CLK_RATE(1608000000, 0),
+ RK3588_CPUB23CLK_RATE(1584000000, 0),
+ RK3588_CPUB23CLK_RATE(1560000000, 0),
+ RK3588_CPUB23CLK_RATE(1536000000, 0),
+ RK3588_CPUB23CLK_RATE(1512000000, 0),
+ RK3588_CPUB23CLK_RATE(1488000000, 0),
+ RK3588_CPUB23CLK_RATE(1464000000, 0),
+ RK3588_CPUB23CLK_RATE(1440000000, 0),
+ RK3588_CPUB23CLK_RATE(1416000000, 0),
+ RK3588_CPUB23CLK_RATE(1392000000, 0),
+ RK3588_CPUB23CLK_RATE(1368000000, 0),
+ RK3588_CPUB23CLK_RATE(1344000000, 0),
+ RK3588_CPUB23CLK_RATE(1320000000, 0),
+ RK3588_CPUB23CLK_RATE(1296000000, 0),
+ RK3588_CPUB23CLK_RATE(1272000000, 0),
+ RK3588_CPUB23CLK_RATE(1248000000, 0),
+ RK3588_CPUB23CLK_RATE(1224000000, 0),
+ RK3588_CPUB23CLK_RATE(1200000000, 0),
+ RK3588_CPUB23CLK_RATE(1104000000, 0),
+ RK3588_CPUB23CLK_RATE(1008000000, 0),
+ RK3588_CPUB23CLK_RATE(912000000, 0),
+ RK3588_CPUB23CLK_RATE(816000000, 0),
+ RK3588_CPUB23CLK_RATE(696000000, 0),
+ RK3588_CPUB23CLK_RATE(600000000, 0),
+ RK3588_CPUB23CLK_RATE(408000000, 0),
+ RK3588_CPUB23CLK_RATE(312000000, 0),
+ RK3588_CPUB23CLK_RATE(216000000, 0),
+ RK3588_CPUB23CLK_RATE(96000000, 0),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3588_cpub1clk_data = {
+ .core_reg[0] = RK3588_BIGCORE1_CLKSEL_CON(0),
+ .div_core_shift[0] = 8,
+ .div_core_mask[0] = 0x1f,
+ .core_reg[1] = RK3588_BIGCORE1_CLKSEL_CON(1),
+ .div_core_shift[1] = 0,
+ .div_core_mask[1] = 0x1f,
+ .num_cores = 2,
+ .mux_core_alt = 1,
+ .mux_core_main = 2,
+ .mux_core_shift = 6,
+ .mux_core_mask = 0x3,
+};
+
+static struct rockchip_cpuclk_rate_table rk3588_cpulclk_rates[] __initdata = {
+ RK3588_CPULCLK_RATE(2208000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(2184000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(2088000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(2040000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(2016000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(1992000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(1896000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(1800000000, 1, 3, 1),
+ RK3588_CPULCLK_RATE(1704000000, 0, 3, 1),
+ RK3588_CPULCLK_RATE(1608000000, 0, 3, 1),
+ RK3588_CPULCLK_RATE(1584000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1560000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1536000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1512000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1488000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1464000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1440000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1416000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1392000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1368000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1344000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1320000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1296000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1272000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1248000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1224000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1200000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1104000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(1008000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(912000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(816000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(696000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(600000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(408000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(312000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(216000000, 0, 2, 1),
+ RK3588_CPULCLK_RATE(96000000, 0, 2, 1),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3588_cpulclk_data = {
+ .core_reg[0] = RK3588_DSU_CLKSEL_CON(6),
+ .div_core_shift[0] = 0,
+ .div_core_mask[0] = 0x1f,
+ .core_reg[1] = RK3588_DSU_CLKSEL_CON(6),
+ .div_core_shift[1] = 7,
+ .div_core_mask[1] = 0x1f,
+ .core_reg[2] = RK3588_DSU_CLKSEL_CON(7),
+ .div_core_shift[2] = 0,
+ .div_core_mask[2] = 0x1f,
+ .core_reg[3] = RK3588_DSU_CLKSEL_CON(7),
+ .div_core_shift[3] = 7,
+ .div_core_mask[3] = 0x1f,
+ .num_cores = 4,
+ .mux_core_reg = RK3588_DSU_CLKSEL_CON(5),
+ .mux_core_alt = 1,
+ .mux_core_main = 2,
+ .mux_core_shift = 14,
+ .mux_core_mask = 0x3,
+};
+
+PNAME(mux_pll_p) = { "xin24m", "xin32k" };
+PNAME(mux_armclkl_p) = { "xin24m", "gpll", "lpll" };
+PNAME(mux_armclkb01_p) = { "xin24m", "gpll", "b0pll",};
+PNAME(mux_armclkb23_p) = { "xin24m", "gpll", "b1pll",};
+PNAME(b0pll_b1pll_lpll_gpll_p) = { "b0pll", "b1pll", "lpll", "gpll" };
+PNAME(gpll_24m_p) = { "gpll", "xin24m" };
+PNAME(gpll_aupll_p) = { "gpll", "aupll" };
+PNAME(gpll_lpll_p) = { "gpll", "lpll" };
+PNAME(gpll_cpll_p) = { "gpll", "cpll" };
+PNAME(gpll_spll_p) = { "gpll", "spll" };
+PNAME(gpll_cpll_24m_p) = { "gpll", "cpll", "xin24m"};
+PNAME(gpll_cpll_aupll_p) = { "gpll", "cpll", "aupll"};
+PNAME(gpll_cpll_npll_p) = { "gpll", "cpll", "npll"};
+PNAME(gpll_cpll_npll_v0pll_p) = { "gpll", "cpll", "npll", "v0pll"};
+PNAME(gpll_cpll_24m_spll_p) = { "gpll", "cpll", "xin24m", "spll" };
+PNAME(gpll_cpll_aupll_spll_p) = { "gpll", "cpll", "aupll", "spll" };
+PNAME(gpll_cpll_aupll_npll_p) = { "gpll", "cpll", "aupll", "npll" };
+PNAME(gpll_cpll_v0pll_aupll_p) = { "gpll", "cpll", "v0pll", "aupll" };
+PNAME(gpll_cpll_v0pll_spll_p) = { "gpll", "cpll", "v0pll", "spll" };
+PNAME(gpll_cpll_aupll_npll_spll_p) = { "gpll", "cpll", "aupll", "npll", "spll" };
+PNAME(gpll_cpll_dmyaupll_npll_spll_p) = { "gpll", "cpll", "dummy_aupll", "npll", "spll" };
+PNAME(gpll_cpll_npll_aupll_spll_p) = { "gpll", "cpll", "npll", "aupll", "spll" };
+PNAME(gpll_cpll_npll_1000m_p) = { "gpll", "cpll", "npll", "clk_1000m_src" };
+PNAME(mux_24m_spll_gpll_cpll_p) = { "xin24m", "spll", "gpll", "cpll" };
+PNAME(mux_24m_32k_p) = { "xin24m", "xin32k" };
+PNAME(mux_24m_100m_p) = { "xin24m", "clk_100m_src" };
+PNAME(mux_200m_100m_p) = { "clk_200m_src", "clk_100m_src" };
+PNAME(mux_100m_50m_24m_p) = { "clk_100m_src", "clk_50m_src", "xin24m" };
+PNAME(mux_150m_50m_24m_p) = { "clk_150m_src", "clk_50m_src", "xin24m" };
+PNAME(mux_150m_100m_24m_p) = { "clk_150m_src", "clk_100m_src", "xin24m" };
+PNAME(mux_200m_150m_24m_p) = { "clk_200m_src", "clk_150m_src", "xin24m" };
+PNAME(mux_150m_100m_50m_24m_p) = { "clk_150m_src", "clk_100m_src", "clk_50m_src", "xin24m" };
+PNAME(mux_200m_100m_50m_24m_p) = { "clk_200m_src", "clk_100m_src", "clk_50m_src", "xin24m" };
+PNAME(mux_300m_200m_100m_24m_p) = { "clk_300m_src", "clk_200m_src", "clk_100m_src", "xin24m" };
+PNAME(mux_700m_400m_200m_24m_p) = { "clk_700m_src", "clk_400m_src", "clk_200m_src", "xin24m" };
+PNAME(mux_500m_250m_100m_24m_p) = { "clk_500m_src", "clk_250m_src", "clk_100m_src", "xin24m" };
+PNAME(mux_500m_300m_100m_24m_p) = { "clk_500m_src", "clk_300m_src", "clk_100m_src", "xin24m" };
+PNAME(mux_400m_200m_100m_24m_p) = { "clk_400m_src", "clk_200m_src", "clk_100m_src", "xin24m" };
+PNAME(clk_i2s2_2ch_p) = { "clk_i2s2_2ch_src", "clk_i2s2_2ch_frac", "i2s2_mclkin", "xin12m" };
+PNAME(i2s2_2ch_mclkout_p) = { "mclk_i2s2_2ch", "xin12m" };
+PNAME(clk_i2s3_2ch_p) = { "clk_i2s3_2ch_src", "clk_i2s3_2ch_frac", "i2s3_mclkin", "xin12m" };
+PNAME(i2s3_2ch_mclkout_p) = { "mclk_i2s3_2ch", "xin12m" };
+PNAME(clk_i2s0_8ch_tx_p) = { "clk_i2s0_8ch_tx_src", "clk_i2s0_8ch_tx_frac", "i2s0_mclkin", "xin12m" };
+PNAME(clk_i2s0_8ch_rx_p) = { "clk_i2s0_8ch_rx_src", "clk_i2s0_8ch_rx_frac", "i2s0_mclkin", "xin12m" };
+PNAME(i2s0_8ch_mclkout_p) = { "mclk_i2s0_8ch_tx", "mclk_i2s0_8ch_rx", "xin12m" };
+PNAME(clk_i2s1_8ch_tx_p) = { "clk_i2s1_8ch_tx_src", "clk_i2s1_8ch_tx_frac", "i2s1_mclkin", "xin12m" };
+PNAME(clk_i2s1_8ch_rx_p) = { "clk_i2s1_8ch_rx_src", "clk_i2s1_8ch_rx_frac", "i2s1_mclkin", "xin12m" };
+PNAME(i2s1_8ch_mclkout_p) = { "mclk_i2s1_8ch_tx", "mclk_i2s1_8ch_rx", "xin12m" };
+PNAME(clk_i2s4_8ch_tx_p) = { "clk_i2s4_8ch_tx_src", "clk_i2s4_8ch_tx_frac", "i2s4_mclkin", "xin12m" };
+PNAME(clk_i2s5_8ch_tx_p) = { "clk_i2s5_8ch_tx_src", "clk_i2s5_8ch_tx_frac", "i2s5_mclkin", "xin12m" };
+PNAME(clk_i2s6_8ch_tx_p) = { "clk_i2s6_8ch_tx_src", "clk_i2s6_8ch_tx_frac", "i2s6_mclkin", "xin12m" };
+PNAME(clk_i2s6_8ch_rx_p) = { "clk_i2s6_8ch_rx_src", "clk_i2s6_8ch_rx_frac", "i2s6_mclkin", "xin12m" };
+PNAME(i2s6_8ch_mclkout_p) = { "mclk_i2s6_8ch_tx", "mclk_i2s6_8ch_rx", "xin12m" };
+PNAME(clk_i2s7_8ch_rx_p) = { "clk_i2s7_8ch_rx_src", "clk_i2s7_8ch_rx_frac", "i2s7_mclkin", "xin12m" };
+PNAME(clk_i2s8_8ch_tx_p) = { "clk_i2s8_8ch_tx_src", "clk_i2s8_8ch_tx_frac", "i2s8_mclkin", "xin12m" };
+PNAME(clk_i2s9_8ch_rx_p) = { "clk_i2s9_8ch_rx_src", "clk_i2s9_8ch_rx_frac", "i2s9_mclkin", "xin12m" };
+PNAME(clk_i2s10_8ch_rx_p) = { "clk_i2s10_8ch_rx_src", "clk_i2s10_8ch_rx_frac", "i2s10_mclkin", "xin12m" };
+PNAME(clk_spdif0_p) = { "clk_spdif0_src", "clk_spdif0_frac", "xin12m" };
+PNAME(clk_spdif1_p) = { "clk_spdif1_src", "clk_spdif1_frac", "xin12m" };
+PNAME(clk_spdif2_dp0_p) = { "clk_spdif2_dp0_src", "clk_spdif2_dp0_frac", "xin12m" };
+PNAME(clk_spdif3_p) = { "clk_spdif3_src", "clk_spdif3_frac", "xin12m" };
+PNAME(clk_spdif4_p) = { "clk_spdif4_src", "clk_spdif4_frac", "xin12m" };
+PNAME(clk_spdif5_dp1_p) = { "clk_spdif5_dp1_src", "clk_spdif5_dp1_frac", "xin12m" };
+PNAME(clk_uart0_p) = { "clk_uart0_src", "clk_uart0_frac", "xin24m" };
+PNAME(clk_uart1_p) = { "clk_uart1_src", "clk_uart1_frac", "xin24m" };
+PNAME(clk_uart2_p) = { "clk_uart2_src", "clk_uart2_frac", "xin24m" };
+PNAME(clk_uart3_p) = { "clk_uart3_src", "clk_uart3_frac", "xin24m" };
+PNAME(clk_uart4_p) = { "clk_uart4_src", "clk_uart4_frac", "xin24m" };
+PNAME(clk_uart5_p) = { "clk_uart5_src", "clk_uart5_frac", "xin24m" };
+PNAME(clk_uart6_p) = { "clk_uart6_src", "clk_uart6_frac", "xin24m" };
+PNAME(clk_uart7_p) = { "clk_uart7_src", "clk_uart7_frac", "xin24m" };
+PNAME(clk_uart8_p) = { "clk_uart8_src", "clk_uart8_frac", "xin24m" };
+PNAME(clk_uart9_p) = { "clk_uart9_src", "clk_uart9_frac", "xin24m" };
+PNAME(clk_gmac0_ptp_ref_p) = { "cpll", "clk_gmac0_ptpref_io" };
+PNAME(clk_gmac1_ptp_ref_p) = { "cpll", "clk_gmac1_ptpref_io" };
+PNAME(clk_hdmirx_aud_p) = { "clk_hdmirx_aud_src", "clk_hdmirx_aud_frac" };
+PNAME(aclk_hdcp1_root_p) = { "gpll", "cpll", "clk_hdmitrx_refsrc" };
+PNAME(aclk_vop_sub_src_p) = { "aclk_vop_root", "aclk_vop_div2_src" };
+PNAME(dclk_vop0_p) = { "dclk_vop0_src", "clk_hdmiphy_pixel0", "clk_hdmiphy_pixel1" };
+PNAME(dclk_vop1_p) = { "dclk_vop1_src", "clk_hdmiphy_pixel0", "clk_hdmiphy_pixel1" };
+PNAME(dclk_vop2_p) = { "dclk_vop2_src", "clk_hdmiphy_pixel0", "clk_hdmiphy_pixel1" };
+PNAME(pmu_200m_100m_p) = { "clk_pmu1_200m_src", "clk_pmu1_100m_src" };
+PNAME(pmu_300m_24m_p) = { "clk_300m_src", "xin24m" };
+PNAME(pmu_400m_24m_p) = { "clk_400m_src", "xin24m" };
+PNAME(pmu_100m_50m_24m_src_p) = { "clk_pmu1_100m_src", "clk_pmu1_50m_src", "xin24m" };
+PNAME(pmu_24m_32k_100m_src_p) = { "xin24m", "32k", "clk_pmu1_100m_src" };
+PNAME(hclk_pmu1_root_p) = { "clk_pmu1_200m_src", "clk_pmu1_100m_src", "clk_pmu1_50m_src", "xin24m" };
+PNAME(hclk_pmu_cm0_root_p) = { "clk_pmu1_400m_src", "clk_pmu1_200m_src", "clk_pmu1_100m_src", "xin24m" };
+PNAME(mclk_pdm0_p) = { "clk_pmu1_300m_src", "clk_pmu1_200m_src" };
+PNAME(mux_24m_ppll_spll_p) = { "xin24m", "ppll", "spll" };
+PNAME(mux_24m_ppll_p) = { "xin24m", "ppll" };
+PNAME(clk_ref_pipe_phy0_p) = { "clk_ref_pipe_phy0_osc_src", "clk_ref_pipe_phy0_pll_src" };
+PNAME(clk_ref_pipe_phy1_p) = { "clk_ref_pipe_phy1_osc_src", "clk_ref_pipe_phy1_pll_src" };
+PNAME(clk_ref_pipe_phy2_p) = { "clk_ref_pipe_phy2_osc_src", "clk_ref_pipe_phy2_pll_src" };
+
+#define MFLAGS CLK_MUX_HIWORD_MASK
+#define DFLAGS CLK_DIVIDER_HIWORD_MASK
+#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE)
+
+static struct rockchip_clk_branch rk3588_i2s0_8ch_tx_fracmux __initdata =
+ MUX(CLK_I2S0_8CH_TX, "clk_i2s0_8ch_tx", clk_i2s0_8ch_tx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(26), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s0_8ch_rx_fracmux __initdata =
+ MUX(CLK_I2S0_8CH_RX, "clk_i2s0_8ch_rx", clk_i2s0_8ch_rx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(28), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s1_8ch_tx_fracmux __initdata =
+ MUX(CLK_I2S1_8CH_TX, "clk_i2s1_8ch_tx", clk_i2s1_8ch_tx_p, CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(7), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s1_8ch_rx_fracmux __initdata =
+ MUX(CLK_I2S1_8CH_RX, "clk_i2s1_8ch_rx", clk_i2s1_8ch_rx_p, CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(9), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s2_2ch_fracmux __initdata =
+ MUX(CLK_I2S2_2CH, "clk_i2s2_2ch", clk_i2s2_2ch_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(30), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s3_2ch_fracmux __initdata =
+ MUX(CLK_I2S3_2CH, "clk_i2s3_2ch", clk_i2s3_2ch_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(32), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s4_8ch_tx_fracmux __initdata =
+ MUX(CLK_I2S4_8CH_TX, "clk_i2s4_8ch_tx", clk_i2s4_8ch_tx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(120), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s5_8ch_tx_fracmux __initdata =
+ MUX(CLK_I2S5_8CH_TX, "clk_i2s5_8ch_tx", clk_i2s5_8ch_tx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(142), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s6_8ch_tx_fracmux __initdata =
+ MUX(CLK_I2S6_8CH_TX, "clk_i2s6_8ch_tx", clk_i2s6_8ch_tx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(146), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s6_8ch_rx_fracmux __initdata =
+ MUX(CLK_I2S6_8CH_RX, "clk_i2s6_8ch_rx", clk_i2s6_8ch_rx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(148), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s7_8ch_rx_fracmux __initdata =
+ MUX(CLK_I2S7_8CH_RX, "clk_i2s7_8ch_rx", clk_i2s7_8ch_rx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(131), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s8_8ch_tx_fracmux __initdata =
+ MUX(CLK_I2S8_8CH_TX, "clk_i2s8_8ch_tx", clk_i2s8_8ch_tx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(122), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s9_8ch_rx_fracmux __initdata =
+ MUX(CLK_I2S9_8CH_RX, "clk_i2s9_8ch_rx", clk_i2s9_8ch_rx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(155), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_i2s10_8ch_rx_fracmux __initdata =
+ MUX(CLK_I2S10_8CH_RX, "clk_i2s10_8ch_rx", clk_i2s10_8ch_rx_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(157), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_spdif0_fracmux __initdata =
+ MUX(CLK_SPDIF0, "clk_spdif0", clk_spdif0_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(34), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_spdif1_fracmux __initdata =
+ MUX(CLK_SPDIF1, "clk_spdif1", clk_spdif1_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(36), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_spdif2_dp0_fracmux __initdata =
+ MUX(CLK_SPDIF2_DP0, "clk_spdif2_dp0", clk_spdif2_dp0_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(124), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_spdif3_fracmux __initdata =
+ MUX(CLK_SPDIF3, "clk_spdif3", clk_spdif3_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(150), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_spdif4_fracmux __initdata =
+ MUX(CLK_SPDIF4, "clk_spdif4", clk_spdif4_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(152), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_spdif5_dp1_fracmux __initdata =
+ MUX(CLK_SPDIF5_DP1, "clk_spdif5_dp1", clk_spdif5_dp1_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(126), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart0_fracmux __initdata =
+ MUX(CLK_UART0, "clk_uart0", clk_uart0_p, CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(5), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart1_fracmux __initdata =
+ MUX(CLK_UART1, "clk_uart1", clk_uart1_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(43), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart2_fracmux __initdata =
+ MUX(CLK_UART2, "clk_uart2", clk_uart2_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(45), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart3_fracmux __initdata =
+ MUX(CLK_UART3, "clk_uart3", clk_uart3_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(47), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart4_fracmux __initdata =
+ MUX(CLK_UART4, "clk_uart4", clk_uart4_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(49), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart5_fracmux __initdata =
+ MUX(CLK_UART5, "clk_uart5", clk_uart5_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(51), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart6_fracmux __initdata =
+ MUX(CLK_UART6, "clk_uart6", clk_uart6_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(53), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart7_fracmux __initdata =
+ MUX(CLK_UART7, "clk_uart7", clk_uart7_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(55), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart8_fracmux __initdata =
+ MUX(CLK_UART8, "clk_uart8", clk_uart8_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(57), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_uart9_fracmux __initdata =
+ MUX(CLK_UART9, "clk_uart9", clk_uart9_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(59), 0, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3588_hdmirx_aud_fracmux __initdata =
+ MUX(CLK_HDMIRX_AUD_P_MUX, "clk_hdmirx_aud_mux", clk_hdmirx_aud_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(140), 0, 1, MFLAGS);
+
+static struct rockchip_pll_clock rk3588_pll_clks[] __initdata = {
+ [b0pll] = PLL(pll_rk3588_core, PLL_B0PLL, "b0pll", mux_pll_p,
+ CLK_IGNORE_UNUSED, RK3588_B0_PLL_CON(0),
+ RK3588_B0_PLL_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
+ [b1pll] = PLL(pll_rk3588_core, PLL_B1PLL, "b1pll", mux_pll_p,
+ CLK_IGNORE_UNUSED, RK3588_B1_PLL_CON(8),
+ RK3588_B1_PLL_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
+ [lpll] = PLL(pll_rk3588_core, PLL_LPLL, "lpll", mux_pll_p,
+ CLK_IGNORE_UNUSED, RK3588_LPLL_CON(16),
+ RK3588_LPLL_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
+ [v0pll] = PLL(pll_rk3588, PLL_V0PLL, "v0pll", mux_pll_p,
+ 0, RK3588_PLL_CON(88),
+ RK3588_MODE_CON0, 4, 15, 0, rk3588_pll_rates),
+ [aupll] = PLL(pll_rk3588, PLL_AUPLL, "aupll", mux_pll_p,
+ 0, RK3588_PLL_CON(96),
+ RK3588_MODE_CON0, 6, 15, 0, rk3588_pll_rates),
+ [cpll] = PLL(pll_rk3588, PLL_CPLL, "cpll", mux_pll_p,
+ CLK_IGNORE_UNUSED, RK3588_PLL_CON(104),
+ RK3588_MODE_CON0, 8, 15, 0, rk3588_pll_rates),
+ [gpll] = PLL(pll_rk3588, PLL_GPLL, "gpll", mux_pll_p,
+ CLK_IGNORE_UNUSED, RK3588_PLL_CON(112),
+ RK3588_MODE_CON0, 2, 15, 0, rk3588_pll_rates),
+ [npll] = PLL(pll_rk3588, PLL_NPLL, "npll", mux_pll_p,
+ 0, RK3588_PLL_CON(120),
+ RK3588_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
+ [ppll] = PLL(pll_rk3588_core, PLL_PPLL, "ppll", mux_pll_p,
+ CLK_IGNORE_UNUSED, RK3588_PMU_PLL_CON(128),
+ RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
+};
+
+static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
+ /*
+ * CRU Clock-Architecture
+ */
+ /* fixed */
+ FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
+ /* top */
+ COMPOSITE(CLK_50M_SRC, "clk_50m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 0, GFLAGS),
+ COMPOSITE(CLK_100M_SRC, "clk_100m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(0), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 1, GFLAGS),
+ COMPOSITE(CLK_150M_SRC, "clk_150m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(1), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 2, GFLAGS),
+ COMPOSITE(CLK_200M_SRC, "clk_200m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(1), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 3, GFLAGS),
+ COMPOSITE(CLK_250M_SRC, "clk_250m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(2), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 4, GFLAGS),
+ COMPOSITE(CLK_300M_SRC, "clk_300m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(2), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 5, GFLAGS),
+ COMPOSITE(CLK_350M_SRC, "clk_350m_src", gpll_spll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(3), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 6, GFLAGS),
+ COMPOSITE(CLK_400M_SRC, "clk_400m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(3), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 7, GFLAGS),
+ COMPOSITE_HALFDIV(CLK_450M_SRC, "clk_450m_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(4), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 8, GFLAGS),
+ COMPOSITE(CLK_500M_SRC, "clk_500m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(4), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 9, GFLAGS),
+ COMPOSITE(CLK_600M_SRC, "clk_600m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(5), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 10, GFLAGS),
+ COMPOSITE(CLK_650M_SRC, "clk_650m_src", gpll_lpll_p, 0,
+ RK3588_CLKSEL_CON(5), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 11, GFLAGS),
+ COMPOSITE(CLK_700M_SRC, "clk_700m_src", gpll_spll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(6), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 12, GFLAGS),
+ COMPOSITE(CLK_800M_SRC, "clk_800m_src", gpll_aupll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(6), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 13, GFLAGS),
+ COMPOSITE_HALFDIV(CLK_1000M_SRC, "clk_1000m_src", gpll_cpll_npll_v0pll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(7), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 14, GFLAGS),
+ COMPOSITE(CLK_1200M_SRC, "clk_1200m_src", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(7), 12, 1, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(0), 15, GFLAGS),
+ COMPOSITE_NODIV(ACLK_TOP_M300_ROOT, "aclk_top_m300_root", mux_300m_200m_100m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(9), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(1), 10, GFLAGS),
+ COMPOSITE_NODIV(ACLK_TOP_M500_ROOT, "aclk_top_m500_root", mux_500m_300m_100m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(9), 2, 2, MFLAGS,
+ RK3588_CLKGATE_CON(1), 11, GFLAGS),
+ COMPOSITE_NODIV(ACLK_TOP_M400_ROOT, "aclk_top_m400_root", mux_400m_200m_100m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(9), 4, 2, MFLAGS,
+ RK3588_CLKGATE_CON(1), 12, GFLAGS),
+ COMPOSITE_NODIV(ACLK_TOP_S200_ROOT, "aclk_top_s200_root", mux_200m_100m_50m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(9), 6, 2, MFLAGS,
+ RK3588_CLKGATE_CON(1), 13, GFLAGS),
+ COMPOSITE_NODIV(ACLK_TOP_S400_ROOT, "aclk_top_s400_root", mux_400m_200m_100m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(9), 8, 2, MFLAGS,
+ RK3588_CLKGATE_CON(1), 14, GFLAGS),
+ COMPOSITE(ACLK_TOP_ROOT, "aclk_top_root", gpll_cpll_aupll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(8), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(1), 0, GFLAGS),
+ COMPOSITE_NODIV(PCLK_TOP_ROOT, "pclk_top_root", mux_100m_50m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(8), 7, 2, MFLAGS,
+ RK3588_CLKGATE_CON(1), 1, GFLAGS),
+ COMPOSITE(ACLK_LOW_TOP_ROOT, "aclk_low_top_root", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(8), 14, 1, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(1), 2, GFLAGS),
+ COMPOSITE(CLK_MIPI_CAMARAOUT_M0, "clk_mipi_camaraout_m0", mux_24m_spll_gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(18), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(5), 9, GFLAGS),
+ COMPOSITE(CLK_MIPI_CAMARAOUT_M1, "clk_mipi_camaraout_m1", mux_24m_spll_gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(19), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(5), 10, GFLAGS),
+ COMPOSITE(CLK_MIPI_CAMARAOUT_M2, "clk_mipi_camaraout_m2", mux_24m_spll_gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(20), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(5), 11, GFLAGS),
+ COMPOSITE(CLK_MIPI_CAMARAOUT_M3, "clk_mipi_camaraout_m3", mux_24m_spll_gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(21), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(5), 12, GFLAGS),
+ COMPOSITE(CLK_MIPI_CAMARAOUT_M4, "clk_mipi_camaraout_m4", mux_24m_spll_gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(22), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(5), 13, GFLAGS),
+ COMPOSITE(MCLK_GMAC0_OUT, "mclk_gmac0_out", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(15), 7, 1, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(5), 3, GFLAGS),
+ COMPOSITE(REFCLKO25M_ETH0_OUT, "refclko25m_eth0_out", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(15), 15, 1, MFLAGS, 8, 7, DFLAGS,
+ RK3588_CLKGATE_CON(5), 4, GFLAGS),
+ COMPOSITE(REFCLKO25M_ETH1_OUT, "refclko25m_eth1_out", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(16), 7, 1, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(5), 5, GFLAGS),
+ COMPOSITE(CLK_CIFOUT_OUT, "clk_cifout_out", gpll_cpll_24m_spll_p, 0,
+ RK3588_CLKSEL_CON(17), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(5), 6, GFLAGS),
+ GATE(PCLK_MIPI_DCPHY0, "pclk_mipi_dcphy0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(3), 14, GFLAGS),
+ GATE(PCLK_MIPI_DCPHY1, "pclk_mipi_dcphy1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(4), 3, GFLAGS),
+ GATE(PCLK_CSIPHY0, "pclk_csiphy0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(1), 6, GFLAGS),
+ GATE(PCLK_CSIPHY1, "pclk_csiphy1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(1), 8, GFLAGS),
+ GATE(PCLK_CRU, "pclk_cru", "pclk_top_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(5), 0, GFLAGS),
+
+ /* bigcore0 */
+ COMPOSITE_NODIV(PCLK_BIGCORE0_ROOT, "pclk_bigcore0_root", mux_100m_50m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_BIGCORE0_CLKSEL_CON(2), 0, 2, MFLAGS,
+ RK3588_BIGCORE0_CLKGATE_CON(0), 14, GFLAGS),
+ GATE(PCLK_BIGCORE0_PVTM, "pclk_bigcore0_pvtm", "pclk_bigcore0_root", 0,
+ RK3588_BIGCORE0_CLKGATE_CON(1), 0, GFLAGS),
+ GATE(CLK_BIGCORE0_PVTM, "clk_bigcore0_pvtm", "xin24m", 0,
+ RK3588_BIGCORE0_CLKGATE_CON(0), 12, GFLAGS),
+ GATE(CLK_CORE_BIGCORE0_PVTM, "clk_core_bigcore0_pvtm", "armclk_b01", 0,
+ RK3588_BIGCORE0_CLKGATE_CON(0), 13, GFLAGS),
+
+ /* bigcore1 */
+ COMPOSITE_NODIV(PCLK_BIGCORE1_ROOT, "pclk_bigcore1_root", mux_100m_50m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_BIGCORE1_CLKSEL_CON(2), 0, 2, MFLAGS,
+ RK3588_BIGCORE1_CLKGATE_CON(0), 14, GFLAGS),
+ GATE(PCLK_BIGCORE1_PVTM, "pclk_bigcore1_pvtm", "pclk_bigcore1_root", 0,
+ RK3588_BIGCORE1_CLKGATE_CON(1), 0, GFLAGS),
+ GATE(CLK_BIGCORE1_PVTM, "clk_bigcore1_pvtm", "xin24m", 0,
+ RK3588_BIGCORE1_CLKGATE_CON(0), 12, GFLAGS),
+ GATE(CLK_CORE_BIGCORE1_PVTM, "clk_core_bigcore1_pvtm", "armclk_b23", 0,
+ RK3588_BIGCORE1_CLKGATE_CON(0), 13, GFLAGS),
+
+ /* dsu */
+ COMPOSITE(0, "sclk_dsu", b0pll_b1pll_lpll_gpll_p, CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(0), 12, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_DSU_CLKGATE_CON(0), 4, GFLAGS),
+ COMPOSITE_NOMUX(0, "atclk_dsu", "sclk_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(3), 0, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(1), 0, GFLAGS),
+ COMPOSITE_NOMUX(0, "gicclk_dsu", "sclk_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(3), 5, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(1), 1, GFLAGS),
+ COMPOSITE_NOMUX(0, "aclkmp_dsu", "sclk_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(1), 11, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(0), 12, GFLAGS),
+ COMPOSITE_NOMUX(0, "aclkm_dsu", "sclk_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(1), 1, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(0), 8, GFLAGS),
+ COMPOSITE_NOMUX(0, "aclks_dsu", "sclk_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(1), 6, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(0), 9, GFLAGS),
+ COMPOSITE_NOMUX(0, "periph_dsu", "sclk_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(2), 0, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(0), 13, GFLAGS),
+ COMPOSITE_NOMUX(0, "cntclk_dsu", "periph_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(2), 5, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(0), 14, GFLAGS),
+ COMPOSITE_NOMUX(0, "tsclk_dsu", "periph_dsu", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(2), 10, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ RK3588_DSU_CLKGATE_CON(0), 15, GFLAGS),
+ COMPOSITE_NODIV(PCLK_DSU_S_ROOT, "pclk_dsu_s_root", mux_100m_50m_24m_p, CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(4), 11, 2, MFLAGS,
+ RK3588_DSU_CLKGATE_CON(2), 2, GFLAGS),
+ COMPOSITE(PCLK_DSU_ROOT, "pclk_dsu_root", b0pll_b1pll_lpll_gpll_p, CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(4), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_DSU_CLKGATE_CON(1), 3, GFLAGS),
+ COMPOSITE_NODIV(PCLK_DSU_NS_ROOT, "pclk_dsu_ns_root", mux_100m_50m_24m_p, CLK_IS_CRITICAL,
+ RK3588_DSU_CLKSEL_CON(4), 7, 2, MFLAGS,
+ RK3588_DSU_CLKGATE_CON(1), 4, GFLAGS),
+ GATE(PCLK_LITCORE_PVTM, "pclk_litcore_pvtm", "pclk_dsu_ns_root", 0,
+ RK3588_DSU_CLKGATE_CON(2), 6, GFLAGS),
+ GATE(PCLK_DBG, "pclk_dbg", "pclk_dsu_root", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKGATE_CON(1), 7, GFLAGS),
+ GATE(PCLK_DSU, "pclk_dsu", "pclk_dsu_root", CLK_IS_CRITICAL,
+ RK3588_DSU_CLKGATE_CON(1), 6, GFLAGS),
+ GATE(PCLK_S_DAPLITE, "pclk_s_daplite", "pclk_dsu_ns_root", CLK_IGNORE_UNUSED,
+ RK3588_DSU_CLKGATE_CON(1), 8, GFLAGS),
+ GATE(PCLK_M_DAPLITE, "pclk_m_daplite", "pclk_dsu_root", CLK_IGNORE_UNUSED,
+ RK3588_DSU_CLKGATE_CON(1), 9, GFLAGS),
+ GATE(CLK_LITCORE_PVTM, "clk_litcore_pvtm", "xin24m", 0,
+ RK3588_DSU_CLKGATE_CON(2), 0, GFLAGS),
+ GATE(CLK_CORE_LITCORE_PVTM, "clk_core_litcore_pvtm", "armclk_l", 0,
+ RK3588_DSU_CLKGATE_CON(2), 1, GFLAGS),
+
+ /* audio */
+ COMPOSITE_NODIV(HCLK_AUDIO_ROOT, "hclk_audio_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(24), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(7), 0, GFLAGS),
+ COMPOSITE_NODIV(PCLK_AUDIO_ROOT, "pclk_audio_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(24), 2, 2, MFLAGS,
+ RK3588_CLKGATE_CON(7), 1, GFLAGS),
+ GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_audio_root", 0,
+ RK3588_CLKGATE_CON(7), 12, GFLAGS),
+ GATE(HCLK_I2S3_2CH, "hclk_i2s3_2ch", "hclk_audio_root", 0,
+ RK3588_CLKGATE_CON(7), 13, GFLAGS),
+ COMPOSITE(CLK_I2S2_2CH_SRC, "clk_i2s2_2ch_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(28), 9, 1, MFLAGS, 4, 5, DFLAGS,
+ RK3588_CLKGATE_CON(7), 14, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S2_2CH_FRAC, "clk_i2s2_2ch_frac", "clk_i2s2_2ch_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(29), 0,
+ RK3588_CLKGATE_CON(7), 15, GFLAGS,
+ &rk3588_i2s2_2ch_fracmux),
+ GATE(MCLK_I2S2_2CH, "mclk_i2s2_2ch", "clk_i2s2_2ch", 0,
+ RK3588_CLKGATE_CON(8), 0, GFLAGS),
+ MUX(I2S2_2CH_MCLKOUT, "i2s2_2ch_mclkout", i2s2_2ch_mclkout_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(30), 2, 1, MFLAGS),
+
+ COMPOSITE(CLK_I2S3_2CH_SRC, "clk_i2s3_2ch_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(30), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(8), 1, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S3_2CH_FRAC, "clk_i2s3_2ch_frac", "clk_i2s3_2ch_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(31), 0,
+ RK3588_CLKGATE_CON(8), 2, GFLAGS,
+ &rk3588_i2s3_2ch_fracmux),
+ GATE(MCLK_I2S3_2CH, "mclk_i2s3_2ch", "clk_i2s3_2ch", 0,
+ RK3588_CLKGATE_CON(8), 3, GFLAGS),
+ GATE(CLK_DAC_ACDCDIG, "clk_dac_acdcdig", "mclk_i2s3_2ch", 0,
+ RK3588_CLKGATE_CON(8), 4, GFLAGS),
+ MUX(I2S3_2CH_MCLKOUT, "i2s3_2ch_mclkout", i2s3_2ch_mclkout_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(32), 2, 1, MFLAGS),
+ GATE(PCLK_ACDCDIG, "pclk_acdcdig", "pclk_audio_root", 0,
+ RK3588_CLKGATE_CON(7), 11, GFLAGS),
+ GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_audio_root", 0,
+ RK3588_CLKGATE_CON(7), 4, GFLAGS),
+
+ COMPOSITE(CLK_I2S0_8CH_TX_SRC, "clk_i2s0_8ch_tx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(24), 9, 1, MFLAGS, 4, 5, DFLAGS,
+ RK3588_CLKGATE_CON(7), 5, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S0_8CH_TX_FRAC, "clk_i2s0_8ch_tx_frac", "clk_i2s0_8ch_tx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(25), 0,
+ RK3588_CLKGATE_CON(7), 6, GFLAGS,
+ &rk3588_i2s0_8ch_tx_fracmux),
+ GATE(MCLK_I2S0_8CH_TX, "mclk_i2s0_8ch_tx", "clk_i2s0_8ch_tx", 0,
+ RK3588_CLKGATE_CON(7), 7, GFLAGS),
+
+ COMPOSITE(CLK_I2S0_8CH_RX_SRC, "clk_i2s0_8ch_rx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(26), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(7), 8, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S0_8CH_RX_FRAC, "clk_i2s0_8ch_rx_frac", "clk_i2s0_8ch_rx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(27), 0,
+ RK3588_CLKGATE_CON(7), 9, GFLAGS,
+ &rk3588_i2s0_8ch_rx_fracmux),
+ GATE(MCLK_I2S0_8CH_RX, "mclk_i2s0_8ch_rx", "clk_i2s0_8ch_rx", 0,
+ RK3588_CLKGATE_CON(7), 10, GFLAGS),
+ MUX(I2S0_8CH_MCLKOUT, "i2s0_8ch_mclkout", i2s0_8ch_mclkout_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(28), 2, 2, MFLAGS),
+
+ GATE(HCLK_PDM1, "hclk_pdm1", "hclk_audio_root", 0,
+ RK3588_CLKGATE_CON(9), 6, GFLAGS),
+ COMPOSITE(MCLK_PDM1, "mclk_pdm1", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(36), 7, 2, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(9), 7, GFLAGS),
+
+ GATE(HCLK_SPDIF0, "hclk_spdif0", "hclk_audio_root", 0,
+ RK3588_CLKGATE_CON(8), 14, GFLAGS),
+ COMPOSITE(CLK_SPDIF0_SRC, "clk_spdif0_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(32), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(8), 15, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_SPDIF0_FRAC, "clk_spdif0_frac", "clk_spdif0_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(33), 0,
+ RK3588_CLKGATE_CON(9), 0, GFLAGS,
+ &rk3588_spdif0_fracmux),
+ GATE(MCLK_SPDIF0, "mclk_spdif0", "clk_spdif0", 0,
+ RK3588_CLKGATE_CON(9), 1, GFLAGS),
+
+ GATE(HCLK_SPDIF1, "hclk_spdif1", "hclk_audio_root", 0,
+ RK3588_CLKGATE_CON(9), 2, GFLAGS),
+ COMPOSITE(CLK_SPDIF1_SRC, "clk_spdif1_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(34), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(9), 3, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_SPDIF1_FRAC, "clk_spdif1_frac", "clk_spdif1_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(35), 0,
+ RK3588_CLKGATE_CON(9), 4, GFLAGS,
+ &rk3588_spdif1_fracmux),
+ GATE(MCLK_SPDIF1, "mclk_spdif1", "clk_spdif1", 0,
+ RK3588_CLKGATE_CON(9), 5, GFLAGS),
+
+ COMPOSITE(ACLK_AV1_ROOT, "aclk_av1_root", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(163), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(68), 0, GFLAGS),
+ COMPOSITE_NODIV(PCLK_AV1_ROOT, "pclk_av1_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(163), 7, 2, MFLAGS,
+ RK3588_CLKGATE_CON(68), 3, GFLAGS),
+
+ /* bus */
+ COMPOSITE(ACLK_BUS_ROOT, "aclk_bus_root", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(38), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(10), 0, GFLAGS),
+
+ GATE(PCLK_MAILBOX0, "pclk_mailbox0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(16), 11, GFLAGS),
+ GATE(PCLK_MAILBOX1, "pclk_mailbox1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(16), 12, GFLAGS),
+ GATE(PCLK_MAILBOX2, "pclk_mailbox2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(16), 13, GFLAGS),
+ GATE(PCLK_PMU2, "pclk_pmu2", "pclk_top_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(19), 3, GFLAGS),
+ GATE(PCLK_PMUCM0_INTMUX, "pclk_pmucm0_intmux", "pclk_top_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(19), 4, GFLAGS),
+ GATE(PCLK_DDRCM0_INTMUX, "pclk_ddrcm0_intmux", "pclk_top_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(19), 5, GFLAGS),
+
+ GATE(PCLK_PWM1, "pclk_pwm1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(15), 3, GFLAGS),
+ COMPOSITE_NODIV(CLK_PWM1, "clk_pwm1", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 12, 2, MFLAGS,
+ RK3588_CLKGATE_CON(15), 4, GFLAGS),
+ GATE(CLK_PWM1_CAPTURE, "clk_pwm1_capture", "xin24m", 0,
+ RK3588_CLKGATE_CON(15), 5, GFLAGS),
+ GATE(PCLK_PWM2, "pclk_pwm2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(15), 6, GFLAGS),
+ COMPOSITE_NODIV(CLK_PWM2, "clk_pwm2", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 14, 2, MFLAGS,
+ RK3588_CLKGATE_CON(15), 7, GFLAGS),
+ GATE(CLK_PWM2_CAPTURE, "clk_pwm2_capture", "xin24m", 0,
+ RK3588_CLKGATE_CON(15), 8, GFLAGS),
+ GATE(PCLK_PWM3, "pclk_pwm3", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(15), 9, GFLAGS),
+ COMPOSITE_NODIV(CLK_PWM3, "clk_pwm3", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(60), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(15), 10, GFLAGS),
+ GATE(CLK_PWM3_CAPTURE, "clk_pwm3_capture", "xin24m", 0,
+ RK3588_CLKGATE_CON(15), 11, GFLAGS),
+
+ GATE(PCLK_BUSTIMER0, "pclk_bustimer0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(15), 12, GFLAGS),
+ GATE(PCLK_BUSTIMER1, "pclk_bustimer1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(15), 13, GFLAGS),
+ COMPOSITE_NODIV(CLK_BUS_TIMER_ROOT, "clk_bus_timer_root", mux_24m_100m_p, 0,
+ RK3588_CLKSEL_CON(60), 2, 1, MFLAGS,
+ RK3588_CLKGATE_CON(15), 14, GFLAGS),
+ GATE(CLK_BUSTIMER0, "clk_bustimer0", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(15), 15, GFLAGS),
+ GATE(CLK_BUSTIMER1, "clk_bustimer1", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 0, GFLAGS),
+ GATE(CLK_BUSTIMER2, "clk_bustimer2", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 1, GFLAGS),
+ GATE(CLK_BUSTIMER3, "clk_bustimer3", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 2, GFLAGS),
+ GATE(CLK_BUSTIMER4, "clk_bustimer4", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 3, GFLAGS),
+ GATE(CLK_BUSTIMER5, "clk_bustimer5", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 4, GFLAGS),
+ GATE(CLK_BUSTIMER6, "clk_bustimer6", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 5, GFLAGS),
+ GATE(CLK_BUSTIMER7, "clk_bustimer7", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 6, GFLAGS),
+ GATE(CLK_BUSTIMER8, "clk_bustimer8", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 7, GFLAGS),
+ GATE(CLK_BUSTIMER9, "clk_bustimer9", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 8, GFLAGS),
+ GATE(CLK_BUSTIMER10, "clk_bustimer10", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 9, GFLAGS),
+ GATE(CLK_BUSTIMER11, "clk_bustimer11", "clk_bus_timer_root", 0,
+ RK3588_CLKGATE_CON(16), 10, GFLAGS),
+
+ GATE(PCLK_WDT0, "pclk_wdt0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(15), 0, GFLAGS),
+ GATE(TCLK_WDT0, "tclk_wdt0", "xin24m", 0,
+ RK3588_CLKGATE_CON(15), 1, GFLAGS),
+
+ GATE(PCLK_CAN0, "pclk_can0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(11), 8, GFLAGS),
+ COMPOSITE(CLK_CAN0, "clk_can0", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(39), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(11), 9, GFLAGS),
+ GATE(PCLK_CAN1, "pclk_can1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(11), 10, GFLAGS),
+ COMPOSITE(CLK_CAN1, "clk_can1", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(39), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(11), 11, GFLAGS),
+ GATE(PCLK_CAN2, "pclk_can2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(11), 12, GFLAGS),
+ COMPOSITE(CLK_CAN2, "clk_can2", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(40), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(11), 13, GFLAGS),
+
+ GATE(ACLK_DECOM, "aclk_decom", "aclk_bus_root", 0,
+ RK3588_CLKGATE_CON(17), 6, GFLAGS),
+ GATE(PCLK_DECOM, "pclk_decom", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(17), 7, GFLAGS),
+ COMPOSITE(DCLK_DECOM, "dclk_decom", gpll_spll_p, 0,
+ RK3588_CLKSEL_CON(62), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(17), 8, GFLAGS),
+ GATE(ACLK_DMAC0, "aclk_dmac0", "aclk_bus_root", 0,
+ RK3588_CLKGATE_CON(10), 5, GFLAGS),
+ GATE(ACLK_DMAC1, "aclk_dmac1", "aclk_bus_root", 0,
+ RK3588_CLKGATE_CON(10), 6, GFLAGS),
+ GATE(ACLK_DMAC2, "aclk_dmac2", "aclk_bus_root", 0,
+ RK3588_CLKGATE_CON(10), 7, GFLAGS),
+ GATE(ACLK_GIC, "aclk_gic", "aclk_bus_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(10), 3, GFLAGS),
+
+ GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(16), 14, GFLAGS),
+ COMPOSITE(DBCLK_GPIO1, "dbclk_gpio1", mux_24m_32k_p, 0,
+ RK3588_CLKSEL_CON(60), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(16), 15, GFLAGS),
+ GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(17), 0, GFLAGS),
+ COMPOSITE(DBCLK_GPIO2, "dbclk_gpio2", mux_24m_32k_p, 0,
+ RK3588_CLKSEL_CON(60), 14, 1, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(17), 1, GFLAGS),
+ GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(17), 2, GFLAGS),
+ COMPOSITE(DBCLK_GPIO3, "dbclk_gpio3", mux_24m_32k_p, 0,
+ RK3588_CLKSEL_CON(61), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(17), 3, GFLAGS),
+ GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(17), 4, GFLAGS),
+ COMPOSITE(DBCLK_GPIO4, "dbclk_gpio4", mux_24m_32k_p, 0,
+ RK3588_CLKSEL_CON(61), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(17), 5, GFLAGS),
+
+ GATE(PCLK_I2C1, "pclk_i2c1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 8, GFLAGS),
+ GATE(PCLK_I2C2, "pclk_i2c2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 9, GFLAGS),
+ GATE(PCLK_I2C3, "pclk_i2c3", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 10, GFLAGS),
+ GATE(PCLK_I2C4, "pclk_i2c4", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 11, GFLAGS),
+ GATE(PCLK_I2C5, "pclk_i2c5", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 12, GFLAGS),
+ GATE(PCLK_I2C6, "pclk_i2c6", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 13, GFLAGS),
+ GATE(PCLK_I2C7, "pclk_i2c7", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 14, GFLAGS),
+ GATE(PCLK_I2C8, "pclk_i2c8", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(10), 15, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C1, "clk_i2c1", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 6, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 0, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C2, "clk_i2c2", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 7, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 1, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C3, "clk_i2c3", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 8, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 2, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C4, "clk_i2c4", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 9, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 3, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C5, "clk_i2c5", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 10, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 4, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C6, "clk_i2c6", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 11, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 5, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C7, "clk_i2c7", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 12, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 6, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C8, "clk_i2c8", mux_200m_100m_p, 0,
+ RK3588_CLKSEL_CON(38), 13, 1, MFLAGS,
+ RK3588_CLKGATE_CON(11), 7, GFLAGS),
+
+ GATE(PCLK_OTPC_NS, "pclk_otpc_ns", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(18), 9, GFLAGS),
+ GATE(CLK_OTPC_NS, "clk_otpc_ns", "xin24m", 0,
+ RK3588_CLKGATE_CON(18), 10, GFLAGS),
+ GATE(CLK_OTPC_ARB, "clk_otpc_arb", "xin24m", 0,
+ RK3588_CLKGATE_CON(18), 11, GFLAGS),
+ GATE(CLK_OTP_PHY_G, "clk_otp_phy_g", "xin24m", 0,
+ RK3588_CLKGATE_CON(18), 13, GFLAGS),
+ GATE(CLK_OTPC_AUTO_RD_G, "clk_otpc_auto_rd_g", "xin24m", 0,
+ RK3588_CLKGATE_CON(18), 12, GFLAGS),
+
+ GATE(PCLK_SARADC, "pclk_saradc", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(11), 14, GFLAGS),
+ COMPOSITE(CLK_SARADC, "clk_saradc", gpll_24m_p, 0,
+ RK3588_CLKSEL_CON(40), 14, 1, MFLAGS, 6, 8, DFLAGS,
+ RK3588_CLKGATE_CON(11), 15, GFLAGS),
+
+ GATE(PCLK_SPI0, "pclk_spi0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(14), 6, GFLAGS),
+ GATE(PCLK_SPI1, "pclk_spi1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(14), 7, GFLAGS),
+ GATE(PCLK_SPI2, "pclk_spi2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(14), 8, GFLAGS),
+ GATE(PCLK_SPI3, "pclk_spi3", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(14), 9, GFLAGS),
+ GATE(PCLK_SPI4, "pclk_spi4", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(14), 10, GFLAGS),
+ COMPOSITE_NODIV(CLK_SPI0, "clk_spi0", mux_200m_150m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 2, 2, MFLAGS,
+ RK3588_CLKGATE_CON(14), 11, GFLAGS),
+ COMPOSITE_NODIV(CLK_SPI1, "clk_spi1", mux_200m_150m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 4, 2, MFLAGS,
+ RK3588_CLKGATE_CON(14), 12, GFLAGS),
+ COMPOSITE_NODIV(CLK_SPI2, "clk_spi2", mux_200m_150m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 6, 2, MFLAGS,
+ RK3588_CLKGATE_CON(14), 13, GFLAGS),
+ COMPOSITE_NODIV(CLK_SPI3, "clk_spi3", mux_200m_150m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 8, 2, MFLAGS,
+ RK3588_CLKGATE_CON(14), 14, GFLAGS),
+ COMPOSITE_NODIV(CLK_SPI4, "clk_spi4", mux_200m_150m_24m_p, 0,
+ RK3588_CLKSEL_CON(59), 10, 2, MFLAGS,
+ RK3588_CLKGATE_CON(14), 15, GFLAGS),
+
+ GATE(ACLK_SPINLOCK, "aclk_spinlock", "aclk_bus_root", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(18), 6, GFLAGS),
+ GATE(PCLK_TSADC, "pclk_tsadc", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 0, GFLAGS),
+ COMPOSITE(CLK_TSADC, "clk_tsadc", gpll_24m_p, 0,
+ RK3588_CLKSEL_CON(41), 8, 1, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(12), 1, GFLAGS),
+
+ GATE(PCLK_UART1, "pclk_uart1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 2, GFLAGS),
+ GATE(PCLK_UART2, "pclk_uart2", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 3, GFLAGS),
+ GATE(PCLK_UART3, "pclk_uart3", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 4, GFLAGS),
+ GATE(PCLK_UART4, "pclk_uart4", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 5, GFLAGS),
+ GATE(PCLK_UART5, "pclk_uart5", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 6, GFLAGS),
+ GATE(PCLK_UART6, "pclk_uart6", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 7, GFLAGS),
+ GATE(PCLK_UART7, "pclk_uart7", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 8, GFLAGS),
+ GATE(PCLK_UART8, "pclk_uart8", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 9, GFLAGS),
+ GATE(PCLK_UART9, "pclk_uart9", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(12), 10, GFLAGS),
+
+ COMPOSITE(CLK_UART1_SRC, "clk_uart1_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(41), 14, 1, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(12), 11, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART1_FRAC, "clk_uart1_frac", "clk_uart1_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(42), 0,
+ RK3588_CLKGATE_CON(12), 12, GFLAGS,
+ &rk3588_uart1_fracmux),
+ GATE(SCLK_UART1, "sclk_uart1", "clk_uart1", 0,
+ RK3588_CLKGATE_CON(12), 13, GFLAGS),
+ COMPOSITE(CLK_UART2_SRC, "clk_uart2_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(43), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(12), 14, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART2_FRAC, "clk_uart2_frac", "clk_uart2_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(44), 0,
+ RK3588_CLKGATE_CON(12), 15, GFLAGS,
+ &rk3588_uart2_fracmux),
+ GATE(SCLK_UART2, "sclk_uart2", "clk_uart2", 0,
+ RK3588_CLKGATE_CON(13), 0, GFLAGS),
+ COMPOSITE(CLK_UART3_SRC, "clk_uart3_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(45), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(13), 1, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART3_FRAC, "clk_uart3_frac", "clk_uart3_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(46), 0,
+ RK3588_CLKGATE_CON(13), 2, GFLAGS,
+ &rk3588_uart3_fracmux),
+ GATE(SCLK_UART3, "sclk_uart3", "clk_uart3", 0,
+ RK3588_CLKGATE_CON(13), 3, GFLAGS),
+ COMPOSITE(CLK_UART4_SRC, "clk_uart4_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(47), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(13), 4, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART4_FRAC, "clk_uart4_frac", "clk_uart4_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(48), 0,
+ RK3588_CLKGATE_CON(13), 5, GFLAGS,
+ &rk3588_uart4_fracmux),
+ GATE(SCLK_UART4, "sclk_uart4", "clk_uart4", 0,
+ RK3588_CLKGATE_CON(13), 6, GFLAGS),
+ COMPOSITE(CLK_UART5_SRC, "clk_uart5_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(49), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(13), 7, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART5_FRAC, "clk_uart5_frac", "clk_uart5_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(50), 0,
+ RK3588_CLKGATE_CON(13), 8, GFLAGS,
+ &rk3588_uart5_fracmux),
+ GATE(SCLK_UART5, "sclk_uart5", "clk_uart5", 0,
+ RK3588_CLKGATE_CON(13), 9, GFLAGS),
+ COMPOSITE(CLK_UART6_SRC, "clk_uart6_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(51), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(13), 10, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART6_FRAC, "clk_uart6_frac", "clk_uart6_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(52), 0,
+ RK3588_CLKGATE_CON(13), 11, GFLAGS,
+ &rk3588_uart6_fracmux),
+ GATE(SCLK_UART6, "sclk_uart6", "clk_uart6", 0,
+ RK3588_CLKGATE_CON(13), 12, GFLAGS),
+ COMPOSITE(CLK_UART7_SRC, "clk_uart7_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(53), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(13), 13, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART7_FRAC, "clk_uart7_frac", "clk_uart7_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(54), 0,
+ RK3588_CLKGATE_CON(13), 14, GFLAGS,
+ &rk3588_uart7_fracmux),
+ GATE(SCLK_UART7, "sclk_uart7", "clk_uart7", 0,
+ RK3588_CLKGATE_CON(13), 15, GFLAGS),
+ COMPOSITE(CLK_UART8_SRC, "clk_uart8_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(55), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(14), 0, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART8_FRAC, "clk_uart8_frac", "clk_uart8_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(56), 0,
+ RK3588_CLKGATE_CON(14), 1, GFLAGS,
+ &rk3588_uart8_fracmux),
+ GATE(SCLK_UART8, "sclk_uart8", "clk_uart8", 0,
+ RK3588_CLKGATE_CON(14), 2, GFLAGS),
+ COMPOSITE(CLK_UART9_SRC, "clk_uart9_src", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(57), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(14), 3, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART9_FRAC, "clk_uart9_frac", "clk_uart9_src", CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(58), 0,
+ RK3588_CLKGATE_CON(14), 4, GFLAGS,
+ &rk3588_uart9_fracmux),
+ GATE(SCLK_UART9, "sclk_uart9", "clk_uart9", 0,
+ RK3588_CLKGATE_CON(14), 5, GFLAGS),
+
+ /* center */
+ COMPOSITE_NODIV(ACLK_CENTER_ROOT, "aclk_center_root", mux_700m_400m_200m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(165), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(69), 0, GFLAGS),
+ COMPOSITE_NODIV(ACLK_CENTER_LOW_ROOT, "aclk_center_low_root", mux_500m_250m_100m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(165), 2, 2, MFLAGS,
+ RK3588_CLKGATE_CON(69), 1, GFLAGS),
+ COMPOSITE_NODIV(HCLK_CENTER_ROOT, "hclk_center_root", mux_400m_200m_100m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(165), 4, 2, MFLAGS,
+ RK3588_CLKGATE_CON(69), 2, GFLAGS),
+ COMPOSITE_NODIV(PCLK_CENTER_ROOT, "pclk_center_root", mux_200m_100m_50m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(165), 6, 2, MFLAGS | CLK_MUX_READ_ONLY,
+ RK3588_CLKGATE_CON(69), 3, GFLAGS),
+ GATE(ACLK_DMA2DDR, "aclk_dma2ddr", "aclk_center_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(69), 5, GFLAGS),
+ GATE(ACLK_DDR_SHAREMEM, "aclk_ddr_sharemem", "aclk_center_low_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(69), 6, GFLAGS),
+ COMPOSITE_NODIV(ACLK_CENTER_S200_ROOT, "aclk_center_s200_root", mux_200m_100m_50m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(165), 8, 2, MFLAGS,
+ RK3588_CLKGATE_CON(69), 8, GFLAGS),
+ COMPOSITE_NODIV(ACLK_CENTER_S400_ROOT, "aclk_center_s400_root", mux_400m_200m_100m_24m_p,
+ CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(165), 10, 2, MFLAGS,
+ RK3588_CLKGATE_CON(69), 9, GFLAGS),
+ GATE(FCLK_DDR_CM0_CORE, "fclk_ddr_cm0_core", "hclk_center_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(69), 14, GFLAGS),
+ COMPOSITE_NODIV(CLK_DDR_TIMER_ROOT, "clk_ddr_timer_root", mux_24m_100m_p, CLK_IGNORE_UNUSED,
+ RK3588_CLKSEL_CON(165), 12, 1, MFLAGS,
+ RK3588_CLKGATE_CON(69), 15, GFLAGS),
+ GATE(CLK_DDR_TIMER0, "clk_ddr_timer0", "clk_ddr_timer_root", 0,
+ RK3588_CLKGATE_CON(70), 0, GFLAGS),
+ GATE(CLK_DDR_TIMER1, "clk_ddr_timer1", "clk_ddr_timer_root", 0,
+ RK3588_CLKGATE_CON(70), 1, GFLAGS),
+ GATE(TCLK_WDT_DDR, "tclk_wdt_ddr", "xin24m", 0,
+ RK3588_CLKGATE_CON(70), 2, GFLAGS),
+ COMPOSITE(CLK_DDR_CM0_RTC, "clk_ddr_cm0_rtc", mux_24m_32k_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(166), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(70), 4, GFLAGS),
+ GATE(PCLK_WDT, "pclk_wdt", "pclk_center_root", 0,
+ RK3588_CLKGATE_CON(70), 7, GFLAGS),
+ GATE(PCLK_TIMER, "pclk_timer", "pclk_center_root", 0,
+ RK3588_CLKGATE_CON(70), 8, GFLAGS),
+ GATE(PCLK_DMA2DDR, "pclk_dma2ddr", "pclk_center_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(70), 9, GFLAGS),
+ GATE(PCLK_SHAREMEM, "pclk_sharemem", "pclk_center_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(70), 10, GFLAGS),
+
+ /* gpu */
+ COMPOSITE(CLK_GPU_SRC, "clk_gpu_src", gpll_cpll_aupll_npll_spll_p, 0,
+ RK3588_CLKSEL_CON(158), 5, 3, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(66), 1, GFLAGS),
+ GATE(CLK_GPU, "clk_gpu", "clk_gpu_src", 0,
+ RK3588_CLKGATE_CON(66), 4, GFLAGS),
+ GATE(CLK_GPU_COREGROUP, "clk_gpu_coregroup", "clk_gpu_src", 0,
+ RK3588_CLKGATE_CON(66), 6, GFLAGS),
+ COMPOSITE_NOMUX(CLK_GPU_STACKS, "clk_gpu_stacks", "clk_gpu_src", 0,
+ RK3588_CLKSEL_CON(159), 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(66), 7, GFLAGS),
+ GATE(CLK_GPU_PVTM, "clk_gpu_pvtm", "xin24m", 0,
+ RK3588_CLKGATE_CON(67), 0, GFLAGS),
+ GATE(CLK_CORE_GPU_PVTM, "clk_core_gpu_pvtm", "clk_gpu_src", 0,
+ RK3588_CLKGATE_CON(67), 1, GFLAGS),
+
+ /* isp1 */
+ COMPOSITE(ACLK_ISP1_ROOT, "aclk_isp1_root", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(67), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(26), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_ISP1_ROOT, "hclk_isp1_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(67), 7, 2, MFLAGS,
+ RK3588_CLKGATE_CON(26), 1, GFLAGS),
+ COMPOSITE(CLK_ISP1_CORE, "clk_isp1_core", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(67), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(26), 2, GFLAGS),
+ GATE(CLK_ISP1_CORE_MARVIN, "clk_isp1_core_marvin", "clk_isp1_core", 0,
+ RK3588_CLKGATE_CON(26), 3, GFLAGS),
+ GATE(CLK_ISP1_CORE_VICAP, "clk_isp1_core_vicap", "clk_isp1_core", 0,
+ RK3588_CLKGATE_CON(26), 4, GFLAGS),
+
+ /* npu */
+ COMPOSITE_NODIV(HCLK_NPU_ROOT, "hclk_npu_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(73), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(29), 0, GFLAGS),
+ COMPOSITE(CLK_NPU_DSU0, "clk_npu_dsu0", gpll_cpll_aupll_npll_spll_p, 0,
+ RK3588_CLKSEL_CON(73), 7, 3, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(29), 1, GFLAGS),
+ COMPOSITE_NODIV(PCLK_NPU_ROOT, "pclk_npu_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(74), 1, 2, MFLAGS,
+ RK3588_CLKGATE_CON(29), 4, GFLAGS),
+ GATE(ACLK_NPU1, "aclk_npu1", "clk_npu_dsu0", 0,
+ RK3588_CLKGATE_CON(27), 0, GFLAGS),
+ GATE(HCLK_NPU1, "hclk_npu1", "hclk_npu_root", 0,
+ RK3588_CLKGATE_CON(27), 2, GFLAGS),
+ GATE(ACLK_NPU2, "aclk_npu2", "clk_npu_dsu0", 0,
+ RK3588_CLKGATE_CON(28), 0, GFLAGS),
+ GATE(HCLK_NPU2, "hclk_npu2", "hclk_npu_root", 0,
+ RK3588_CLKGATE_CON(28), 2, GFLAGS),
+ COMPOSITE_NODIV(HCLK_NPU_CM0_ROOT, "hclk_npu_cm0_root", mux_400m_200m_100m_24m_p, 0,
+ RK3588_CLKSEL_CON(74), 5, 2, MFLAGS,
+ RK3588_CLKGATE_CON(30), 1, GFLAGS),
+ GATE(FCLK_NPU_CM0_CORE, "fclk_npu_cm0_core", "hclk_npu_cm0_root", 0,
+ RK3588_CLKGATE_CON(30), 3, GFLAGS),
+ COMPOSITE(CLK_NPU_CM0_RTC, "clk_npu_cm0_rtc", mux_24m_32k_p, 0,
+ RK3588_CLKSEL_CON(74), 12, 1, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(30), 5, GFLAGS),
+ GATE(PCLK_NPU_PVTM, "pclk_npu_pvtm", "pclk_npu_root", 0,
+ RK3588_CLKGATE_CON(29), 12, GFLAGS),
+ GATE(PCLK_NPU_GRF, "pclk_npu_grf", "pclk_npu_root", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(29), 13, GFLAGS),
+ GATE(CLK_NPU_PVTM, "clk_npu_pvtm", "xin24m", 0,
+ RK3588_CLKGATE_CON(29), 14, GFLAGS),
+ GATE(CLK_CORE_NPU_PVTM, "clk_core_npu_pvtm", "clk_npu_dsu0", 0,
+ RK3588_CLKGATE_CON(29), 15, GFLAGS),
+ GATE(ACLK_NPU0, "aclk_npu0", "clk_npu_dsu0", 0,
+ RK3588_CLKGATE_CON(30), 6, GFLAGS),
+ GATE(HCLK_NPU0, "hclk_npu0", "hclk_npu_root", 0,
+ RK3588_CLKGATE_CON(30), 8, GFLAGS),
+ GATE(PCLK_NPU_TIMER, "pclk_npu_timer", "pclk_npu_root", 0,
+ RK3588_CLKGATE_CON(29), 6, GFLAGS),
+ COMPOSITE_NODIV(CLK_NPUTIMER_ROOT, "clk_nputimer_root", mux_24m_100m_p, 0,
+ RK3588_CLKSEL_CON(74), 3, 1, MFLAGS,
+ RK3588_CLKGATE_CON(29), 7, GFLAGS),
+ GATE(CLK_NPUTIMER0, "clk_nputimer0", "clk_nputimer_root", 0,
+ RK3588_CLKGATE_CON(29), 8, GFLAGS),
+ GATE(CLK_NPUTIMER1, "clk_nputimer1", "clk_nputimer_root", 0,
+ RK3588_CLKGATE_CON(29), 9, GFLAGS),
+ GATE(PCLK_NPU_WDT, "pclk_npu_wdt", "pclk_npu_root", 0,
+ RK3588_CLKGATE_CON(29), 10, GFLAGS),
+ GATE(TCLK_NPU_WDT, "tclk_npu_wdt", "xin24m", 0,
+ RK3588_CLKGATE_CON(29), 11, GFLAGS),
+
+ /* nvm */
+ COMPOSITE_NODIV(HCLK_NVM_ROOT, "hclk_nvm_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(77), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(31), 0, GFLAGS),
+ COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(77), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(31), 1, GFLAGS),
+ GATE(ACLK_EMMC, "aclk_emmc", "aclk_nvm_root", 0,
+ RK3588_CLKGATE_CON(31), 5, GFLAGS),
+ COMPOSITE(CCLK_EMMC, "cclk_emmc", gpll_cpll_24m_p, 0,
+ RK3588_CLKSEL_CON(77), 14, 2, MFLAGS, 8, 6, DFLAGS,
+ RK3588_CLKGATE_CON(31), 6, GFLAGS),
+ COMPOSITE(BCLK_EMMC, "bclk_emmc", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(78), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(31), 7, GFLAGS),
+ GATE(TMCLK_EMMC, "tmclk_emmc", "xin24m", 0,
+ RK3588_CLKGATE_CON(31), 8, GFLAGS),
+
+ COMPOSITE(SCLK_SFC, "sclk_sfc", gpll_cpll_24m_p, 0,
+ RK3588_CLKSEL_CON(78), 12, 2, MFLAGS, 6, 6, DFLAGS,
+ RK3588_CLKGATE_CON(31), 9, GFLAGS),
+
+ /* php */
+ COMPOSITE(CLK_GMAC0_PTP_REF, "clk_gmac0_ptp_ref", clk_gmac0_ptp_ref_p, 0,
+ RK3588_CLKSEL_CON(81), 6, 1, MFLAGS, 0, 6, DFLAGS,
+ RK3588_CLKGATE_CON(34), 10, GFLAGS),
+ COMPOSITE(CLK_GMAC1_PTP_REF, "clk_gmac1_ptp_ref", clk_gmac1_ptp_ref_p, 0,
+ RK3588_CLKSEL_CON(81), 13, 1, MFLAGS, 7, 6, DFLAGS,
+ RK3588_CLKGATE_CON(34), 11, GFLAGS),
+ COMPOSITE(CLK_GMAC_125M, "clk_gmac_125m", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(83), 15, 1, MFLAGS, 8, 7, DFLAGS,
+ RK3588_CLKGATE_CON(35), 5, GFLAGS),
+ COMPOSITE(CLK_GMAC_50M, "clk_gmac_50m", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(84), 7, 1, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(35), 6, GFLAGS),
+
+ COMPOSITE(ACLK_PCIE_ROOT, "aclk_pcie_root", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(80), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(32), 6, GFLAGS),
+ COMPOSITE(ACLK_PHP_ROOT, "aclk_php_root", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(80), 13, 1, MFLAGS, 8, 5, DFLAGS,
+ RK3588_CLKGATE_CON(32), 7, GFLAGS),
+ COMPOSITE_NODIV(PCLK_PHP_ROOT, "pclk_php_root", mux_150m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(80), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(32), 0, GFLAGS),
+ GATE(ACLK_PHP_GIC_ITS, "aclk_php_gic_its", "aclk_pcie_root", CLK_IS_CRITICAL,
+ RK3588_CLKGATE_CON(34), 6, GFLAGS),
+ GATE(ACLK_PCIE_BRIDGE, "aclk_pcie_bridge", "aclk_pcie_root", 0,
+ RK3588_CLKGATE_CON(32), 8, GFLAGS),
+ GATE(ACLK_MMU_PCIE, "aclk_mmu_pcie", "aclk_pcie_bridge", 0,
+ RK3588_CLKGATE_CON(34), 7, GFLAGS),
+ GATE(ACLK_MMU_PHP, "aclk_mmu_php", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(34), 8, GFLAGS),
+ GATE(ACLK_PCIE_4L_DBI, "aclk_pcie_4l_dbi", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(32), 13, GFLAGS),
+ GATE(ACLK_PCIE_2L_DBI, "aclk_pcie_2l_dbi", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(32), 14, GFLAGS),
+ GATE(ACLK_PCIE_1L0_DBI, "aclk_pcie_1l0_dbi", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(32), 15, GFLAGS),
+ GATE(ACLK_PCIE_1L1_DBI, "aclk_pcie_1l1_dbi", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 0, GFLAGS),
+ GATE(ACLK_PCIE_1L2_DBI, "aclk_pcie_1l2_dbi", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 1, GFLAGS),
+ GATE(ACLK_PCIE_4L_MSTR, "aclk_pcie_4l_mstr", "aclk_mmu_pcie", 0,
+ RK3588_CLKGATE_CON(33), 2, GFLAGS),
+ GATE(ACLK_PCIE_2L_MSTR, "aclk_pcie_2l_mstr", "aclk_mmu_pcie", 0,
+ RK3588_CLKGATE_CON(33), 3, GFLAGS),
+ GATE(ACLK_PCIE_1L0_MSTR, "aclk_pcie_1l0_mstr", "aclk_mmu_pcie", 0,
+ RK3588_CLKGATE_CON(33), 4, GFLAGS),
+ GATE(ACLK_PCIE_1L1_MSTR, "aclk_pcie_1l1_mstr", "aclk_mmu_pcie", 0,
+ RK3588_CLKGATE_CON(33), 5, GFLAGS),
+ GATE(ACLK_PCIE_1L2_MSTR, "aclk_pcie_1l2_mstr", "aclk_mmu_pcie", 0,
+ RK3588_CLKGATE_CON(33), 6, GFLAGS),
+ GATE(ACLK_PCIE_4L_SLV, "aclk_pcie_4l_slv", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 7, GFLAGS),
+ GATE(ACLK_PCIE_2L_SLV, "aclk_pcie_2l_slv", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 8, GFLAGS),
+ GATE(ACLK_PCIE_1L0_SLV, "aclk_pcie_1l0_slv", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 9, GFLAGS),
+ GATE(ACLK_PCIE_1L1_SLV, "aclk_pcie_1l1_slv", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 10, GFLAGS),
+ GATE(ACLK_PCIE_1L2_SLV, "aclk_pcie_1l2_slv", "aclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 11, GFLAGS),
+ GATE(PCLK_PCIE_4L, "pclk_pcie_4l", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 12, GFLAGS),
+ GATE(PCLK_PCIE_2L, "pclk_pcie_2l", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 13, GFLAGS),
+ GATE(PCLK_PCIE_1L0, "pclk_pcie_1l0", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 14, GFLAGS),
+ GATE(PCLK_PCIE_1L1, "pclk_pcie_1l1", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(33), 15, GFLAGS),
+ GATE(PCLK_PCIE_1L2, "pclk_pcie_1l2", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(34), 0, GFLAGS),
+ GATE(CLK_PCIE_AUX0, "clk_pcie_aux0", "xin24m", 0,
+ RK3588_CLKGATE_CON(34), 1, GFLAGS),
+ GATE(CLK_PCIE_AUX1, "clk_pcie_aux1", "xin24m", 0,
+ RK3588_CLKGATE_CON(34), 2, GFLAGS),
+ GATE(CLK_PCIE_AUX2, "clk_pcie_aux2", "xin24m", 0,
+ RK3588_CLKGATE_CON(34), 3, GFLAGS),
+ GATE(CLK_PCIE_AUX3, "clk_pcie_aux3", "xin24m", 0,
+ RK3588_CLKGATE_CON(34), 4, GFLAGS),
+ GATE(CLK_PCIE_AUX4, "clk_pcie_aux4", "xin24m", 0,
+ RK3588_CLKGATE_CON(34), 5, GFLAGS),
+ GATE(CLK_PIPEPHY0_REF, "clk_pipephy0_ref", "xin24m", 0,
+ RK3588_CLKGATE_CON(37), 0, GFLAGS),
+ GATE(CLK_PIPEPHY1_REF, "clk_pipephy1_ref", "xin24m", 0,
+ RK3588_CLKGATE_CON(37), 1, GFLAGS),
+ GATE(CLK_PIPEPHY2_REF, "clk_pipephy2_ref", "xin24m", 0,
+ RK3588_CLKGATE_CON(37), 2, GFLAGS),
+ GATE(PCLK_GMAC0, "pclk_gmac0", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(32), 3, GFLAGS),
+ GATE(PCLK_GMAC1, "pclk_gmac1", "pclk_php_root", 0,
+ RK3588_CLKGATE_CON(32), 4, GFLAGS),
+ GATE(ACLK_GMAC0, "aclk_gmac0", "aclk_mmu_php", 0,
+ RK3588_CLKGATE_CON(32), 10, GFLAGS),
+ GATE(ACLK_GMAC1, "aclk_gmac1", "aclk_mmu_php", 0,
+ RK3588_CLKGATE_CON(32), 11, GFLAGS),
+ GATE(CLK_PMALIVE0, "clk_pmalive0", "xin24m", 0,
+ RK3588_CLKGATE_CON(37), 4, GFLAGS),
+ GATE(CLK_PMALIVE1, "clk_pmalive1", "xin24m", 0,
+ RK3588_CLKGATE_CON(37), 5, GFLAGS),
+ GATE(CLK_PMALIVE2, "clk_pmalive2", "xin24m", 0,
+ RK3588_CLKGATE_CON(37), 6, GFLAGS),
+ GATE(ACLK_SATA0, "aclk_sata0", "aclk_mmu_php", 0,
+ RK3588_CLKGATE_CON(37), 7, GFLAGS),
+ GATE(ACLK_SATA1, "aclk_sata1", "aclk_mmu_php", 0,
+ RK3588_CLKGATE_CON(37), 8, GFLAGS),
+ GATE(ACLK_SATA2, "aclk_sata2", "aclk_mmu_php", 0,
+ RK3588_CLKGATE_CON(37), 9, GFLAGS),
+ COMPOSITE(CLK_RXOOB0, "clk_rxoob0", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(82), 7, 1, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(37), 10, GFLAGS),
+ COMPOSITE(CLK_RXOOB1, "clk_rxoob1", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(82), 15, 1, MFLAGS, 8, 7, DFLAGS,
+ RK3588_CLKGATE_CON(37), 11, GFLAGS),
+ COMPOSITE(CLK_RXOOB2, "clk_rxoob2", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(83), 7, 1, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(37), 12, GFLAGS),
+ GATE(ACLK_USB3OTG2, "aclk_usb3otg2", "aclk_mmu_php", 0,
+ RK3588_CLKGATE_CON(35), 7, GFLAGS),
+ GATE(SUSPEND_CLK_USB3OTG2, "suspend_clk_usb3otg2", "xin24m", 0,
+ RK3588_CLKGATE_CON(35), 8, GFLAGS),
+ GATE(REF_CLK_USB3OTG2, "ref_clk_usb3otg2", "xin24m", 0,
+ RK3588_CLKGATE_CON(35), 9, GFLAGS),
+ COMPOSITE(CLK_UTMI_OTG2, "clk_utmi_otg2", mux_150m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(84), 12, 2, MFLAGS, 8, 4, DFLAGS,
+ RK3588_CLKGATE_CON(35), 10, GFLAGS),
+ GATE(PCLK_PCIE_COMBO_PIPE_PHY0, "pclk_pcie_combo_pipe_phy0", "pclk_top_root", 0,
+ RK3588_PHP_CLKGATE_CON(0), 5, GFLAGS),
+ GATE(PCLK_PCIE_COMBO_PIPE_PHY1, "pclk_pcie_combo_pipe_phy1", "pclk_top_root", 0,
+ RK3588_PHP_CLKGATE_CON(0), 6, GFLAGS),
+ GATE(PCLK_PCIE_COMBO_PIPE_PHY2, "pclk_pcie_combo_pipe_phy2", "pclk_top_root", 0,
+ RK3588_PHP_CLKGATE_CON(0), 7, GFLAGS),
+ GATE(PCLK_PCIE_COMBO_PIPE_PHY, "pclk_pcie_combo_pipe_phy", "pclk_top_root", 0,
+ RK3588_PHP_CLKGATE_CON(0), 8, GFLAGS),
+
+ /* rga */
+ COMPOSITE(CLK_RGA3_1_CORE, "clk_rga3_1_core", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(174), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(76), 6, GFLAGS),
+ COMPOSITE(ACLK_RGA3_ROOT, "aclk_rga3_root", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(174), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(76), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_RGA3_ROOT, "hclk_rga3_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(174), 7, 2, MFLAGS,
+ RK3588_CLKGATE_CON(76), 1, GFLAGS),
+ GATE(HCLK_RGA3_1, "hclk_rga3_1", "hclk_rga3_root", 0,
+ RK3588_CLKGATE_CON(76), 4, GFLAGS),
+ GATE(ACLK_RGA3_1, "aclk_rga3_1", "aclk_rga3_root", 0,
+ RK3588_CLKGATE_CON(76), 5, GFLAGS),
+
+ /* vdec */
+ COMPOSITE_NODIV(0, "hclk_rkvdec0_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(89), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(40), 0, GFLAGS),
+ COMPOSITE(0, "aclk_rkvdec0_root", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(89), 7, 2, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(40), 1, GFLAGS),
+ COMPOSITE(ACLK_RKVDEC_CCU, "aclk_rkvdec_ccu", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(89), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(40), 2, GFLAGS),
+ COMPOSITE(CLK_RKVDEC0_CA, "clk_rkvdec0_ca", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(90), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(40), 7, GFLAGS),
+ COMPOSITE(CLK_RKVDEC0_HEVC_CA, "clk_rkvdec0_hevc_ca", gpll_cpll_npll_1000m_p, 0,
+ RK3588_CLKSEL_CON(90), 11, 2, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(40), 8, GFLAGS),
+ COMPOSITE(CLK_RKVDEC0_CORE, "clk_rkvdec0_core", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(91), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(40), 9, GFLAGS),
+ COMPOSITE_NODIV(0, "hclk_rkvdec1_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(93), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(41), 0, GFLAGS),
+ COMPOSITE(0, "aclk_rkvdec1_root", gpll_cpll_aupll_npll_p, 0,
+ RK3588_CLKSEL_CON(93), 7, 2, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(41), 1, GFLAGS),
+ COMPOSITE(CLK_RKVDEC1_CA, "clk_rkvdec1_ca", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(93), 14, 1, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(41), 6, GFLAGS),
+ COMPOSITE(CLK_RKVDEC1_HEVC_CA, "clk_rkvdec1_hevc_ca", gpll_cpll_npll_1000m_p, 0,
+ RK3588_CLKSEL_CON(94), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(41), 7, GFLAGS),
+ COMPOSITE(CLK_RKVDEC1_CORE, "clk_rkvdec1_core", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(94), 12, 1, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(41), 8, GFLAGS),
+
+ /* sdio */
+ COMPOSITE_NODIV(0, "hclk_sdio_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(172), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(75), 0, GFLAGS),
+ COMPOSITE(CCLK_SRC_SDIO, "cclk_src_sdio", gpll_cpll_24m_p, 0,
+ RK3588_CLKSEL_CON(172), 8, 2, MFLAGS, 2, 6, DFLAGS,
+ RK3588_CLKGATE_CON(75), 3, GFLAGS),
+ MMC(SCLK_SDIO_DRV, "sdio_drv", "cclk_src_sdio", RK3588_SDIO_CON0, 1),
+ MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "cclk_src_sdio", RK3588_SDIO_CON1, 1),
+
+ /* usb */
+ COMPOSITE(ACLK_USB_ROOT, "aclk_usb_root", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(96), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(42), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_USB_ROOT, "hclk_usb_root", mux_150m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(96), 6, 2, MFLAGS,
+ RK3588_CLKGATE_CON(42), 1, GFLAGS),
+ GATE(SUSPEND_CLK_USB3OTG0, "suspend_clk_usb3otg0", "xin24m", 0,
+ RK3588_CLKGATE_CON(42), 5, GFLAGS),
+ GATE(REF_CLK_USB3OTG0, "ref_clk_usb3otg0", "xin24m", 0,
+ RK3588_CLKGATE_CON(42), 6, GFLAGS),
+ GATE(SUSPEND_CLK_USB3OTG1, "suspend_clk_usb3otg1", "xin24m", 0,
+ RK3588_CLKGATE_CON(42), 8, GFLAGS),
+ GATE(REF_CLK_USB3OTG1, "ref_clk_usb3otg1", "xin24m", 0,
+ RK3588_CLKGATE_CON(42), 9, GFLAGS),
+
+ /* vdpu */
+ COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(98), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(44), 0, GFLAGS),
+ COMPOSITE_NODIV(ACLK_VDPU_LOW_ROOT, "aclk_vdpu_low_root", mux_400m_200m_100m_24m_p, 0,
+ RK3588_CLKSEL_CON(98), 7, 2, MFLAGS,
+ RK3588_CLKGATE_CON(44), 1, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(98), 9, 2, MFLAGS,
+ RK3588_CLKGATE_CON(44), 2, GFLAGS),
+ COMPOSITE(ACLK_JPEG_DECODER_ROOT, "aclk_jpeg_decoder_root", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(99), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(44), 3, GFLAGS),
+ GATE(HCLK_IEP2P0, "hclk_iep2p0", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 4, GFLAGS),
+ COMPOSITE(CLK_IEP2P0_CORE, "clk_iep2p0_core", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(99), 12, 1, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(45), 6, GFLAGS),
+ GATE(HCLK_JPEG_ENCODER0, "hclk_jpeg_encoder0", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(44), 11, GFLAGS),
+ GATE(HCLK_JPEG_ENCODER1, "hclk_jpeg_encoder1", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(44), 13, GFLAGS),
+ GATE(HCLK_JPEG_ENCODER2, "hclk_jpeg_encoder2", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(44), 15, GFLAGS),
+ GATE(HCLK_JPEG_ENCODER3, "hclk_jpeg_encoder3", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 1, GFLAGS),
+ GATE(HCLK_JPEG_DECODER, "hclk_jpeg_decoder", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 3, GFLAGS),
+ GATE(HCLK_RGA2, "hclk_rga2", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 7, GFLAGS),
+ GATE(ACLK_RGA2, "aclk_rga2", "aclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 8, GFLAGS),
+ COMPOSITE(CLK_RGA2_CORE, "clk_rga2_core", gpll_cpll_npll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(100), 5, 3, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(45), 9, GFLAGS),
+ GATE(HCLK_RGA3_0, "hclk_rga3_0", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 10, GFLAGS),
+ GATE(ACLK_RGA3_0, "aclk_rga3_0", "aclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(45), 11, GFLAGS),
+ COMPOSITE(CLK_RGA3_0_CORE, "clk_rga3_0_core", gpll_cpll_npll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(100), 13, 3, MFLAGS, 8, 5, DFLAGS,
+ RK3588_CLKGATE_CON(45), 12, GFLAGS),
+ GATE(HCLK_VPU, "hclk_vpu", "hclk_vdpu_root", 0,
+ RK3588_CLKGATE_CON(44), 9, GFLAGS),
+
+ /* venc */
+ COMPOSITE_NODIV(HCLK_RKVENC1_ROOT, "hclk_rkvenc1_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(104), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(48), 0, GFLAGS),
+ COMPOSITE(ACLK_RKVENC1_ROOT, "aclk_rkvenc1_root", gpll_cpll_npll_p, 0,
+ RK3588_CLKSEL_CON(104), 7, 2, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(48), 1, GFLAGS),
+ COMPOSITE_NODIV(HCLK_RKVENC0_ROOT, "hclk_rkvenc0_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(102), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(47), 0, GFLAGS),
+ COMPOSITE(ACLK_RKVENC0_ROOT, "aclk_rkvenc0_root", gpll_cpll_npll_p, 0,
+ RK3588_CLKSEL_CON(102), 7, 2, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(47), 1, GFLAGS),
+ GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", 0,
+ RK3588_CLKGATE_CON(47), 4, GFLAGS),
+ GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", 0,
+ RK3588_CLKGATE_CON(47), 5, GFLAGS),
+ COMPOSITE(CLK_RKVENC0_CORE, "clk_rkvenc0_core", gpll_cpll_aupll_npll_p, 0,
+ RK3588_CLKSEL_CON(102), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(47), 6, GFLAGS),
+ COMPOSITE(CLK_RKVENC1_CORE, "clk_rkvenc1_core", gpll_cpll_aupll_npll_p, 0,
+ RK3588_CLKSEL_CON(104), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(48), 6, GFLAGS),
+
+ /* vi */
+ COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(106), 5, 3, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(49), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(106), 8, 2, MFLAGS,
+ RK3588_CLKGATE_CON(49), 1, GFLAGS),
+ COMPOSITE_NODIV(PCLK_VI_ROOT, "pclk_vi_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(106), 10, 2, MFLAGS,
+ RK3588_CLKGATE_CON(49), 2, GFLAGS),
+ COMPOSITE_NODIV(ICLK_CSIHOST01, "iclk_csihost01", mux_400m_200m_100m_24m_p, 0,
+ RK3588_CLKSEL_CON(108), 14, 2, MFLAGS,
+ RK3588_CLKGATE_CON(51), 10, GFLAGS),
+ GATE(ICLK_CSIHOST0, "iclk_csihost0", "iclk_csihost01", 0,
+ RK3588_CLKGATE_CON(51), 11, GFLAGS),
+ GATE(ICLK_CSIHOST1, "iclk_csihost1", "iclk_csihost01", 0,
+ RK3588_CLKGATE_CON(51), 12, GFLAGS),
+ GATE(PCLK_CSI_HOST_0, "pclk_csi_host_0", "pclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 4, GFLAGS),
+ GATE(PCLK_CSI_HOST_1, "pclk_csi_host_1", "pclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 5, GFLAGS),
+ GATE(PCLK_CSI_HOST_2, "pclk_csi_host_2", "pclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 6, GFLAGS),
+ GATE(PCLK_CSI_HOST_3, "pclk_csi_host_3", "pclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 7, GFLAGS),
+ GATE(PCLK_CSI_HOST_4, "pclk_csi_host_4", "pclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 8, GFLAGS),
+ GATE(PCLK_CSI_HOST_5, "pclk_csi_host_5", "pclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 9, GFLAGS),
+ GATE(ACLK_FISHEYE0, "aclk_fisheye0", "aclk_vi_root", 0,
+ RK3588_CLKGATE_CON(49), 14, GFLAGS),
+ GATE(HCLK_FISHEYE0, "hclk_fisheye0", "hclk_vi_root", 0,
+ RK3588_CLKGATE_CON(49), 15, GFLAGS),
+ COMPOSITE(CLK_FISHEYE0_CORE, "clk_fisheye0_core", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(108), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(50), 0, GFLAGS),
+ GATE(ACLK_FISHEYE1, "aclk_fisheye1", "aclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 1, GFLAGS),
+ GATE(HCLK_FISHEYE1, "hclk_fisheye1", "hclk_vi_root", 0,
+ RK3588_CLKGATE_CON(50), 2, GFLAGS),
+ COMPOSITE(CLK_FISHEYE1_CORE, "clk_fisheye1_core", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(108), 12, 2, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(50), 3, GFLAGS),
+ COMPOSITE(CLK_ISP0_CORE, "clk_isp0_core", gpll_cpll_aupll_spll_p, 0,
+ RK3588_CLKSEL_CON(107), 11, 2, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(49), 9, GFLAGS),
+ GATE(CLK_ISP0_CORE_MARVIN, "clk_isp0_core_marvin", "clk_isp0_core", 0,
+ RK3588_CLKGATE_CON(49), 10, GFLAGS),
+ GATE(CLK_ISP0_CORE_VICAP, "clk_isp0_core_vicap", "clk_isp0_core", 0,
+ RK3588_CLKGATE_CON(49), 11, GFLAGS),
+ GATE(ACLK_ISP0, "aclk_isp0", "aclk_vi_root", 0,
+ RK3588_CLKGATE_CON(49), 12, GFLAGS),
+ GATE(HCLK_ISP0, "hclk_isp0", "hclk_vi_root", 0,
+ RK3588_CLKGATE_CON(49), 13, GFLAGS),
+ COMPOSITE(DCLK_VICAP, "dclk_vicap", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(107), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(49), 6, GFLAGS),
+ GATE(ACLK_VICAP, "aclk_vicap", "aclk_vi_root", 0,
+ RK3588_CLKGATE_CON(49), 7, GFLAGS),
+ GATE(HCLK_VICAP, "hclk_vicap", "hclk_vi_root", 0,
+ RK3588_CLKGATE_CON(49), 8, GFLAGS),
+
+ /* vo0 */
+ COMPOSITE(ACLK_VO0_ROOT, "aclk_vo0_root", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(116), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(55), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VO0_ROOT, "hclk_vo0_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(116), 6, 2, MFLAGS,
+ RK3588_CLKGATE_CON(55), 1, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VO0_S_ROOT, "hclk_vo0_s_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(116), 8, 2, MFLAGS,
+ RK3588_CLKGATE_CON(55), 2, GFLAGS),
+ COMPOSITE_NODIV(PCLK_VO0_ROOT, "pclk_vo0_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(116), 10, 2, MFLAGS,
+ RK3588_CLKGATE_CON(55), 3, GFLAGS),
+ COMPOSITE_NODIV(PCLK_VO0_S_ROOT, "pclk_vo0_s_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(116), 12, 2, MFLAGS,
+ RK3588_CLKGATE_CON(55), 4, GFLAGS),
+ GATE(PCLK_DP0, "pclk_dp0", "pclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(56), 4, GFLAGS),
+ GATE(PCLK_DP1, "pclk_dp1", "pclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(56), 5, GFLAGS),
+ GATE(PCLK_S_DP0, "pclk_s_dp0", "pclk_vo0_s_root", 0,
+ RK3588_CLKGATE_CON(56), 6, GFLAGS),
+ GATE(PCLK_S_DP1, "pclk_s_dp1", "pclk_vo0_s_root", 0,
+ RK3588_CLKGATE_CON(56), 7, GFLAGS),
+ GATE(CLK_DP0, "clk_dp0", "aclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(56), 8, GFLAGS),
+ GATE(CLK_DP1, "clk_dp1", "aclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(56), 9, GFLAGS),
+ GATE(HCLK_HDCP_KEY0, "hclk_hdcp_key0", "hclk_vo0_s_root", 0,
+ RK3588_CLKGATE_CON(55), 11, GFLAGS),
+ GATE(PCLK_HDCP0, "pclk_hdcp0", "pclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(55), 14, GFLAGS),
+ GATE(ACLK_TRNG0, "aclk_trng0", "aclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(56), 0, GFLAGS),
+ GATE(PCLK_TRNG0, "pclk_trng0", "pclk_vo0_root", 0,
+ RK3588_CLKGATE_CON(56), 1, GFLAGS),
+ GATE(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(55), 10, GFLAGS),
+ COMPOSITE(CLK_I2S4_8CH_TX_SRC, "clk_i2s4_8ch_tx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(118), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(56), 11, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S4_8CH_TX_FRAC, "clk_i2s4_8ch_tx_frac", "clk_i2s4_8ch_tx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(119), 0,
+ RK3588_CLKGATE_CON(56), 12, GFLAGS,
+ &rk3588_i2s4_8ch_tx_fracmux),
+ GATE(MCLK_I2S4_8CH_TX, "mclk_i2s4_8ch_tx", "clk_i2s4_8ch_tx", 0,
+ RK3588_CLKGATE_CON(56), 13, GFLAGS),
+ COMPOSITE(CLK_I2S8_8CH_TX_SRC, "clk_i2s8_8ch_tx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(120), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(56), 15, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S8_8CH_TX_FRAC, "clk_i2s8_8ch_tx_frac", "clk_i2s8_8ch_tx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(121), 0,
+ RK3588_CLKGATE_CON(57), 0, GFLAGS,
+ &rk3588_i2s8_8ch_tx_fracmux),
+ GATE(MCLK_I2S8_8CH_TX, "mclk_i2s8_8ch_tx", "clk_i2s8_8ch_tx", 0,
+ RK3588_CLKGATE_CON(57), 1, GFLAGS),
+ COMPOSITE(CLK_SPDIF2_DP0_SRC, "clk_spdif2_dp0_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(122), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(57), 3, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_SPDIF2_DP0_FRAC, "clk_spdif2_dp0_frac", "clk_spdif2_dp0_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(123), 0,
+ RK3588_CLKGATE_CON(57), 4, GFLAGS,
+ &rk3588_spdif2_dp0_fracmux),
+ GATE(MCLK_SPDIF2_DP0, "mclk_spdif2_dp0", "clk_spdif2_dp0", 0,
+ RK3588_CLKGATE_CON(57), 5, GFLAGS),
+ GATE(MCLK_SPDIF2, "mclk_spdif2", "clk_spdif2_dp0", 0,
+ RK3588_CLKGATE_CON(57), 6, GFLAGS),
+ COMPOSITE(CLK_SPDIF5_DP1_SRC, "clk_spdif5_dp1_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(124), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(57), 8, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_SPDIF5_DP1_FRAC, "clk_spdif5_dp1_frac", "clk_spdif5_dp1_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(125), 0,
+ RK3588_CLKGATE_CON(57), 9, GFLAGS,
+ &rk3588_spdif5_dp1_fracmux),
+ GATE(MCLK_SPDIF5_DP1, "mclk_spdif5_dp1", "clk_spdif5_dp1", 0,
+ RK3588_CLKGATE_CON(57), 10, GFLAGS),
+ GATE(MCLK_SPDIF5, "mclk_spdif5", "clk_spdif5_dp1", 0,
+ RK3588_CLKGATE_CON(57), 11, GFLAGS),
+ COMPOSITE_NOMUX(CLK_AUX16M_0, "clk_aux16m_0", "gpll", 0,
+ RK3588_CLKSEL_CON(117), 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(56), 2, GFLAGS),
+ COMPOSITE_NOMUX(CLK_AUX16M_1, "clk_aux16m_1", "gpll", 0,
+ RK3588_CLKSEL_CON(117), 8, 8, DFLAGS,
+ RK3588_CLKGATE_CON(56), 3, GFLAGS),
+
+ /* vo1 */
+ COMPOSITE_HALFDIV(CLK_HDMITRX_REFSRC, "clk_hdmitrx_refsrc", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(157), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(65), 9, GFLAGS),
+ COMPOSITE(ACLK_HDCP1_ROOT, "aclk_hdcp1_root", aclk_hdcp1_root_p, 0,
+ RK3588_CLKSEL_CON(128), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(59), 0, GFLAGS),
+ COMPOSITE(ACLK_HDMIRX_ROOT, "aclk_hdmirx_root", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(128), 12, 1, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(59), 1, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VO1_ROOT, "hclk_vo1_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(128), 13, 2, MFLAGS,
+ RK3588_CLKGATE_CON(59), 2, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VO1_S_ROOT, "hclk_vo1_s_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(129), 0, 2, MFLAGS,
+ RK3588_CLKGATE_CON(59), 3, GFLAGS),
+ COMPOSITE_NODIV(PCLK_VO1_ROOT, "pclk_vo1_root", mux_150m_100m_24m_p, 0,
+ RK3588_CLKSEL_CON(129), 2, 2, MFLAGS,
+ RK3588_CLKGATE_CON(59), 4, GFLAGS),
+ COMPOSITE_NODIV(PCLK_VO1_S_ROOT, "pclk_vo1_s_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(129), 4, 2, MFLAGS,
+ RK3588_CLKGATE_CON(59), 5, GFLAGS),
+ COMPOSITE(ACLK_VOP_ROOT, "aclk_vop_root", gpll_cpll_dmyaupll_npll_spll_p, 0,
+ RK3588_CLKSEL_CON(110), 5, 3, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(52), 0, GFLAGS),
+ COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, 0,
+ RK3588_CLKSEL_CON(110), 8, 2, MFLAGS,
+ RK3588_CLKGATE_CON(52), 1, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(110), 10, 2, MFLAGS,
+ RK3588_CLKGATE_CON(52), 2, GFLAGS),
+ COMPOSITE_NODIV(PCLK_VOP_ROOT, "pclk_vop_root", mux_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(110), 12, 2, MFLAGS,
+ RK3588_CLKGATE_CON(52), 3, GFLAGS),
+ COMPOSITE(ACLK_VO1USB_TOP_ROOT, "aclk_vo1usb_top_root", gpll_cpll_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(170), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(74), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_VO1USB_TOP_ROOT, "hclk_vo1usb_top_root", mux_200m_100m_50m_24m_p, CLK_IS_CRITICAL,
+ RK3588_CLKSEL_CON(170), 6, 2, MFLAGS,
+ RK3588_CLKGATE_CON(74), 2, GFLAGS),
+ MUX(ACLK_VOP_SUB_SRC, "aclk_vop_sub_src", aclk_vop_sub_src_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(115), 9, 1, MFLAGS),
+ GATE(PCLK_EDP0, "pclk_edp0", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(62), 0, GFLAGS),
+ GATE(CLK_EDP0_24M, "clk_edp0_24m", "xin24m", 0,
+ RK3588_CLKGATE_CON(62), 1, GFLAGS),
+ COMPOSITE_NODIV(CLK_EDP0_200M, "clk_edp0_200m", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(140), 1, 2, MFLAGS,
+ RK3588_CLKGATE_CON(62), 2, GFLAGS),
+ GATE(PCLK_EDP1, "pclk_edp1", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(62), 3, GFLAGS),
+ GATE(CLK_EDP1_24M, "clk_edp1_24m", "xin24m", 0,
+ RK3588_CLKGATE_CON(62), 4, GFLAGS),
+ COMPOSITE_NODIV(CLK_EDP1_200M, "clk_edp1_200m", mux_200m_100m_50m_24m_p, 0,
+ RK3588_CLKSEL_CON(140), 3, 2, MFLAGS,
+ RK3588_CLKGATE_CON(62), 5, GFLAGS),
+ GATE(HCLK_HDCP_KEY1, "hclk_hdcp_key1", "hclk_vo1_s_root", 0,
+ RK3588_CLKGATE_CON(60), 4, GFLAGS),
+ GATE(PCLK_HDCP1, "pclk_hdcp1", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(60), 7, GFLAGS),
+ GATE(ACLK_HDMIRX, "aclk_hdmirx", "aclk_hdmirx_root", 0,
+ RK3588_CLKGATE_CON(61), 9, GFLAGS),
+ GATE(PCLK_HDMIRX, "pclk_hdmirx", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(61), 10, GFLAGS),
+ GATE(CLK_HDMIRX_REF, "clk_hdmirx_ref", "aclk_hdcp1_root", 0,
+ RK3588_CLKGATE_CON(61), 11, GFLAGS),
+ COMPOSITE(CLK_HDMIRX_AUD_SRC, "clk_hdmirx_aud_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(138), 8, 1, MFLAGS, 0, 8, DFLAGS,
+ RK3588_CLKGATE_CON(61), 12, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_HDMIRX_AUD_FRAC, "clk_hdmirx_aud_frac", "clk_hdmirx_aud_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(139), 0,
+ RK3588_CLKGATE_CON(61), 13, GFLAGS,
+ &rk3588_hdmirx_aud_fracmux),
+ GATE(CLK_HDMIRX_AUD, "clk_hdmirx_aud", "clk_hdmirx_aud_mux", 0,
+ RK3588_CLKGATE_CON(61), 14, GFLAGS),
+ GATE(PCLK_HDMITX0, "pclk_hdmitx0", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(60), 11, GFLAGS),
+ COMPOSITE(CLK_HDMITX0_EARC, "clk_hdmitx0_earc", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(133), 6, 1, MFLAGS, 1, 5, DFLAGS,
+ RK3588_CLKGATE_CON(60), 15, GFLAGS),
+ GATE(CLK_HDMITX0_REF, "clk_hdmitx0_ref", "aclk_hdcp1_root", 0,
+ RK3588_CLKGATE_CON(61), 0, GFLAGS),
+ GATE(PCLK_HDMITX1, "pclk_hdmitx1", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(61), 2, GFLAGS),
+ COMPOSITE(CLK_HDMITX1_EARC, "clk_hdmitx1_earc", gpll_cpll_p, 0,
+ RK3588_CLKSEL_CON(136), 6, 1, MFLAGS, 1, 5, DFLAGS,
+ RK3588_CLKGATE_CON(61), 6, GFLAGS),
+ GATE(CLK_HDMITX1_REF, "clk_hdmitx1_ref", "aclk_hdcp1_root", 0,
+ RK3588_CLKGATE_CON(61), 7, GFLAGS),
+ GATE(ACLK_TRNG1, "aclk_trng1", "aclk_hdcp1_root", 0,
+ RK3588_CLKGATE_CON(60), 9, GFLAGS),
+ GATE(PCLK_TRNG1, "pclk_trng1", "pclk_vo1_root", 0,
+ RK3588_CLKGATE_CON(60), 10, GFLAGS),
+ GATE(0, "pclk_vo1grf", "pclk_vo1_root", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(59), 12, GFLAGS),
+ GATE(PCLK_S_EDP0, "pclk_s_edp0", "pclk_vo1_s_root", 0,
+ RK3588_CLKGATE_CON(59), 14, GFLAGS),
+ GATE(PCLK_S_EDP1, "pclk_s_edp1", "pclk_vo1_s_root", 0,
+ RK3588_CLKGATE_CON(59), 15, GFLAGS),
+ GATE(PCLK_S_HDMIRX, "pclk_s_hdmirx", "pclk_vo1_s_root", 0,
+ RK3588_CLKGATE_CON(65), 8, GFLAGS),
+ COMPOSITE(CLK_I2S10_8CH_RX_SRC, "clk_i2s10_8ch_rx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(155), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(65), 5, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S10_8CH_RX_FRAC, "clk_i2s10_8ch_rx_frac", "clk_i2s10_8ch_rx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(156), 0,
+ RK3588_CLKGATE_CON(65), 6, GFLAGS,
+ &rk3588_i2s10_8ch_rx_fracmux),
+ GATE(MCLK_I2S10_8CH_RX, "mclk_i2s10_8ch_rx", "clk_i2s10_8ch_rx", 0,
+ RK3588_CLKGATE_CON(65), 7, GFLAGS),
+ COMPOSITE(CLK_I2S7_8CH_RX_SRC, "clk_i2s7_8ch_rx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(129), 11, 1, MFLAGS, 6, 5, DFLAGS,
+ RK3588_CLKGATE_CON(60), 1, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S7_8CH_RX_FRAC, "clk_i2s7_8ch_rx_frac", "clk_i2s7_8ch_rx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(130), 0,
+ RK3588_CLKGATE_CON(60), 2, GFLAGS,
+ &rk3588_i2s7_8ch_rx_fracmux),
+ GATE(MCLK_I2S7_8CH_RX, "mclk_i2s7_8ch_rx", "clk_i2s7_8ch_rx", 0,
+ RK3588_CLKGATE_CON(60), 3, GFLAGS),
+ COMPOSITE(CLK_I2S9_8CH_RX_SRC, "clk_i2s9_8ch_rx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(153), 12, 1, MFLAGS, 7, 5, DFLAGS,
+ RK3588_CLKGATE_CON(65), 1, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S9_8CH_RX_FRAC, "clk_i2s9_8ch_rx_frac", "clk_i2s9_8ch_rx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(154), 0,
+ RK3588_CLKGATE_CON(65), 2, GFLAGS,
+ &rk3588_i2s9_8ch_rx_fracmux),
+ GATE(MCLK_I2S9_8CH_RX, "mclk_i2s9_8ch_rx", "clk_i2s9_8ch_rx", 0,
+ RK3588_CLKGATE_CON(65), 3, GFLAGS),
+ COMPOSITE(CLK_I2S5_8CH_TX_SRC, "clk_i2s5_8ch_tx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(140), 10, 1, MFLAGS, 5, 5, DFLAGS,
+ RK3588_CLKGATE_CON(62), 6, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S5_8CH_TX_FRAC, "clk_i2s5_8ch_tx_frac", "clk_i2s5_8ch_tx_src", 0,
+ RK3588_CLKSEL_CON(141), 0,
+ RK3588_CLKGATE_CON(62), 7, GFLAGS,
+ &rk3588_i2s5_8ch_tx_fracmux),
+ GATE(MCLK_I2S5_8CH_TX, "mclk_i2s5_8ch_tx", "clk_i2s5_8ch_tx", 0,
+ RK3588_CLKGATE_CON(62), 8, GFLAGS),
+ COMPOSITE(CLK_I2S6_8CH_TX_SRC, "clk_i2s6_8ch_tx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(144), 8, 1, MFLAGS, 3, 5, DFLAGS,
+ RK3588_CLKGATE_CON(62), 13, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S6_8CH_TX_FRAC, "clk_i2s6_8ch_tx_frac", "clk_i2s6_8ch_tx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(145), 0,
+ RK3588_CLKGATE_CON(62), 14, GFLAGS,
+ &rk3588_i2s6_8ch_tx_fracmux),
+ GATE(MCLK_I2S6_8CH_TX, "mclk_i2s6_8ch_tx", "clk_i2s6_8ch_tx", 0,
+ RK3588_CLKGATE_CON(62), 15, GFLAGS),
+ COMPOSITE(CLK_I2S6_8CH_RX_SRC, "clk_i2s6_8ch_rx_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(146), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(63), 0, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S6_8CH_RX_FRAC, "clk_i2s6_8ch_rx_frac", "clk_i2s6_8ch_rx_src", 0,
+ RK3588_CLKSEL_CON(147), 0,
+ RK3588_CLKGATE_CON(63), 1, GFLAGS,
+ &rk3588_i2s6_8ch_rx_fracmux),
+ GATE(MCLK_I2S6_8CH_RX, "mclk_i2s6_8ch_rx", "clk_i2s6_8ch_rx", 0,
+ RK3588_CLKGATE_CON(63), 2, GFLAGS),
+ MUX(I2S6_8CH_MCLKOUT, "i2s6_8ch_mclkout", i2s6_8ch_mclkout_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(148), 2, 2, MFLAGS),
+ COMPOSITE(CLK_SPDIF3_SRC, "clk_spdif3_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(148), 9, 1, MFLAGS, 4, 5, DFLAGS,
+ RK3588_CLKGATE_CON(63), 5, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_SPDIF3_FRAC, "clk_spdif3_frac", "clk_spdif3_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(149), 0,
+ RK3588_CLKGATE_CON(63), 6, GFLAGS,
+ &rk3588_spdif3_fracmux),
+ GATE(MCLK_SPDIF3, "mclk_spdif3", "clk_spdif3", 0,
+ RK3588_CLKGATE_CON(63), 7, GFLAGS),
+ COMPOSITE(CLK_SPDIF4_SRC, "clk_spdif4_src", gpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(150), 7, 1, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(63), 9, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_SPDIF4_FRAC, "clk_spdif4_frac", "clk_spdif4_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(151), 0,
+ RK3588_CLKGATE_CON(63), 10, GFLAGS,
+ &rk3588_spdif4_fracmux),
+ GATE(MCLK_SPDIF4, "mclk_spdif4", "clk_spdif4", 0,
+ RK3588_CLKGATE_CON(63), 11, GFLAGS),
+ COMPOSITE(MCLK_SPDIFRX0, "mclk_spdifrx0", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(152), 7, 2, MFLAGS, 2, 5, DFLAGS,
+ RK3588_CLKGATE_CON(63), 13, GFLAGS),
+ COMPOSITE(MCLK_SPDIFRX1, "mclk_spdifrx1", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(152), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(63), 15, GFLAGS),
+ COMPOSITE(MCLK_SPDIFRX2, "mclk_spdifrx2", gpll_cpll_aupll_p, 0,
+ RK3588_CLKSEL_CON(153), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(64), 1, GFLAGS),
+ GATE(CLK_HDMIHDP0, "clk_hdmihdp0", "xin24m", 0,
+ RK3588_CLKGATE_CON(73), 12, GFLAGS),
+ GATE(CLK_HDMIHDP1, "clk_hdmihdp1", "xin24m", 0,
+ RK3588_CLKGATE_CON(73), 13, GFLAGS),
+ GATE(PCLK_HDPTX0, "pclk_hdptx0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(72), 5, GFLAGS),
+ GATE(PCLK_HDPTX1, "pclk_hdptx1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(72), 6, GFLAGS),
+ GATE(PCLK_USBDPPHY0, "pclk_usbdpphy0", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(72), 2, GFLAGS),
+ GATE(PCLK_USBDPPHY1, "pclk_usbdpphy1", "pclk_top_root", 0,
+ RK3588_CLKGATE_CON(72), 4, GFLAGS),
+ GATE(HCLK_VOP, "hclk_vop", "hclk_vop_root", 0,
+ RK3588_CLKGATE_CON(52), 8, GFLAGS),
+ GATE(ACLK_VOP, "aclk_vop", "aclk_vop_sub_src", 0,
+ RK3588_CLKGATE_CON(52), 9, GFLAGS),
+ COMPOSITE(DCLK_VOP0_SRC, "dclk_vop0_src", gpll_cpll_v0pll_aupll_p, 0,
+ RK3588_CLKSEL_CON(111), 7, 2, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(52), 10, GFLAGS),
+ COMPOSITE(DCLK_VOP1_SRC, "dclk_vop1_src", gpll_cpll_v0pll_aupll_p, 0,
+ RK3588_CLKSEL_CON(111), 14, 2, MFLAGS, 9, 5, DFLAGS,
+ RK3588_CLKGATE_CON(52), 11, GFLAGS),
+ COMPOSITE(DCLK_VOP2_SRC, "dclk_vop2_src", gpll_cpll_v0pll_aupll_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ RK3588_CLKSEL_CON(112), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_CLKGATE_CON(52), 12, GFLAGS),
+ COMPOSITE_NODIV(DCLK_VOP0, "dclk_vop0", dclk_vop0_p,
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ RK3588_CLKSEL_CON(112), 7, 2, MFLAGS,
+ RK3588_CLKGATE_CON(52), 13, GFLAGS),
+ COMPOSITE_NODIV(DCLK_VOP1, "dclk_vop1", dclk_vop1_p,
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ RK3588_CLKSEL_CON(112), 9, 2, MFLAGS,
+ RK3588_CLKGATE_CON(53), 0, GFLAGS),
+ COMPOSITE_NODIV(DCLK_VOP2, "dclk_vop2", dclk_vop2_p,
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ RK3588_CLKSEL_CON(112), 11, 2, MFLAGS,
+ RK3588_CLKGATE_CON(53), 1, GFLAGS),
+ COMPOSITE(DCLK_VOP3, "dclk_vop3", gpll_cpll_v0pll_aupll_p, 0,
+ RK3588_CLKSEL_CON(113), 7, 2, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(53), 2, GFLAGS),
+ GATE(PCLK_DSIHOST0, "pclk_dsihost0", "pclk_vop_root", 0,
+ RK3588_CLKGATE_CON(53), 4, GFLAGS),
+ GATE(PCLK_DSIHOST1, "pclk_dsihost1", "pclk_vop_root", 0,
+ RK3588_CLKGATE_CON(53), 5, GFLAGS),
+ COMPOSITE(CLK_DSIHOST0, "clk_dsihost0", gpll_cpll_v0pll_spll_p, 0,
+ RK3588_CLKSEL_CON(114), 7, 2, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(53), 6, GFLAGS),
+ COMPOSITE(CLK_DSIHOST1, "clk_dsihost1", gpll_cpll_v0pll_spll_p, 0,
+ RK3588_CLKSEL_CON(115), 7, 2, MFLAGS, 0, 7, DFLAGS,
+ RK3588_CLKGATE_CON(53), 7, GFLAGS),
+ GATE(CLK_VOP_PMU, "clk_vop_pmu", "xin24m", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(53), 8, GFLAGS),
+ GATE(ACLK_VOP_DOBY, "aclk_vop_doby", "aclk_vop_root", 0,
+ RK3588_CLKGATE_CON(53), 10, GFLAGS),
+ GATE(CLK_USBDP_PHY0_IMMORTAL, "clk_usbdp_phy0_immortal", "xin24m", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(2), 8, GFLAGS),
+ GATE(CLK_USBDP_PHY1_IMMORTAL, "clk_usbdp_phy1_immortal", "xin24m", CLK_IGNORE_UNUSED,
+ RK3588_CLKGATE_CON(2), 15, GFLAGS),
+
+ GATE(CLK_REF_PIPE_PHY0_OSC_SRC, "clk_ref_pipe_phy0_osc_src", "xin24m", 0,
+ RK3588_CLKGATE_CON(77), 0, GFLAGS),
+ GATE(CLK_REF_PIPE_PHY1_OSC_SRC, "clk_ref_pipe_phy1_osc_src", "xin24m", 0,
+ RK3588_CLKGATE_CON(77), 1, GFLAGS),
+ GATE(CLK_REF_PIPE_PHY2_OSC_SRC, "clk_ref_pipe_phy2_osc_src", "xin24m", 0,
+ RK3588_CLKGATE_CON(77), 2, GFLAGS),
+ COMPOSITE_NOMUX(CLK_REF_PIPE_PHY0_PLL_SRC, "clk_ref_pipe_phy0_pll_src", "ppll", 0,
+ RK3588_CLKSEL_CON(176), 0, 6, DFLAGS,
+ RK3588_CLKGATE_CON(77), 3, GFLAGS),
+ COMPOSITE_NOMUX(CLK_REF_PIPE_PHY1_PLL_SRC, "clk_ref_pipe_phy1_pll_src", "ppll", 0,
+ RK3588_CLKSEL_CON(176), 6, 6, DFLAGS,
+ RK3588_CLKGATE_CON(77), 4, GFLAGS),
+ COMPOSITE_NOMUX(CLK_REF_PIPE_PHY2_PLL_SRC, "clk_ref_pipe_phy2_pll_src", "ppll", 0,
+ RK3588_CLKSEL_CON(177), 0, 6, DFLAGS,
+ RK3588_CLKGATE_CON(77), 5, GFLAGS),
+ MUX(CLK_REF_PIPE_PHY0, "clk_ref_pipe_phy0", clk_ref_pipe_phy0_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(177), 6, 1, MFLAGS),
+ MUX(CLK_REF_PIPE_PHY1, "clk_ref_pipe_phy1", clk_ref_pipe_phy1_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(177), 7, 1, MFLAGS),
+ MUX(CLK_REF_PIPE_PHY2, "clk_ref_pipe_phy2", clk_ref_pipe_phy2_p, CLK_SET_RATE_PARENT,
+ RK3588_CLKSEL_CON(177), 8, 1, MFLAGS),
+
+ /* pmu */
+ COMPOSITE(CLK_PMU1_300M_SRC, "clk_pmu1_300m_src", pmu_300m_24m_p, 0,
+ RK3588_PMU_CLKSEL_CON(0), 15, 1, MFLAGS, 10, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 3, GFLAGS),
+ COMPOSITE(CLK_PMU1_400M_SRC, "clk_pmu1_400m_src", pmu_400m_24m_p, 0,
+ RK3588_PMU_CLKSEL_CON(1), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 4, GFLAGS),
+ COMPOSITE_NOMUX(CLK_PMU1_50M_SRC, "clk_pmu1_50m_src", "clk_pmu1_400m_src", 0,
+ RK3588_PMU_CLKSEL_CON(0), 0, 4, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 0, GFLAGS),
+ COMPOSITE_NOMUX(CLK_PMU1_100M_SRC, "clk_pmu1_100m_src", "clk_pmu1_400m_src", 0,
+ RK3588_PMU_CLKSEL_CON(0), 4, 3, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 1, GFLAGS),
+ COMPOSITE_NOMUX(CLK_PMU1_200M_SRC, "clk_pmu1_200m_src", "clk_pmu1_400m_src", 0,
+ RK3588_PMU_CLKSEL_CON(0), 7, 3, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 2, GFLAGS),
+ COMPOSITE_NODIV(HCLK_PMU1_ROOT, "hclk_pmu1_root", hclk_pmu1_root_p, CLK_IS_CRITICAL,
+ RK3588_PMU_CLKSEL_CON(1), 6, 2, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 5, GFLAGS),
+ COMPOSITE_NODIV(PCLK_PMU1_ROOT, "pclk_pmu1_root", pmu_100m_50m_24m_src_p, CLK_IS_CRITICAL,
+ RK3588_PMU_CLKSEL_CON(1), 8, 2, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 7, GFLAGS),
+ GATE(PCLK_PMU0_ROOT, "pclk_pmu0_root", "pclk_pmu1_root", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(5), 0, GFLAGS),
+ COMPOSITE_NODIV(HCLK_PMU_CM0_ROOT, "hclk_pmu_cm0_root", hclk_pmu_cm0_root_p, CLK_IS_CRITICAL,
+ RK3588_PMU_CLKSEL_CON(1), 10, 2, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 8, GFLAGS),
+ GATE(CLK_PMU0, "clk_pmu0", "xin24m", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(5), 1, GFLAGS),
+ GATE(PCLK_PMU0, "pclk_pmu0", "pclk_pmu0_root", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(5), 2, GFLAGS),
+ GATE(PCLK_PMU0IOC, "pclk_pmu0ioc", "pclk_pmu0_root", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(5), 4, GFLAGS),
+ GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pmu0_root", 0,
+ RK3588_PMU_CLKGATE_CON(5), 5, GFLAGS),
+ COMPOSITE_NODIV(DBCLK_GPIO0, "dbclk_gpio0", mux_24m_32k_p, 0,
+ RK3588_PMU_CLKSEL_CON(17), 0, 1, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(5), 6, GFLAGS),
+ GATE(PCLK_I2C0, "pclk_i2c0", "pclk_pmu0_root", 0,
+ RK3588_PMU_CLKGATE_CON(2), 1, GFLAGS),
+ COMPOSITE_NODIV(CLK_I2C0, "clk_i2c0", pmu_200m_100m_p, 0,
+ RK3588_PMU_CLKSEL_CON(3), 6, 1, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(2), 2, GFLAGS),
+ GATE(HCLK_I2S1_8CH, "hclk_i2s1_8ch", "hclk_pmu1_root", 0,
+ RK3588_PMU_CLKGATE_CON(2), 7, GFLAGS),
+ COMPOSITE_NOMUX(CLK_I2S1_8CH_TX_SRC, "clk_i2s1_8ch_tx_src", "cpll", 0,
+ RK3588_PMU_CLKSEL_CON(5), 2, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(2), 8, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S1_8CH_TX_FRAC, "clk_i2s1_8ch_tx_frac", "clk_i2s1_8ch_tx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(6), 0,
+ RK3588_PMU_CLKGATE_CON(2), 9, GFLAGS,
+ &rk3588_i2s1_8ch_tx_fracmux),
+ GATE(MCLK_I2S1_8CH_TX, "mclk_i2s1_8ch_tx", "clk_i2s1_8ch_tx", 0,
+ RK3588_PMU_CLKGATE_CON(2), 10, GFLAGS),
+ COMPOSITE_NOMUX(CLK_I2S1_8CH_RX_SRC, "clk_i2s1_8ch_rx_src", "cpll", 0,
+ RK3588_PMU_CLKSEL_CON(7), 2, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(2), 11, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_I2S1_8CH_RX_FRAC, "clk_i2s1_8ch_rx_frac", "clk_i2s1_8ch_rx_src",
+ CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(8), 0,
+ RK3588_PMU_CLKGATE_CON(2), 12, GFLAGS,
+ &rk3588_i2s1_8ch_rx_fracmux),
+ GATE(MCLK_I2S1_8CH_RX, "mclk_i2s1_8ch_rx", "clk_i2s1_8ch_rx", 0,
+ RK3588_PMU_CLKGATE_CON(2), 13, GFLAGS),
+ MUX(I2S1_8CH_MCLKOUT, "i2s1_8ch_mclkout", i2s1_8ch_mclkout_p, CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(9), 2, 2, MFLAGS),
+ GATE(PCLK_PMU1, "pclk_pmu1", "pclk_pmu0_root", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(1), 0, GFLAGS),
+ GATE(CLK_DDR_FAIL_SAFE, "clk_ddr_fail_safe", "clk_pmu0", CLK_IGNORE_UNUSED,
+ RK3588_PMU_CLKGATE_CON(1), 1, GFLAGS),
+ GATE(CLK_PMU1, "clk_pmu1", "clk_pmu0", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(1), 3, GFLAGS),
+ GATE(HCLK_PDM0, "hclk_pdm0", "hclk_pmu1_root", 0,
+ RK3588_PMU_CLKGATE_CON(2), 14, GFLAGS),
+ COMPOSITE_NODIV(MCLK_PDM0, "mclk_pdm0", mclk_pdm0_p, 0,
+ RK3588_PMU_CLKSEL_CON(9), 4, 1, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(2), 15, GFLAGS),
+ GATE(HCLK_VAD, "hclk_vad", "hclk_pmu1_root", 0,
+ RK3588_PMU_CLKGATE_CON(3), 0, GFLAGS),
+ GATE(FCLK_PMU_CM0_CORE, "fclk_pmu_cm0_core", "hclk_pmu_cm0_root", CLK_IS_CRITICAL,
+ RK3588_PMU_CLKGATE_CON(0), 13, GFLAGS),
+ COMPOSITE(CLK_PMU_CM0_RTC, "clk_pmu_cm0_rtc", mux_24m_32k_p, CLK_IS_CRITICAL,
+ RK3588_PMU_CLKSEL_CON(2), 5, 1, MFLAGS, 0, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(0), 15, GFLAGS),
+ GATE(PCLK_PMU1_IOC, "pclk_pmu1_ioc", "pclk_pmu0_root", CLK_IGNORE_UNUSED,
+ RK3588_PMU_CLKGATE_CON(1), 5, GFLAGS),
+ GATE(PCLK_PMU1PWM, "pclk_pmu1pwm", "pclk_pmu0_root", 0,
+ RK3588_PMU_CLKGATE_CON(1), 12, GFLAGS),
+ COMPOSITE_NODIV(CLK_PMU1PWM, "clk_pmu1pwm", pmu_100m_50m_24m_src_p, 0,
+ RK3588_PMU_CLKSEL_CON(2), 9, 2, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(1), 13, GFLAGS),
+ GATE(CLK_PMU1PWM_CAPTURE, "clk_pmu1pwm_capture", "xin24m", 0,
+ RK3588_PMU_CLKGATE_CON(1), 14, GFLAGS),
+ GATE(PCLK_PMU1TIMER, "pclk_pmu1timer", "pclk_pmu0_root", 0,
+ RK3588_PMU_CLKGATE_CON(1), 8, GFLAGS),
+ COMPOSITE_NODIV(CLK_PMU1TIMER_ROOT, "clk_pmu1timer_root", pmu_24m_32k_100m_src_p, 0,
+ RK3588_PMU_CLKSEL_CON(2), 7, 2, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(1), 9, GFLAGS),
+ GATE(CLK_PMU1TIMER0, "clk_pmu1timer0", "clk_pmu1timer_root", 0,
+ RK3588_PMU_CLKGATE_CON(1), 10, GFLAGS),
+ GATE(CLK_PMU1TIMER1, "clk_pmu1timer1", "clk_pmu1timer_root", 0,
+ RK3588_PMU_CLKGATE_CON(1), 11, GFLAGS),
+ COMPOSITE_NOMUX(CLK_UART0_SRC, "clk_uart0_src", "cpll", 0,
+ RK3588_PMU_CLKSEL_CON(3), 7, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(2), 3, GFLAGS),
+ COMPOSITE_FRACMUX(CLK_UART0_FRAC, "clk_uart0_frac", "clk_uart0_src", CLK_SET_RATE_PARENT,
+ RK3588_PMU_CLKSEL_CON(4), 0,
+ RK3588_PMU_CLKGATE_CON(2), 4, GFLAGS,
+ &rk3588_uart0_fracmux),
+ GATE(SCLK_UART0, "sclk_uart0", "clk_uart0", 0,
+ RK3588_PMU_CLKGATE_CON(2), 5, GFLAGS),
+ GATE(PCLK_UART0, "pclk_uart0", "pclk_pmu0_root", 0,
+ RK3588_PMU_CLKGATE_CON(2), 6, GFLAGS),
+ GATE(PCLK_PMU1WDT, "pclk_pmu1wdt", "pclk_pmu0_root", 0,
+ RK3588_PMU_CLKGATE_CON(1), 6, GFLAGS),
+ COMPOSITE_NODIV(TCLK_PMU1WDT, "tclk_pmu1wdt", mux_24m_32k_p, 0,
+ RK3588_PMU_CLKSEL_CON(2), 6, 1, MFLAGS,
+ RK3588_PMU_CLKGATE_CON(1), 7, GFLAGS),
+ COMPOSITE(CLK_CR_PARA, "clk_cr_para", mux_24m_ppll_spll_p, 0,
+ RK3588_PMU_CLKSEL_CON(15), 5, 2, MFLAGS, 0, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(4), 11, GFLAGS),
+ COMPOSITE(CLK_USB2PHY_HDPTXRXPHY_REF, "clk_usb2phy_hdptxrxphy_ref", mux_24m_ppll_p,
+ CLK_IS_CRITICAL,
+ RK3588_PMU_CLKSEL_CON(14), 14, 1, MFLAGS, 9, 5, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(4), 7, GFLAGS),
+ COMPOSITE(CLK_USBDPPHY_MIPIDCPPHY_REF, "clk_usbdpphy_mipidcpphy_ref", mux_24m_ppll_spll_p,
+ CLK_IS_CRITICAL,
+ RK3588_PMU_CLKSEL_CON(14), 7, 2, MFLAGS, 0, 7, DFLAGS,
+ RK3588_PMU_CLKGATE_CON(4), 3, GFLAGS),
+
+ GATE(CLK_PHY0_REF_ALT_P, "clk_phy0_ref_alt_p", "ppll", 0,
+ RK3588_PHYREF_ALT_GATE, 0, GFLAGS),
+ GATE(CLK_PHY0_REF_ALT_M, "clk_phy0_ref_alt_m", "ppll", 0,
+ RK3588_PHYREF_ALT_GATE, 1, GFLAGS),
+ GATE(CLK_PHY1_REF_ALT_P, "clk_phy1_ref_alt_p", "ppll", 0,
+ RK3588_PHYREF_ALT_GATE, 2, GFLAGS),
+ GATE(CLK_PHY1_REF_ALT_M, "clk_phy1_ref_alt_m", "ppll", 0,
+ RK3588_PHYREF_ALT_GATE, 3, GFLAGS),
+
+ GATE(HCLK_SPDIFRX0, "hclk_spdifrx0", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(63), 12, GFLAGS),
+ GATE(HCLK_SPDIFRX1, "hclk_spdifrx1", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(63), 14, GFLAGS),
+ GATE(HCLK_SPDIFRX2, "hclk_spdifrx2", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(64), 0, GFLAGS),
+ GATE(HCLK_SPDIF4, "hclk_spdif4", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(63), 8, GFLAGS),
+ GATE(HCLK_SPDIF3, "hclk_spdif3", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(63), 4, GFLAGS),
+ GATE(HCLK_I2S6_8CH, "hclk_i2s6_8ch", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(63), 3, GFLAGS),
+ GATE(HCLK_I2S5_8CH, "hclk_i2s5_8ch", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(62), 12, GFLAGS),
+ GATE(HCLK_I2S9_8CH, "hclk_i2s9_8ch", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(65), 0, GFLAGS),
+ GATE(HCLK_I2S7_8CH, "hclk_i2s7_8ch", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(60), 0, GFLAGS),
+ GATE(HCLK_I2S10_8CH, "hclk_i2s10_8ch", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(65), 4, GFLAGS),
+ GATE(ACLK_HDCP1, "aclk_hdcp1", "aclk_hdcp1_pre", 0,
+ RK3588_CLKGATE_CON(60), 5, GFLAGS),
+ GATE(HCLK_HDCP1, "hclk_hdcp1", "hclk_vo1", 0,
+ RK3588_CLKGATE_CON(60), 6, GFLAGS),
+ GATE(HCLK_SPDIF5_DP1, "hclk_spdif5_dp1", "hclk_vo0", 0,
+ RK3588_CLKGATE_CON(57), 7, GFLAGS),
+ GATE(HCLK_SPDIF2_DP0, "hclk_spdif2_dp0", "hclk_vo0", 0,
+ RK3588_CLKGATE_CON(57), 2, GFLAGS),
+ GATE(HCLK_I2S8_8CH, "hclk_i2s8_8ch", "hclk_vo0", 0,
+ RK3588_CLKGATE_CON(56), 14, GFLAGS),
+ GATE(HCLK_I2S4_8CH, "hclk_i2s4_8ch", "hclk_vo0", 0,
+ RK3588_CLKGATE_CON(56), 10, GFLAGS),
+ GATE(ACLK_HDCP0, "aclk_hdcp0", "aclk_hdcp0_pre", 0,
+ RK3588_CLKGATE_CON(55), 12, GFLAGS),
+ GATE(HCLK_HDCP0, "hclk_hdcp0", "hclk_vo0", 0,
+ RK3588_CLKGATE_CON(55), 13, GFLAGS),
+ GATE(HCLK_RKVENC1, "hclk_rkvenc1", "hclk_rkvenc1_pre", 0,
+ RK3588_CLKGATE_CON(48), 4, GFLAGS),
+ GATE(ACLK_RKVENC1, "aclk_rkvenc1", "aclk_rkvenc1_pre", 0,
+ RK3588_CLKGATE_CON(48), 5, GFLAGS),
+ GATE(ACLK_VPU, "aclk_vpu", "aclk_vdpu_low_pre", 0,
+ RK3588_CLKGATE_CON(44), 8, GFLAGS),
+ GATE(ACLK_IEP2P0, "aclk_iep2p0", "aclk_vdpu_low_pre", 0,
+ RK3588_CLKGATE_CON(45), 5, GFLAGS),
+ GATE(ACLK_JPEG_ENCODER0, "aclk_jpeg_encoder0", "aclk_vdpu_low_pre", 0,
+ RK3588_CLKGATE_CON(44), 10, GFLAGS),
+ GATE(ACLK_JPEG_ENCODER1, "aclk_jpeg_encoder1", "aclk_vdpu_low_pre", 0,
+ RK3588_CLKGATE_CON(44), 12, GFLAGS),
+ GATE(ACLK_JPEG_ENCODER2, "aclk_jpeg_encoder2", "aclk_vdpu_low_pre", 0,
+ RK3588_CLKGATE_CON(44), 14, GFLAGS),
+ GATE(ACLK_JPEG_ENCODER3, "aclk_jpeg_encoder3", "aclk_vdpu_low_pre", 0,
+ RK3588_CLKGATE_CON(45), 0, GFLAGS),
+ GATE(ACLK_JPEG_DECODER, "aclk_jpeg_decoder", "aclk_jpeg_decoder_pre", 0,
+ RK3588_CLKGATE_CON(45), 2, GFLAGS),
+ GATE(ACLK_USB3OTG1, "aclk_usb3otg1", "aclk_usb", 0,
+ RK3588_CLKGATE_CON(42), 7, GFLAGS),
+ GATE(HCLK_HOST0, "hclk_host0", "hclk_usb", 0,
+ RK3588_CLKGATE_CON(42), 10, GFLAGS),
+ GATE(HCLK_HOST_ARB0, "hclk_host_arb0", "hclk_usb", 0,
+ RK3588_CLKGATE_CON(42), 11, GFLAGS),
+ GATE(HCLK_HOST1, "hclk_host1", "hclk_usb", 0,
+ RK3588_CLKGATE_CON(42), 12, GFLAGS),
+ GATE(HCLK_HOST_ARB1, "hclk_host_arb1", "hclk_usb", 0,
+ RK3588_CLKGATE_CON(42), 13, GFLAGS),
+ GATE(ACLK_USB3OTG0, "aclk_usb3otg0", "aclk_usb", 0,
+ RK3588_CLKGATE_CON(42), 4, GFLAGS),
+ MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "scmi_cclk_sd", RK3588_SDMMC_CON0, 1),
+ MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "scmi_cclk_sd", RK3588_SDMMC_CON1, 1),
+ GATE(HCLK_SDIO, "hclk_sdio", "hclk_sdio_pre", 0,
+ RK3588_CLKGATE_CON(75), 2, GFLAGS),
+ GATE(HCLK_RKVDEC1, "hclk_rkvdec1", "hclk_rkvdec1_pre", 0,
+ RK3588_CLKGATE_CON(41), 2, GFLAGS),
+ GATE(ACLK_RKVDEC1, "aclk_rkvdec1", "aclk_rkvdec1_pre", 0,
+ RK3588_CLKGATE_CON(41), 3, GFLAGS),
+ GATE(HCLK_RKVDEC0, "hclk_rkvdec0", "hclk_rkvdec0_pre", 0,
+ RK3588_CLKGATE_CON(40), 3, GFLAGS),
+ GATE(ACLK_RKVDEC0, "aclk_rkvdec0", "aclk_rkvdec0_pre", 0,
+ RK3588_CLKGATE_CON(40), 4, GFLAGS),
+ GATE(CLK_PCIE4L_PIPE, "clk_pcie4l_pipe", "clk_pipe30phy_pipe0_i", 0,
+ RK3588_CLKGATE_CON(39), 0, GFLAGS),
+ GATE(CLK_PCIE2L_PIPE, "clk_pcie2l_pipe", "clk_pipe30phy_pipe2_i", 0,
+ RK3588_CLKGATE_CON(39), 1, GFLAGS),
+ GATE(CLK_PIPEPHY0_PIPE_G, "clk_pipephy0_pipe_g", "clk_pipephy0_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 3, GFLAGS),
+ GATE(CLK_PIPEPHY1_PIPE_G, "clk_pipephy1_pipe_g", "clk_pipephy1_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 4, GFLAGS),
+ GATE(CLK_PIPEPHY2_PIPE_G, "clk_pipephy2_pipe_g", "clk_pipephy2_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 5, GFLAGS),
+ GATE(CLK_PIPEPHY0_PIPE_ASIC_G, "clk_pipephy0_pipe_asic_g", "clk_pipephy0_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 6, GFLAGS),
+ GATE(CLK_PIPEPHY1_PIPE_ASIC_G, "clk_pipephy1_pipe_asic_g", "clk_pipephy1_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 7, GFLAGS),
+ GATE(CLK_PIPEPHY2_PIPE_ASIC_G, "clk_pipephy2_pipe_asic_g", "clk_pipephy2_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 8, GFLAGS),
+ GATE(CLK_PIPEPHY2_PIPE_U3_G, "clk_pipephy2_pipe_u3_g", "clk_pipephy2_pipe_i", 0,
+ RK3588_CLKGATE_CON(38), 9, GFLAGS),
+ GATE(CLK_PCIE1L2_PIPE, "clk_pcie1l2_pipe", "clk_pipephy0_pipe_g", 0,
+ RK3588_CLKGATE_CON(38), 13, GFLAGS),
+ GATE(CLK_PCIE1L0_PIPE, "clk_pcie1l0_pipe", "clk_pipephy1_pipe_g", 0,
+ RK3588_CLKGATE_CON(38), 14, GFLAGS),
+ GATE(CLK_PCIE1L1_PIPE, "clk_pcie1l1_pipe", "clk_pipephy2_pipe_g", 0,
+ RK3588_CLKGATE_CON(38), 15, GFLAGS),
+ GATE(HCLK_SFC, "hclk_sfc", "hclk_nvm", 0,
+ RK3588_CLKGATE_CON(31), 10, GFLAGS),
+ GATE(HCLK_SFC_XIP, "hclk_sfc_xip", "hclk_nvm", 0,
+ RK3588_CLKGATE_CON(31), 11, GFLAGS),
+ GATE(HCLK_EMMC, "hclk_emmc", "hclk_nvm", 0,
+ RK3588_CLKGATE_CON(31), 4, GFLAGS),
+ GATE(ACLK_ISP1, "aclk_isp1", "aclk_isp1_pre", 0,
+ RK3588_CLKGATE_CON(26), 5, GFLAGS),
+ GATE(HCLK_ISP1, "hclk_isp1", "hclk_isp1_pre", 0,
+ RK3588_CLKGATE_CON(26), 7, GFLAGS),
+ GATE(PCLK_AV1, "pclk_av1", "pclk_av1_pre", 0,
+ RK3588_CLKGATE_CON(68), 5, GFLAGS),
+ GATE(ACLK_AV1, "aclk_av1", "aclk_av1_pre", 0,
+ RK3588_CLKGATE_CON(68), 2, GFLAGS),
+
+ GATE_LINK(ACLK_ISP1_PRE, "aclk_isp1_pre", "aclk_isp1_root", "aclk_vi_root", 0, RK3588_CLKGATE_CON(26), 6, GFLAGS),
+ GATE_LINK(HCLK_ISP1_PRE, "hclk_isp1_pre", "hclk_isp1_root", "hclk_vi_root", 0, RK3588_CLKGATE_CON(26), 8, GFLAGS),
+ GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", "aclk_nvm_root", 0, RK3588_CLKGATE_CON(31), 2, GFLAGS),
+ GATE_LINK(ACLK_USB, "aclk_usb", "aclk_usb_root", "aclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(42), 2, GFLAGS),
+ GATE_LINK(HCLK_USB, "hclk_usb", "hclk_usb_root", "hclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(42), 3, GFLAGS),
+ GATE_LINK(ACLK_JPEG_DECODER_PRE, "aclk_jpeg_decoder_pre", "aclk_jpeg_decoder_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(44), 7, GFLAGS),
+ GATE_LINK(ACLK_VDPU_LOW_PRE, "aclk_vdpu_low_pre", "aclk_vdpu_low_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(44), 5, GFLAGS),
+ GATE_LINK(ACLK_RKVENC1_PRE, "aclk_rkvenc1_pre", "aclk_rkvenc1_root", "aclk_rkvenc0", 0, RK3588_CLKGATE_CON(48), 3, GFLAGS),
+ GATE_LINK(HCLK_RKVENC1_PRE, "hclk_rkvenc1_pre", "hclk_rkvenc1_root", "hclk_rkvenc0", 0, RK3588_CLKGATE_CON(48), 2, GFLAGS),
+ GATE_LINK(HCLK_RKVDEC0_PRE, "hclk_rkvdec0_pre", "hclk_rkvdec0_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(40), 5, GFLAGS),
+ GATE_LINK(ACLK_RKVDEC0_PRE, "aclk_rkvdec0_pre", "aclk_rkvdec0_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(40), 6, GFLAGS),
+ GATE_LINK(HCLK_RKVDEC1_PRE, "hclk_rkvdec1_pre", "hclk_rkvdec1_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(41), 4, GFLAGS),
+ GATE_LINK(ACLK_RKVDEC1_PRE, "aclk_rkvdec1_pre", "aclk_rkvdec1_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(41), 5, GFLAGS),
+ GATE_LINK(ACLK_HDCP0_PRE, "aclk_hdcp0_pre", "aclk_vo0_root", "aclk_vop_low_root", 0, RK3588_CLKGATE_CON(55), 9, GFLAGS),
+ GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", "hclk_vop_root", 0, RK3588_CLKGATE_CON(55), 5, GFLAGS),
+ GATE_LINK(ACLK_HDCP1_PRE, "aclk_hdcp1_pre", "aclk_hdcp1_root", "aclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(59), 6, GFLAGS),
+ GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", "hclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(59), 9, GFLAGS),
+ GATE_LINK(ACLK_AV1_PRE, "aclk_av1_pre", "aclk_av1_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(68), 1, GFLAGS),
+ GATE_LINK(PCLK_AV1_PRE, "pclk_av1_pre", "pclk_av1_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(68), 4, GFLAGS),
+ GATE_LINK(HCLK_SDIO_PRE, "hclk_sdio_pre", "hclk_sdio_root", "hclk_nvm", 0, RK3588_CLKGATE_CON(75), 1, GFLAGS),
+};
+
+static void __init rk3588_clk_init(struct device_node *np)
+{
+ struct rockchip_clk_provider *ctx;
+ unsigned long clk_nr_clks;
+ void __iomem *reg_base;
+
+ clk_nr_clks = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
+ ARRAY_SIZE(rk3588_clk_branches)) + 1;
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: could not map cru region\n", __func__);
+ return;
+ }
+
+ ctx = rockchip_clk_init(np, reg_base, clk_nr_clks);
+ if (IS_ERR(ctx)) {
+ pr_err("%s: rockchip clk init failed\n", __func__);
+ return;
+ }
+
+ rockchip_clk_register_plls(ctx, rk3588_pll_clks,
+ ARRAY_SIZE(rk3588_pll_clks),
+ RK3588_GRF_SOC_STATUS0);
+
+ rockchip_clk_register_armclk(ctx, ARMCLK_L, "armclk_l",
+ mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p),
+ &rk3588_cpulclk_data, rk3588_cpulclk_rates,
+ ARRAY_SIZE(rk3588_cpulclk_rates));
+ rockchip_clk_register_armclk(ctx, ARMCLK_B01, "armclk_b01",
+ mux_armclkb01_p, ARRAY_SIZE(mux_armclkb01_p),
+ &rk3588_cpub0clk_data, rk3588_cpub0clk_rates,
+ ARRAY_SIZE(rk3588_cpub0clk_rates));
+ rockchip_clk_register_armclk(ctx, ARMCLK_B23, "armclk_b23",
+ mux_armclkb23_p, ARRAY_SIZE(mux_armclkb23_p),
+ &rk3588_cpub1clk_data, rk3588_cpub1clk_rates,
+ ARRAY_SIZE(rk3588_cpub1clk_rates));
+
+ rockchip_clk_register_branches(ctx, rk3588_clk_branches,
+ ARRAY_SIZE(rk3588_clk_branches));
+
+ rk3588_rst_init(np, reg_base);
+
+ rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST);
+
+ rockchip_clk_of_add_provider(np, ctx);
+}
+
+struct clk_rk3588_inits {
+ void (*inits)(struct device_node *np);
+};
+
+static const struct clk_rk3588_inits clk_3588_cru_init = {
+ .inits = rk3588_clk_init,
+};
+
+static const struct of_device_id clk_rk3588_match_table[] = {
+ {
+ .compatible = "rockchip,rk3588-cru",
+ .data = &clk_3588_cru_init,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clk_rk3588_match_table);
+
+static int __init clk_rk3588_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ const struct clk_rk3588_inits *init_data;
+
+ init_data = of_device_get_match_data(dev);
+ if (init_data->inits)
+ init_data->inits(np);
+
+ return 0;
+}
+
+static struct driver clk_rk3588_driver = {
+ .probe = clk_rk3588_probe,
+ .name = "clk-rk3588",
+ .of_compatible = DRV_OF_COMPAT(clk_rk3588_match_table),
+};
+
+core_platform_driver(clk_rk3588_driver);
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 6e7bba414f..aedb02a8d3 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -17,7 +17,7 @@
#include <common.h>
#include <malloc.h>
#include <linux/clk.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
#include <linux/spinlock.h>
#include <linux/rational.h>
@@ -59,6 +59,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
mux->width = mux_width;
mux->flags = mux_flags;
mux->lock = lock;
+ mux->hw.clk.name = basprintf("%s.mux", name);
mux->hw.clk.ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
: &clk_mux_ops;
}
@@ -74,6 +75,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
gate->reg = base + gate_offset;
gate->shift = gate_shift;
gate->lock = lock;
+ gate->hw.clk.name = basprintf("%s.gate", name);
gate->hw.clk.ops = &clk_gate_ops;
}
@@ -93,6 +95,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
div->width = div_width;
div->lock = lock;
div->table = div_table;
+ div->hw.clk.name = basprintf("%s.div", name);
div->hw.clk.ops = (div_flags & CLK_DIVIDER_READ_ONLY)
? &clk_divider_ro_ops
: &clk_divider_ops;
@@ -392,6 +395,23 @@ void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
+unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
+ unsigned int nr_clk)
+{
+ unsigned long max = 0;
+ unsigned int idx;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ if (list->id > max)
+ max = list->id;
+ if (list->child && list->child->id > max)
+ max = list->id;
+ }
+
+ return max;
+}
+EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id);
+
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk)
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index a3db88dfc8..a451229326 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -215,11 +215,58 @@
#define RK3568_PMU_CLKGATE_CON(x) ((x) * 0x4 + 0x180)
#define RK3568_PMU_SOFTRST_CON(x) ((x) * 0x4 + 0x200)
+#define RK3588_PHP_CRU_BASE 0x8000
+#define RK3588_PMU_CRU_BASE 0x30000
+#define RK3588_BIGCORE0_CRU_BASE 0x50000
+#define RK3588_BIGCORE1_CRU_BASE 0x52000
+#define RK3588_DSU_CRU_BASE 0x58000
+
+#define RK3588_PLL_CON(x) RK2928_PLL_CON(x)
+#define RK3588_MODE_CON0 0x280
+#define RK3588_B0_PLL_MODE_CON0 (RK3588_BIGCORE0_CRU_BASE + 0x280)
+#define RK3588_B1_PLL_MODE_CON0 (RK3588_BIGCORE1_CRU_BASE + 0x280)
+#define RK3588_LPLL_MODE_CON0 (RK3588_DSU_CRU_BASE + 0x280)
+#define RK3588_CLKSEL_CON(x) ((x) * 0x4 + 0x300)
+#define RK3588_CLKGATE_CON(x) ((x) * 0x4 + 0x800)
+#define RK3588_SOFTRST_CON(x) ((x) * 0x4 + 0xa00)
+#define RK3588_GLB_CNT_TH 0xc00
+#define RK3588_GLB_SRST_FST 0xc08
+#define RK3588_GLB_SRST_SND 0xc0c
+#define RK3588_GLB_RST_CON 0xc10
+#define RK3588_GLB_RST_ST 0xc04
+#define RK3588_SDIO_CON0 0xC24
+#define RK3588_SDIO_CON1 0xC28
+#define RK3588_SDMMC_CON0 0xC30
+#define RK3588_SDMMC_CON1 0xC34
+
+#define RK3588_PHP_CLKGATE_CON(x) ((x) * 0x4 + RK3588_PHP_CRU_BASE + 0x800)
+#define RK3588_PHP_SOFTRST_CON(x) ((x) * 0x4 + RK3588_PHP_CRU_BASE + 0xa00)
+
+#define RK3588_PMU_PLL_CON(x) ((x) * 0x4 + RK3588_PHP_CRU_BASE)
+#define RK3588_PMU_CLKSEL_CON(x) ((x) * 0x4 + RK3588_PMU_CRU_BASE + 0x300)
+#define RK3588_PMU_CLKGATE_CON(x) ((x) * 0x4 + RK3588_PMU_CRU_BASE + 0x800)
+#define RK3588_PMU_SOFTRST_CON(x) ((x) * 0x4 + RK3588_PMU_CRU_BASE + 0xa00)
+
+#define RK3588_B0_PLL_CON(x) ((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE)
+#define RK3588_BIGCORE0_CLKSEL_CON(x) ((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE + 0x300)
+#define RK3588_BIGCORE0_CLKGATE_CON(x) ((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE + 0x800)
+#define RK3588_BIGCORE0_SOFTRST_CON(x) ((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE + 0xa00)
+#define RK3588_B1_PLL_CON(x) ((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE)
+#define RK3588_BIGCORE1_CLKSEL_CON(x) ((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE + 0x300)
+#define RK3588_BIGCORE1_CLKGATE_CON(x) ((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE + 0x800)
+#define RK3588_BIGCORE1_SOFTRST_CON(x) ((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE + 0xa00)
+#define RK3588_LPLL_CON(x) ((x) * 0x4 + RK3588_DSU_CRU_BASE)
+#define RK3588_DSU_CLKSEL_CON(x) ((x) * 0x4 + RK3588_DSU_CRU_BASE + 0x300)
+#define RK3588_DSU_CLKGATE_CON(x) ((x) * 0x4 + RK3588_DSU_CRU_BASE + 0x800)
+#define RK3588_DSU_SOFTRST_CON(x) ((x) * 0x4 + RK3588_DSU_CRU_BASE + 0xa00)
+
enum rockchip_pll_type {
pll_rk3036,
pll_rk3066,
pll_rk3328,
pll_rk3399,
+ pll_rk3588,
+ pll_rk3588_core,
};
#define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \
@@ -252,6 +299,15 @@ enum rockchip_pll_type {
.nb = _nb, \
}
+#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k) \
+{ \
+ .rate = _rate##U, \
+ .p = _p, \
+ .m = _m, \
+ .s = _s, \
+ .k = _k, \
+}
+
/**
* struct rockchip_clk_provider - information about clock provider
* @reg_base: virtual address for the register base.
@@ -272,17 +328,31 @@ struct rockchip_clk_provider {
struct rockchip_pll_rate_table {
unsigned long rate;
- unsigned int nr;
- unsigned int nf;
- unsigned int no;
- unsigned int nb;
- /* for RK3036/RK3399 */
- unsigned int fbdiv;
- unsigned int postdiv1;
- unsigned int refdiv;
- unsigned int postdiv2;
- unsigned int dsmpd;
- unsigned int frac;
+ union {
+ struct {
+ /* for RK3066 */
+ unsigned int nr;
+ unsigned int nf;
+ unsigned int no;
+ unsigned int nb;
+ };
+ struct {
+ /* for RK3036/RK3399 */
+ unsigned int fbdiv;
+ unsigned int postdiv1;
+ unsigned int refdiv;
+ unsigned int postdiv2;
+ unsigned int dsmpd;
+ unsigned int frac;
+ };
+ struct {
+ /* for RK3588 */
+ unsigned int m;
+ unsigned int p;
+ unsigned int s;
+ unsigned int k;
+ };
+ };
};
/**
@@ -351,11 +421,13 @@ struct rockchip_cpuclk_clksel {
u32 val;
};
-#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 5
+#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 6
#define ROCKCHIP_CPUCLK_MAX_CORES 4
struct rockchip_cpuclk_rate_table {
unsigned long prate;
struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
+ struct rockchip_cpuclk_clksel pre_muxs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
+ struct rockchip_cpuclk_clksel post_muxs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
};
/**
@@ -364,6 +436,8 @@ struct rockchip_cpuclk_rate_table {
* @div_core_shift[]: cores divider offset used to divide the pll value
* @div_core_mask[]: cores divider mask
* @num_cores: number of cpu cores
+ * @mux_core_reg: register offset of the cores select parent
+ * @mux_core_alt: mux value to select alternate parent
* @mux_core_main: mux value to select main parent of core
* @mux_core_shift: offset of the core multiplexer
* @mux_core_mask: core multiplexer mask
@@ -373,6 +447,7 @@ struct rockchip_cpuclk_reg_data {
u8 div_core_shift[ROCKCHIP_CPUCLK_MAX_CORES];
u32 div_core_mask[ROCKCHIP_CPUCLK_MAX_CORES];
int num_cores;
+ int mux_core_reg;
u8 mux_core_alt;
u8 mux_core_main;
u8 mux_core_shift;
@@ -782,6 +857,27 @@ struct rockchip_clk_branch {
.gate_flags = gf, \
}
+#define COMPOSITE_HALFDIV(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\
+ df, go, gs, gf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_half_divider, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .mux_shift = ms, \
+ .mux_width = mw, \
+ .mux_flags = mf, \
+ .div_shift = ds, \
+ .div_width = dw, \
+ .div_flags = df, \
+ .gate_offset = go, \
+ .gate_shift = gs, \
+ .gate_flags = gf, \
+ }
+
/* SGRF clocks are only accessible from secure mode, so not controllable */
#define SGRF_GATE(_id, cname, pname) \
FACTOR(_id, cname, pname, 0, 1, 1)
@@ -792,6 +888,8 @@ void rockchip_clk_of_add_provider(struct device_node *np,
struct rockchip_clk_provider *ctx);
void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
struct clk *clk, unsigned int id);
+unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
+ unsigned int nr_clk);
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk);
@@ -808,18 +906,29 @@ void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
unsigned int reg);
+#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
+
#ifdef CONFIG_RESET_CONTROLLER
-void rockchip_register_softrst(struct device_node *np,
- unsigned int num_regs,
- void __iomem *base, u8 flags);
+void rockchip_register_softrst_lut(struct device_node *np,
+ const int *lookup_table,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags);
#else
-static inline void rockchip_register_softrst(struct device_node *np,
- unsigned int num_regs,
- void __iomem *base, u8 flags)
+static inline void rockchip_register_softrst_lut(struct device_node *np,
+ const int *lookup_table,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags)
{
}
#endif
-#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
+static inline void rockchip_register_softrst(struct device_node *np,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags)
+{
+ return rockchip_register_softrst_lut(np, NULL, num_regs, base, flags);
+}
+
+void rk3588_rst_init(struct device_node *np, void __iomem *reg_base);
#endif
diff --git a/drivers/clk/rockchip/rst-rk3588.c b/drivers/clk/rockchip/rst-rk3588.c
new file mode 100644
index 0000000000..7501b92b45
--- /dev/null
+++ b/drivers/clk/rockchip/rst-rk3588.c
@@ -0,0 +1,855 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: Sebastian Reichel <sebastian.reichel@collabora.com>
+ */
+
+#include <dt-bindings/reset/rockchip,rk3588-cru.h>
+#include "clk.h"
+
+/* 0xFD7C0000 + 0x0A00 */
+#define RK3588_CRU_RESET_OFFSET(id, reg, bit) [id] = (0 + reg * 16 + bit)
+
+/* 0xFD7C8000 + 0x0A00 */
+#define RK3588_PHPTOPCRU_RESET_OFFSET(id, reg, bit) [id] = (0x8000*4 + reg * 16 + bit)
+
+/* 0xFD7D0000 + 0x0A00 */
+#define RK3588_SECURECRU_RESET_OFFSET(id, reg, bit) [id] = (0x10000*4 + reg * 16 + bit)
+
+/* 0xFD7F0000 + 0x0A00 */
+#define RK3588_PMU1CRU_RESET_OFFSET(id, reg, bit) [id] = (0x30000*4 + reg * 16 + bit)
+
+/* mapping table for reset ID to register offset */
+static const int rk3588_register_offset[] = {
+ /* SOFTRST_CON01 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_TOP_BIU, 1, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_TOP_BIU, 1, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSIPHY0, 1, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIPHY0, 1, 7), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSIPHY1, 1, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIPHY1, 1, 9), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_A_TOP_M500_BIU, 1, 15),
+
+ /* SOFTRST_CON02 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_TOP_M400_BIU, 2, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_TOP_S200_BIU, 2, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_A_TOP_S400_BIU, 2, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_TOP_M300_BIU, 2, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_INIT, 2, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_CMN, 2, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_LANE, 2, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_PCS, 2, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_INIT, 2, 15),
+
+ /* SOFTRST_CON03 */
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_CMN, 3, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_LANE, 3, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_PCS, 3, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_DCPHY0, 3, 11), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_P_MIPI_DCPHY0, 3, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_MIPI_DCPHY0_GRF, 3, 15),
+
+ /* SOFTRST_CON04 */
+ RK3588_CRU_RESET_OFFSET(SRST_DCPHY1, 4, 0), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_P_MIPI_DCPHY1, 4, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_MIPI_DCPHY1_GRF, 4, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_CDPHY, 4, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_CSIPHY, 4, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_VCCIO3_5, 4, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_VCCIO6, 4, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_EMMCIO, 4, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_IOC_TOP, 4, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_IOC_RIGHT, 4, 11),
+
+ /* SOFTRST_CON05 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_CRU, 5, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_CHANNEL_SECURE2VO1USB, 5, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_CHANNEL_SECURE2CENTER, 5, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_H_CHANNEL_SECURE2VO1USB, 5, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_H_CHANNEL_SECURE2CENTER, 5, 15),
+
+ /* SOFTRST_CON06 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_CHANNEL_SECURE2VO1USB, 6, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CHANNEL_SECURE2CENTER, 6, 1),
+
+ /* SOFTRST_CON07 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_AUDIO_BIU, 7, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_AUDIO_BIU, 7, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S0_8CH, 7, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S0_8CH_TX, 7, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S0_8CH_RX, 7, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_ACDCDIG, 7, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S2_2CH, 7, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S3_2CH, 7, 13),
+
+ /* SOFTRST_CON08 */
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S2_2CH, 8, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S3_2CH, 8, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_DAC_ACDCDIG, 8, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIF0, 8, 14),
+
+ /* SOFTRST_CON09 */
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIF0, 9, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIF1, 9, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIF1, 9, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_H_PDM1, 9, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_PDM1, 9, 7),
+
+ /* SOFTRST_CON10 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_BUS_BIU, 10, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_BUS_BIU, 10, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_GIC, 10, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_A_GIC_DBG, 10, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DMAC0, 10, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DMAC1, 10, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DMAC2, 10, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C1, 10, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C2, 10, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C3, 10, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C4, 10, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C5, 10, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C6, 10, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C7, 10, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_I2C8, 10, 15),
+
+ /* SOFTRST_CON11 */
+ RK3588_CRU_RESET_OFFSET(SRST_I2C1, 11, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C2, 11, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C3, 11, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C4, 11, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C5, 11, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C6, 11, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C7, 11, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_I2C8, 11, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CAN0, 11, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_CAN0, 11, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CAN1, 11, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_CAN1, 11, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CAN2, 11, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_CAN2, 11, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SARADC, 11, 14),
+
+ /* SOFTRST_CON12 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_TSADC, 12, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_TSADC, 12, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART1, 12, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART2, 12, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART3, 12, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART4, 12, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART5, 12, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART6, 12, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART7, 12, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART8, 12, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_UART9, 12, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART1, 12, 13),
+
+ /* SOFTRST_CON13 */
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART2, 13, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART3, 13, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART4, 13, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART5, 13, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART6, 13, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART7, 13, 15),
+
+ /* SOFTRST_CON14 */
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART8, 14, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_S_UART9, 14, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SPI0, 14, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SPI1, 14, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SPI2, 14, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SPI3, 14, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SPI4, 14, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_SPI0, 14, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_SPI1, 14, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_SPI2, 14, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_SPI3, 14, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_SPI4, 14, 15),
+
+ /* SOFTRST_CON15 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_WDT0, 15, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_T_WDT0, 15, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SYS_GRF, 15, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PWM1, 15, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_PWM1, 15, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PWM2, 15, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_PWM2, 15, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PWM3, 15, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_PWM3, 15, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_BUSTIMER0, 15, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_BUSTIMER1, 15, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER0, 15, 15),
+
+ /* SOFTRST_CON16 */
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER1, 16, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER2, 16, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER3, 16, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER4, 16, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER5, 16, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER6, 16, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER7, 16, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER8, 16, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER9, 16, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER10, 16, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_BUSTIMER11, 16, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_MAILBOX0, 16, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_MAILBOX1, 16, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_MAILBOX2, 16, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPIO1, 16, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_GPIO1, 16, 15),
+
+ /* SOFTRST_CON17 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPIO2, 17, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_GPIO2, 17, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPIO3, 17, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_GPIO3, 17, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPIO4, 17, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_GPIO4, 17, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DECOM, 17, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DECOM, 17, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_D_DECOM, 17, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_TOP, 17, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_A_GICADB_GIC2CORE_BUS, 17, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DFT2APB, 17, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_TOP, 17, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_CDPHY, 17, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_BOT_RIGHT, 17, 15),
+
+ /* SOFTRST_CON18 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_IOC_TOP, 18, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_IOC_RIGHT, 18, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_CSIPHY, 18, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_VCCIO3_5, 18, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_VCCIO6, 18, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_MST_EMMCIO, 18, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_SPINLOCK, 18, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_OTPC_NS, 18, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_OTPC_NS, 18, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_OTPC_ARB, 18, 11),
+
+ /* SOFTRST_CON19 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_BUSIOC, 19, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PMUCM0_INTMUX, 19, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDRCM0_INTMUX, 19, 5),
+
+ /* SOFTRST_CON20 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_DFICTL_CH0, 20, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH0, 20, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_STANDBY_CH0, 20, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH0, 20, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH0, 20, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_GRF_CH01, 20, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_DFI_CH0, 20, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_SBR_CH0, 20, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH0, 20, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_DFICTL_CH0, 20, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_MON_CH0, 20, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_STANDBY_CH0, 20, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_CH0, 20, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_DFICTL_CH1, 20, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH1, 20, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_STANDBY_CH1, 20, 15),
+
+ /* SOFTRST_CON21 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH1, 21, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH1, 21, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_DFI_CH1, 21, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_SBR_CH1, 21, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH1, 21, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_DFICTL_CH1, 21, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_MON_CH1, 21, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_STANDBY_CH1, 21, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_CH1, 21, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_MSCH0, 21, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_RS_MSCH0, 21, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_FRS_MSCH0, 21, 15),
+
+ /* SOFTRST_CON22 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_SCRAMBLE0, 22, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_FRS_SCRAMBLE0, 22, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_MSCH1, 22, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_RS_MSCH1, 22, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_FRS_MSCH1, 22, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_SCRAMBLE1, 22, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR01_FRS_SCRAMBLE1, 22, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR01_MSCH0, 22, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR01_MSCH1, 22, 8),
+
+ /* SOFTRST_CON23 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_DFICTL_CH2, 23, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH2, 23, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_STANDBY_CH2, 23, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH2, 23, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH2, 23, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_GRF_CH23, 23, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_DFI_CH2, 23, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_SBR_CH2, 23, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH2, 23, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_DFICTL_CH2, 23, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_MON_CH2, 23, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_STANDBY_CH2, 23, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_CH2, 23, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_DFICTL_CH3, 23, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH3, 23, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_STANDBY_CH3, 23, 15),
+
+ /* SOFTRST_CON24 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH3, 24, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH3, 24, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_DFI_CH3, 24, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_SBR_CH3, 24, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH3, 24, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_DFICTL_CH3, 24, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_MON_CH3, 24, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_STANDBY_CH3, 24, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_CH3, 24, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_MSCH2, 24, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_RS_MSCH2, 24, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_FRS_MSCH2, 24, 15),
+
+ /* SOFTRST_CON25 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_SCRAMBLE2, 25, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_FRS_SCRAMBLE2, 25, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_MSCH3, 25, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_RS_MSCH3, 25, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_FRS_MSCH3, 25, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_SCRAMBLE3, 25, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR23_FRS_SCRAMBLE3, 25, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR23_MSCH2, 25, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DDR23_MSCH3, 25, 8),
+
+ /* SOFTRST_CON26 */
+ RK3588_CRU_RESET_OFFSET(SRST_ISP1, 26, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_ISP1_VICAP, 26, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_ISP1_BIU, 26, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_H_ISP1_BIU, 26, 8),
+
+ /* SOFTRST_CON27 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN1, 27, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN1_BIU, 27, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKNN1, 27, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKNN1_BIU, 27, 3),
+
+ /* SOFTRST_CON28 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN2, 28, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN2_BIU, 28, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKNN2, 28, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKNN2_BIU, 28, 3),
+
+ /* SOFTRST_CON29 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN_DSU0, 29, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_NPUTOP_BIU, 29, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_NPU_TIMER, 29, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_NPUTIMER0, 29, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_NPUTIMER1, 29, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_NPU_WDT, 29, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_T_NPU_WDT, 29, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_NPU_PVTM, 29, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_NPU_GRF, 29, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_NPU_PVTM, 29, 14),
+
+ /* SOFTRST_CON30 */
+ RK3588_CRU_RESET_OFFSET(SRST_NPU_PVTPLL, 30, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_H_NPU_CM0_BIU, 30, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_F_NPU_CM0_CORE, 30, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_T_NPU_CM0_JTAG, 30, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN0, 30, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKNN0_BIU, 30, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKNN0, 30, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKNN0_BIU, 30, 9),
+
+ /* SOFTRST_CON31 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_NVM_BIU, 31, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_NVM_BIU, 31, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_EMMC, 31, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_EMMC, 31, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_C_EMMC, 31, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_B_EMMC, 31, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_T_EMMC, 31, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_S_SFC, 31, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SFC, 31, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SFC_XIP, 31, 11),
+
+ /* SOFTRST_CON32 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_GRF, 32, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DEC_BIU, 32, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PHP_BIU, 32, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_PCIE_GRIDGE, 32, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_PHP_BIU, 32, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_A_GMAC0, 32, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_A_GMAC1, 32, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_A_PCIE_BIU, 32, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_PCIE0_POWER_UP, 32, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_PCIE1_POWER_UP, 32, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_PCIE2_POWER_UP, 32, 15),
+
+ /* SOFTRST_CON33 */
+ RK3588_CRU_RESET_OFFSET(SRST_PCIE3_POWER_UP, 33, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_PCIE4_POWER_UP, 33, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PCIE0, 33, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PCIE1, 33, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PCIE2, 33, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_PCIE3, 33, 15),
+
+ /* SOFTRST_CON34 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_PCIE4, 34, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_A_PHP_GIC_ITS, 34, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_MMU_PCIE, 34, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_MMU_PHP, 34, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_MMU_BIU, 34, 9),
+
+ /* SOFTRST_CON35 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_USB3OTG2, 35, 7),
+
+ /* SOFTRST_CON37 */
+ RK3588_CRU_RESET_OFFSET(SRST_PMALIVE0, 37, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_PMALIVE1, 37, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_PMALIVE2, 37, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_SATA0, 37, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_SATA1, 37, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_SATA2, 37, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_RXOOB0, 37, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_RXOOB1, 37, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_RXOOB2, 37, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_ASIC0, 37, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_ASIC1, 37, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_ASIC2, 37, 15),
+
+ /* SOFTRST_CON40 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVDEC_CCU, 40, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVDEC0, 40, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVDEC0, 40, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVDEC0_BIU, 40, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVDEC0_BIU, 40, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVDEC0_CA, 40, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVDEC0_HEVC_CA, 40, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVDEC0_CORE, 40, 9),
+
+ /* SOFTRST_CON41 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVDEC1, 41, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVDEC1, 41, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVDEC1_BIU, 41, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVDEC1_BIU, 41, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVDEC1_CA, 41, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVDEC1_HEVC_CA, 41, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVDEC1_CORE, 41, 8),
+
+ /* SOFTRST_CON42 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_USB_BIU, 42, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_H_USB_BIU, 42, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_A_USB3OTG0, 42, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_USB3OTG1, 42, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HOST0, 42, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HOST_ARB0, 42, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HOST1, 42, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HOST_ARB1, 42, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_A_USB_GRF, 42, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_C_USB2P0_HOST0, 42, 15),
+
+ /* SOFTRST_CON43 */
+ RK3588_CRU_RESET_OFFSET(SRST_C_USB2P0_HOST1, 43, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_HOST_UTMI0, 43, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_HOST_UTMI1, 43, 2),
+
+ /* SOFTRST_CON44 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_VDPU_BIU, 44, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_VDPU_LOW_BIU, 44, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VDPU_BIU, 44, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_JPEG_DECODER_BIU, 44, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_VPU, 44, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VPU, 44, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_A_JPEG_ENCODER0, 44, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_H_JPEG_ENCODER0, 44, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_A_JPEG_ENCODER1, 44, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_H_JPEG_ENCODER1, 44, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_A_JPEG_ENCODER2, 44, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_H_JPEG_ENCODER2, 44, 15),
+
+ /* SOFTRST_CON45 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_JPEG_ENCODER3, 45, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_H_JPEG_ENCODER3, 45, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_A_JPEG_DECODER, 45, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_H_JPEG_DECODER, 45, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_IEP2P0, 45, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_IEP2P0, 45, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_IEP2P0_CORE, 45, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RGA2, 45, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RGA2, 45, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_RGA2_CORE, 45, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RGA3_0, 45, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RGA3_0, 45, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_RGA3_0_CORE, 45, 12),
+
+ /* SOFTRST_CON47 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVENC0_BIU, 47, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVENC0_BIU, 47, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVENC0, 47, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVENC0, 47, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVENC0_CORE, 47, 6),
+
+ /* SOFTRST_CON48 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVENC1_BIU, 48, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVENC1_BIU, 48, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RKVENC1, 48, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RKVENC1, 48, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_RKVENC1_CORE, 48, 6),
+
+ /* SOFTRST_CON49 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_VI_BIU, 49, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VI_BIU, 49, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VI_BIU, 49, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_D_VICAP, 49, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_VICAP, 49, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VICAP, 49, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_ISP0, 49, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_ISP0_VICAP, 49, 11),
+
+ /* SOFTRST_CON50 */
+ RK3588_CRU_RESET_OFFSET(SRST_FISHEYE0, 50, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_FISHEYE1, 50, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSI_HOST_0, 50, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSI_HOST_1, 50, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSI_HOST_2, 50, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSI_HOST_3, 50, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSI_HOST_4, 50, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CSI_HOST_5, 50, 9),
+
+ /* SOFTRST_CON51 */
+ RK3588_CRU_RESET_OFFSET(SRST_CSIHOST0_VICAP, 51, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIHOST1_VICAP, 51, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIHOST2_VICAP, 51, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIHOST3_VICAP, 51, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIHOST4_VICAP, 51, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_CSIHOST5_VICAP, 51, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_CIFIN, 51, 13),
+
+ /* SOFTRST_CON52 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_VOP_BIU, 52, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_VOP_LOW_BIU, 52, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VOP_BIU, 52, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VOP_BIU, 52, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VOP, 52, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_VOP, 52, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_D_VOP0, 52, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_D_VOP2HDMI_BRIDGE0, 52, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_D_VOP2HDMI_BRIDGE1, 52, 15),
+
+ /* SOFTRST_CON53 */
+ RK3588_CRU_RESET_OFFSET(SRST_D_VOP1, 53, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_D_VOP2, 53, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_D_VOP3, 53, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VOPGRF, 53, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DSIHOST0, 53, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DSIHOST1, 53, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_DSIHOST0, 53, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_DSIHOST1, 53, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_VOP_PMU, 53, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VOP_CHANNEL_BIU, 53, 9),
+
+ /* SOFTRST_CON55 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_VO0_BIU, 55, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VO0_S_BIU, 55, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VO0_BIU, 55, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VO0_S_BIU, 55, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_HDCP0_BIU, 55, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VO0GRF, 55, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HDCP_KEY0, 55, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_A_HDCP0, 55, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HDCP0, 55, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_HDCP0, 55, 15),
+
+ /* SOFTRST_CON56 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_TRNG0, 56, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_DP0, 56, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_DP1, 56, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S4_8CH, 56, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S4_8CH_TX, 56, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S8_8CH, 56, 14),
+
+ /* SOFTRST_CON57 */
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S8_8CH_TX, 57, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIF2_DP0, 57, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIF2_DP0, 57, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIF5_DP1, 57, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIF5_DP1, 57, 11),
+
+ /* SOFTRST_CON59 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_HDCP1_BIU, 59, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_VO1_BIU, 59, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VOP1_BIU, 59, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VOP1_S_BIU, 59, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VOP1_BIU, 59, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VO1GRF, 59, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_VO1_S_BIU, 59, 13),
+
+ /* SOFTRST_CON60 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S7_8CH, 60, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S7_8CH_RX, 60, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HDCP_KEY1, 60, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_HDCP1, 60, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_H_HDCP1, 60, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_HDCP1, 60, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_TRNG1, 60, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_HDMITX0, 60, 11),
+
+ /* SOFTRST_CON61 */
+ RK3588_CRU_RESET_OFFSET(SRST_HDMITX0_REF, 61, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_HDMITX1, 61, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_HDMITX1_REF, 61, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_HDMIRX, 61, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_HDMIRX, 61, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_HDMIRX_REF, 61, 11),
+
+ /* SOFTRST_CON62 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_EDP0, 62, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_EDP0_24M, 62, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_EDP1, 62, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_EDP1_24M, 62, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S5_8CH_TX, 62, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S5_8CH, 62, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S6_8CH_TX, 62, 15),
+
+ /* SOFTRST_CON63 */
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S6_8CH_RX, 63, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S6_8CH, 63, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIF3, 63, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIF3, 63, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIF4, 63, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIF4, 63, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIFRX0, 63, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIFRX0, 63, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIFRX1, 63, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIFRX1, 63, 15),
+
+ /* SOFTRST_CON64 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_SPDIFRX2, 64, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_M_SPDIFRX2, 64, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_LINKSYM_HDMITXPHY0, 64, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_LINKSYM_HDMITXPHY1, 64, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_VO1_BRIDGE0, 64, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_VO1_BRIDGE1, 64, 15),
+
+ /* SOFTRST_CON65 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S9_8CH, 65, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S9_8CH_RX, 65, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_I2S10_8CH, 65, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_M_I2S10_8CH_RX, 65, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_S_HDMIRX, 65, 8),
+
+ /* SOFTRST_CON66 */
+ RK3588_CRU_RESET_OFFSET(SRST_GPU, 66, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_SYS_GPU, 66, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_S_GPU_BIU, 66, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_A_M0_GPU_BIU, 66, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_A_M1_GPU_BIU, 66, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_A_M2_GPU_BIU, 66, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_A_M3_GPU_BIU, 66, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPU_BIU, 66, 14),
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPU_PVTM, 66, 15),
+
+ /* SOFTRST_CON67 */
+ RK3588_CRU_RESET_OFFSET(SRST_GPU_PVTM, 67, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_P_GPU_GRF, 67, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_GPU_PVTPLL, 67, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_GPU_JTAG, 67, 4),
+
+ /* SOFTRST_CON68 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_AV1_BIU, 68, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_A_AV1, 68, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_AV1_BIU, 68, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_AV1, 68, 5),
+
+ /* SOFTRST_CON69 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_BIU, 69, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DMA2DDR, 69, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_SHAREMEM, 69, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_A_DDR_SHAREMEM_BIU, 69, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_A_CENTER_S200_BIU, 69, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_A_CENTER_S400_BIU, 69, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_H_AHB2APB, 69, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_H_CENTER_BIU, 69, 13),
+ RK3588_CRU_RESET_OFFSET(SRST_F_DDR_CM0_CORE, 69, 14),
+
+ /* SOFTRST_CON70 */
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_TIMER0, 70, 0),
+ RK3588_CRU_RESET_OFFSET(SRST_DDR_TIMER1, 70, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_T_WDT_DDR, 70, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_T_DDR_CM0_JTAG, 70, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CENTER_GRF, 70, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_AHB2APB, 70, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_WDT, 70, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_TIMER, 70, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_DMA2DDR, 70, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_SHAREMEM, 70, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CENTER_BIU, 70, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_P_CENTER_CHANNEL_BIU, 70, 12),
+
+ /* SOFTRST_CON72 */
+ RK3588_CRU_RESET_OFFSET(SRST_P_USBDPGRF0, 72, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USBDPPHY0, 72, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USBDPGRF1, 72, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USBDPPHY1, 72, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_P_HDPTX0, 72, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_P_HDPTX1, 72, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_BOT_RIGHT, 72, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USB2PHY_U3_0_GRF0, 72, 8),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USB2PHY_U3_1_GRF0, 72, 9),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USB2PHY_U2_0_GRF0, 72, 10),
+ RK3588_CRU_RESET_OFFSET(SRST_P_USB2PHY_U2_1_GRF0, 72, 11),
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX0_ROPLL, 72, 12), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX0_LCPLL, 72, 13), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX0, 72, 14), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX1_ROPLL, 72, 15), // missing in TRM
+
+ /* SOFTRST_CON73 */
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX1_LCPLL, 73, 0), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX1, 73, 1), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_HDPTX0_HDMIRXPHY_SET, 73, 2), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0, 73, 3), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_LCPLL, 73, 4), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_ROPLL, 73, 5), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY0_PCS_HS, 73, 6), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1, 73, 7), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_LCPLL, 73, 8), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_ROPLL, 73, 9), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY1_PCS_HS, 73, 10), // missing in TRM
+ RK3588_CRU_RESET_OFFSET(SRST_HDMIHDP0, 73, 12),
+ RK3588_CRU_RESET_OFFSET(SRST_HDMIHDP1, 73, 13),
+
+ /* SOFTRST_CON74 */
+ RK3588_CRU_RESET_OFFSET(SRST_A_VO1USB_TOP_BIU, 74, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_H_VO1USB_TOP_BIU, 74, 3),
+
+ /* SOFTRST_CON75 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_SDIO_BIU, 75, 1),
+ RK3588_CRU_RESET_OFFSET(SRST_H_SDIO, 75, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_SDIO, 75, 3),
+
+ /* SOFTRST_CON76 */
+ RK3588_CRU_RESET_OFFSET(SRST_H_RGA3_BIU, 76, 2),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RGA3_BIU, 76, 3),
+ RK3588_CRU_RESET_OFFSET(SRST_H_RGA3_1, 76, 4),
+ RK3588_CRU_RESET_OFFSET(SRST_A_RGA3_1, 76, 5),
+ RK3588_CRU_RESET_OFFSET(SRST_RGA3_1_CORE, 76, 6),
+
+ /* SOFTRST_CON77 */
+ RK3588_CRU_RESET_OFFSET(SRST_REF_PIPE_PHY0, 77, 6),
+ RK3588_CRU_RESET_OFFSET(SRST_REF_PIPE_PHY1, 77, 7),
+ RK3588_CRU_RESET_OFFSET(SRST_REF_PIPE_PHY2, 77, 8),
+
+ /* PHPTOPCRU_SOFTRST_CON00 */
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PHPTOP_CRU, 0, 1),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE2_GRF0, 0, 2),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE2_GRF1, 0, 3),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE2_GRF2, 0, 4),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE2_PHY0, 0, 5),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE2_PHY1, 0, 6),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE2_PHY2, 0, 7),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_PCIE3_PHY, 0, 8),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_CHIP_TOP, 0, 9),
+ RK3588_PHPTOPCRU_RESET_OFFSET(SRST_PCIE30_PHY, 0, 10),
+
+ /* PMU1CRU_SOFTRST_CON00 */
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_H_PMU1_BIU, 0, 10),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_BIU, 0, 11),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_H_PMU_CM0_BIU, 0, 12),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_F_PMU_CM0_CORE, 0, 13),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_T_PMU1_CM0_JTAG, 0, 14),
+
+ /* PMU1CRU_SOFTRST_CON01 */
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_DDR_FAIL_SAFE, 1, 1),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_CRU_PMU1, 1, 2),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_GRF, 1, 4),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_IOC, 1, 5),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU1WDT, 1, 6),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_T_PMU1WDT, 1, 7),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU1TIMER, 1, 8),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_PMU1TIMER0, 1, 10),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_PMU1TIMER1, 1, 11),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU1PWM, 1, 12),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_PMU1PWM, 1, 13),
+
+ /* PMU1CRU_SOFTRST_CON02 */
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_I2C0, 2, 1),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_I2C0, 2, 2),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_S_UART0, 2, 5),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_UART0, 2, 6),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_H_I2S1_8CH, 2, 7),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_M_I2S1_8CH_TX, 2, 10),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_M_I2S1_8CH_RX, 2, 13),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_H_PDM0, 2, 14),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_PDM0, 2, 15),
+
+ /* PMU1CRU_SOFTRST_CON03 */
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_H_VAD, 3, 0),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_HDPTX0_INIT, 3, 11),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_HDPTX0_CMN, 3, 12),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_HDPTX0_LANE, 3, 13),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_HDPTX1_INIT, 3, 15),
+
+ /* PMU1CRU_SOFTRST_CON04 */
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_HDPTX1_CMN, 4, 0),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_HDPTX1_LANE, 4, 1),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_M_MIPI_DCPHY0, 4, 3),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_S_MIPI_DCPHY0, 4, 4),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_M_MIPI_DCPHY1, 4, 5),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_S_MIPI_DCPHY1, 4, 6),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_U3_0, 4, 7),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_U3_1, 4, 8),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_U2_0, 4, 9),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_U2_1, 4, 10),
+
+ /* PMU1CRU_SOFTRST_CON05 */
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU0GRF, 5, 3),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU0IOC, 5, 4),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_P_GPIO0, 5, 5),
+ RK3588_PMU1CRU_RESET_OFFSET(SRST_GPIO0, 5, 6),
+
+ /* SECURECRU_SOFTRST_CON00 */
+ RK3588_SECURECRU_RESET_OFFSET(SRST_A_SECURE_NS_BIU, 0, 10),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_SECURE_NS_BIU, 0, 11),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_A_SECURE_S_BIU, 0, 12),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_SECURE_S_BIU, 0, 13),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_P_SECURE_S_BIU, 0, 14),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_CRYPTO_CORE, 0, 15),
+
+ /* SECURECRU_SOFTRST_CON01 */
+ RK3588_SECURECRU_RESET_OFFSET(SRST_CRYPTO_PKA, 1, 0),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_CRYPTO_RNG, 1, 1),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_A_CRYPTO, 1, 2),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_CRYPTO, 1, 3),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_KEYLADDER_CORE, 1, 9),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_KEYLADDER_RNG, 1, 10),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_A_KEYLADDER, 1, 11),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_KEYLADDER, 1, 12),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_P_OTPC_S, 1, 13),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_OTPC_S, 1, 14),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_WDT_S, 1, 15),
+
+ /* SECURECRU_SOFTRST_CON02 */
+ RK3588_SECURECRU_RESET_OFFSET(SRST_T_WDT_S, 2, 0),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_BOOTROM, 2, 1),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_A_DCF, 2, 2),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_P_DCF, 2, 3),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_BOOTROM_NS, 2, 5),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_P_KEYLADDER, 2, 14),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_TRNG_S, 2, 15),
+
+ /* SECURECRU_SOFTRST_CON03 */
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_TRNG_NS, 3, 0),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_D_SDMMC_BUFFER, 3, 1),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_SDMMC, 3, 2),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_H_SDMMC_BUFFER, 3, 3),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_SDMMC, 3, 4),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_P_TRNG_CHK, 3, 5),
+ RK3588_SECURECRU_RESET_OFFSET(SRST_TRNG_S, 3, 6),
+};
+
+void rk3588_rst_init(struct device_node *np, void __iomem *reg_base)
+{
+ rockchip_register_softrst_lut(np,
+ rk3588_register_offset,
+ ARRAY_SIZE(rk3588_register_offset),
+ reg_base + RK3588_SOFTRST_CON(0),
+ ROCKCHIP_SOFTRST_HIWORD_MASK);
+}
diff --git a/drivers/clk/rockchip/softrst.c b/drivers/clk/rockchip/softrst.c
index e3f3937ca0..929b227154 100644
--- a/drivers/clk/rockchip/softrst.c
+++ b/drivers/clk/rockchip/softrst.c
@@ -13,6 +13,7 @@
struct rockchip_softrst {
struct reset_controller_dev rcdev;
+ const int *lut;
void __iomem *reg_base;
int num_regs;
int num_per_reg;
@@ -26,8 +27,13 @@ static int rockchip_softrst_assert(struct reset_controller_dev *rcdev,
struct rockchip_softrst *softrst = container_of(rcdev,
struct rockchip_softrst,
rcdev);
- int bank = id / softrst->num_per_reg;
- int offset = id % softrst->num_per_reg;
+ int bank, offset;
+
+ if (softrst->lut)
+ id = softrst->lut[id];
+
+ bank = id / softrst->num_per_reg;
+ offset = id % softrst->num_per_reg;
if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) {
writel(BIT(offset) | (BIT(offset) << 16),
@@ -53,8 +59,13 @@ static int rockchip_softrst_deassert(struct reset_controller_dev *rcdev,
struct rockchip_softrst *softrst = container_of(rcdev,
struct rockchip_softrst,
rcdev);
- int bank = id / softrst->num_per_reg;
- int offset = id % softrst->num_per_reg;
+ int bank, offset;
+
+ if (softrst->lut)
+ id = softrst->lut[id];
+
+ bank = id / softrst->num_per_reg;
+ offset = id % softrst->num_per_reg;
if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) {
writel((BIT(offset) << 16), softrst->reg_base + (bank * 4));
@@ -78,9 +89,10 @@ static const struct reset_control_ops rockchip_softrst_ops = {
.deassert = rockchip_softrst_deassert,
};
-void rockchip_register_softrst(struct device_node *np,
- unsigned int num_regs,
- void __iomem *base, u8 flags)
+void rockchip_register_softrst_lut(struct device_node *np,
+ const int *lookup_table,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags)
{
struct rockchip_softrst *softrst;
int ret;
@@ -92,12 +104,16 @@ void rockchip_register_softrst(struct device_node *np,
spin_lock_init(&softrst->lock);
softrst->reg_base = base;
+ softrst->lut = lookup_table;
softrst->flags = flags;
softrst->num_regs = num_regs;
softrst->num_per_reg = (flags & ROCKCHIP_SOFTRST_HIWORD_MASK) ? 16
: 32;
- softrst->rcdev.nr_resets = num_regs * softrst->num_per_reg;
+ if (lookup_table)
+ softrst->rcdev.nr_resets = num_regs;
+ else
+ softrst->rcdev.nr_resets = num_regs * softrst->num_per_reg;
softrst->rcdev.ops = &rockchip_softrst_ops;
softrst->rcdev.of_node = np;
ret = reset_controller_register(&softrst->rcdev);
@@ -107,4 +123,4 @@ void rockchip_register_softrst(struct device_node *np,
kfree(softrst);
}
};
-EXPORT_SYMBOL_GPL(rockchip_register_softrst);
+EXPORT_SYMBOL_GPL(rockchip_register_softrst_lut);
diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c
index 2ef10f9693..349629d82a 100644
--- a/drivers/clk/sifive/sifive-prci.c
+++ b/drivers/clk/sifive/sifive-prci.c
@@ -470,14 +470,14 @@ void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd)
*
* Return: 0 upon success or a negative error code upon failure.
*/
-static int __prci_register_clocks(struct device_d *dev, struct __prci_data *pd,
+static int __prci_register_clocks(struct device *dev, struct __prci_data *pd,
const struct prci_clk_desc *desc)
{
struct clk_init_data init = { };
struct __prci_clock *pic;
int parent_count, i, r;
- parent_count = of_clk_get_parent_count(dev->device_node);
+ parent_count = of_clk_get_parent_count(dev->of_node);
if (parent_count != EXPECTED_CLK_PARENT_COUNT) {
dev_err(dev, "expected only two parent clocks, found %d\n",
parent_count);
@@ -520,7 +520,7 @@ static int __prci_register_clocks(struct device_d *dev, struct __prci_data *pd,
pd->hw_clks.clk_num = i;
- r = of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ r = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&pd->hw_clks);
if (r) {
dev_err(dev, "could not add hw_provider: %d\n", r);
@@ -536,7 +536,7 @@ static int __prci_register_clocks(struct device_d *dev, struct __prci_data *pd,
*
* Return: 0 upon success or a negative error code upon failure.
*/
-static int sifive_prci_probe(struct device_d *dev)
+static int sifive_prci_probe(struct device *dev)
{
struct resource *res;
struct __prci_data *pd;
@@ -576,8 +576,9 @@ static const struct of_device_id sifive_prci_of_match[] = {
{.compatible = "sifive,fu740-c000-prci", .data = &prci_clk_fu740},
{}
};
+MODULE_DEVICE_TABLE(of, sifive_prci_of_match);
-static struct driver_d sifive_prci_driver = {
+static struct driver sifive_prci_driver = {
.name = "sifive-clk-prci",
.of_compatible = sifive_prci_of_match,
.probe = sifive_prci_probe,
diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c
index cbdec98fc6..b66fbcdb8c 100644
--- a/drivers/clk/socfpga/clk-gate-a10.c
+++ b/drivers/clk/socfpga/clk-gate-a10.c
@@ -6,11 +6,11 @@
#include <common.h>
#include <io.h>
#include <malloc.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
-#include <mach/arria10-regs.h>
-#include <mach/arria10-system-manager.h>
+#include <mach/socfpga/arria10-regs.h>
+#include <mach/socfpga/arria10-system-manager.h>
#include "clk.h"
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c
index 8ee405c6c6..35de843291 100644
--- a/drivers/clk/socfpga/clk.c
+++ b/drivers/clk/socfpga/clk.c
@@ -367,7 +367,8 @@ static struct clk *socfpga_gate_clk(struct device_node *node)
return &cs->hw.clk;
}
-static void socfpga_register_clocks(struct device_d *dev, struct device_node *node)
+static void socfpga_register_clocks(struct device *dev,
+ struct device_node *node)
{
struct device_node *child;
struct clk *clk;
@@ -394,7 +395,7 @@ static void socfpga_register_clocks(struct device_d *dev, struct device_node *no
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
-static int socfpga_ccm_probe(struct device_d *dev)
+static int socfpga_ccm_probe(struct device *dev)
{
struct resource *iores;
void __iomem *regs;
@@ -407,7 +408,7 @@ static int socfpga_ccm_probe(struct device_d *dev)
clk_mgr_base_addr = regs;
- clknode = of_get_child_by_name(dev->device_node, "clocks");
+ clknode = of_get_child_by_name(dev->of_node, "clocks");
if (!clknode)
return -EINVAL;
@@ -423,8 +424,9 @@ static __maybe_unused struct of_device_id socfpga_ccm_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, socfpga_ccm_dt_ids);
-static struct driver_d socfpga_ccm_driver = {
+static struct driver socfpga_ccm_driver = {
.probe = socfpga_ccm_probe,
.name = "socfpga-ccm",
.of_compatible = DRV_OF_COMPAT(socfpga_ccm_dt_ids),
diff --git a/drivers/clk/starfive/jh7100-clkgen.c b/drivers/clk/starfive/jh7100-clkgen.c
index 36dc91991e..c93b23d448 100644
--- a/drivers/clk/starfive/jh7100-clkgen.c
+++ b/drivers/clk/starfive/jh7100-clkgen.c
@@ -332,7 +332,7 @@ static void starfive_clkgen_init(struct device_node *np, void __iomem *base)
static struct clk_onecell_data clk_data;
-static int starfive_clkgen_clk_probe(struct device_d *dev)
+static int starfive_clkgen_clk_probe(struct device *dev)
{
struct resource *iores;
@@ -340,11 +340,11 @@ static int starfive_clkgen_clk_probe(struct device_d *dev)
if (IS_ERR(iores))
return PTR_ERR(iores);
- starfive_clkgen_init(dev->device_node, IOMEM(iores->start));
+ starfive_clkgen_init(dev->of_node, IOMEM(iores->start));
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
return 0;
@@ -354,8 +354,9 @@ static __maybe_unused struct of_device_id starfive_clkgen_clk_dt_ids[] = {
{ .compatible = "starfive,jh7100-clkgen" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, starfive_clkgen_clk_dt_ids);
-static struct driver_d starfive_clkgen_clk_driver = {
+static struct driver starfive_clkgen_clk_driver = {
.probe = starfive_clkgen_clk_probe,
.name = "starfive-clkgen",
.of_compatible = starfive_clkgen_clk_dt_ids,
diff --git a/drivers/clk/stm32/Makefile b/drivers/clk/stm32/Makefile
new file mode 100644
index 0000000000..95bd2230bb
--- /dev/null
+++ b/drivers/clk/stm32/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_COMMON_CLK_STM32MP135) += clk-stm32mp13.o clk-stm32-core.o reset-stm32.o
diff --git a/drivers/clk/stm32/clk-stm32-core.c b/drivers/clk/stm32/clk-stm32-core.c
new file mode 100644
index 0000000000..5d83c8c807
--- /dev/null
+++ b/drivers/clk/stm32/clk-stm32-core.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk-provider.h>
+#include <clock.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <of.h>
+#include <of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "clk-stm32-core.h"
+#include "reset-stm32.h"
+
+static DEFINE_SPINLOCK(rlock);
+
+static int stm32_rcc_clock_init(struct device *dev,
+ const struct of_device_id *match,
+ void __iomem *base)
+{
+ const struct stm32_rcc_match_data *data = match->data;
+ struct clk_hw_onecell_data *clk_data = data->hw_clks;
+ struct device_node *np = dev_of_node(dev);
+ struct clk_hw **hws;
+ int n, max_binding;
+
+ max_binding = data->maxbinding;
+
+ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, max_binding), GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = max_binding;
+
+ hws = clk_data->hws;
+
+ for (n = 0; n < max_binding; n++)
+ hws[n] = ERR_PTR(-ENOENT);
+
+ for (n = 0; n < data->num_clocks; n++) {
+ const struct clock_config *cfg_clock = &data->tab_clocks[n];
+ struct clk_hw *hw = ERR_PTR(-ENOENT);
+
+ if (data->check_security &&
+ data->check_security(base, cfg_clock))
+ continue;
+
+ if (cfg_clock->func)
+ hw = (*cfg_clock->func)(dev, data, base, &rlock,
+ cfg_clock);
+
+ if (IS_ERR(hw)) {
+ dev_err(dev, "Can't register clk %d: %ld\n", n,
+ PTR_ERR(hw));
+ return PTR_ERR(hw);
+ }
+
+ if (cfg_clock->id != NO_ID)
+ hws[cfg_clock->id] = hw;
+ }
+
+ return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+}
+
+int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data,
+ void __iomem *base)
+{
+ const struct of_device_id *match;
+ int err;
+
+ match = of_match_node(match_data, dev_of_node(dev));
+ if (!match) {
+ dev_err(dev, "match data not found\n");
+ return -ENODEV;
+ }
+
+ /* RCC Reset Configuration */
+ err = stm32_rcc_reset_init(dev, match, base);
+ if (err) {
+ pr_err("stm32 reset failed to initialize\n");
+ return err;
+ }
+
+ /* RCC Clock Configuration */
+ err = stm32_rcc_clock_init(dev, match, base);
+ if (err) {
+ pr_err("stm32 clock failed to initialize\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static u8 stm32_mux_get_parent(void __iomem *base,
+ struct clk_stm32_clock_data *data,
+ u16 mux_id)
+{
+ const struct stm32_mux_cfg *mux = &data->muxes[mux_id];
+ u32 mask = BIT(mux->width) - 1;
+ u32 val;
+
+ val = readl(base + mux->offset) >> mux->shift;
+ val &= mask;
+
+ return val;
+}
+
+static int stm32_mux_set_parent(void __iomem *base,
+ struct clk_stm32_clock_data *data,
+ u16 mux_id, u8 index)
+{
+ const struct stm32_mux_cfg *mux = &data->muxes[mux_id];
+
+ u32 mask = BIT(mux->width) - 1;
+ u32 reg = readl(base + mux->offset);
+ u32 val = index << mux->shift;
+
+ reg &= ~(mask << mux->shift);
+ reg |= val;
+
+ writel(reg, base + mux->offset);
+
+ return 0;
+}
+
+static void stm32_gate_endisable(void __iomem *base,
+ struct clk_stm32_clock_data *data,
+ u16 gate_id, int enable)
+{
+ const struct stm32_gate_cfg *gate = &data->gates[gate_id];
+ void __iomem *addr = base + gate->offset;
+
+ if (enable) {
+ if (data->gate_cpt[gate_id]++ > 0)
+ return;
+
+ if (gate->set_clr != 0)
+ writel(BIT(gate->bit_idx), addr);
+ else
+ writel(readl(addr) | BIT(gate->bit_idx), addr);
+ } else {
+ if (--data->gate_cpt[gate_id] > 0)
+ return;
+
+ if (gate->set_clr != 0)
+ writel(BIT(gate->bit_idx), addr + gate->set_clr);
+ else
+ writel(readl(addr) & ~BIT(gate->bit_idx), addr);
+ }
+}
+
+static int stm32_gate_is_enabled(void __iomem *base,
+ struct clk_stm32_clock_data *data,
+ u16 gate_id)
+{
+ const struct stm32_gate_cfg *gate = &data->gates[gate_id];
+
+ return (readl(base + gate->offset) & BIT(gate->bit_idx)) != 0;
+}
+
+static unsigned int _get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
+static unsigned int _get_div(const struct clk_div_table *table,
+ unsigned int val, unsigned long flags, u8 width)
+{
+ if (flags & CLK_DIVIDER_ONE_BASED)
+ return val;
+ if (flags & CLK_DIVIDER_POWER_OF_TWO)
+ return 1 << val;
+ if (table)
+ return _get_table_div(table, val);
+ return val + 1;
+}
+
+static unsigned long stm32_divider_get_rate(void __iomem *base,
+ struct clk_stm32_clock_data *data,
+ u16 div_id,
+ unsigned long parent_rate)
+{
+ const struct stm32_div_cfg *divider = &data->dividers[div_id];
+ unsigned int val;
+ unsigned int div;
+
+ val = readl(base + divider->offset) >> divider->shift;
+ val &= clk_div_mask(divider->width);
+ div = _get_div(divider->table, val, divider->flags, divider->width);
+
+ if (!div) {
+ WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%d: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ div_id);
+ return parent_rate;
+ }
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+}
+
+static int stm32_divider_set_rate(void __iomem *base,
+ struct clk_stm32_clock_data *data,
+ u16 div_id, unsigned long rate,
+ unsigned long parent_rate)
+{
+ const struct stm32_div_cfg *divider = &data->dividers[div_id];
+ int value;
+ u32 val;
+
+ value = divider_get_val(rate, parent_rate, divider->table,
+ divider->width, divider->flags);
+ if (value < 0)
+ return value;
+
+ if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
+ val = clk_div_mask(divider->width) << (divider->shift + 16);
+ } else {
+ val = readl(base + divider->offset);
+ val &= ~(clk_div_mask(divider->width) << divider->shift);
+ }
+
+ val |= (u32)value << divider->shift;
+
+ writel(val, base + divider->offset);
+
+ return 0;
+}
+
+static int clk_stm32_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_stm32_mux *mux = to_clk_stm32_mux(hw);
+
+ return stm32_mux_get_parent(mux->base, mux->clock_data, mux->mux_id);
+}
+
+static int clk_stm32_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_stm32_mux *mux = to_clk_stm32_mux(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(mux->lock, flags);
+
+ stm32_mux_set_parent(mux->base, mux->clock_data, mux->mux_id, index);
+
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return 0;
+}
+
+const struct clk_ops clk_stm32_mux_ops = {
+ .round_rate = clk_mux_round_rate,
+ .get_parent = clk_stm32_mux_get_parent,
+ .set_parent = clk_stm32_mux_set_parent,
+};
+
+static void clk_stm32_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_stm32_gate *gate = to_clk_stm32_gate(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ stm32_gate_endisable(gate->base, gate->clock_data, gate->gate_id, enable);
+
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int clk_stm32_gate_enable(struct clk_hw *hw)
+{
+ clk_stm32_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+static void clk_stm32_gate_disable(struct clk_hw *hw)
+{
+ clk_stm32_gate_endisable(hw, 0);
+}
+
+static int clk_stm32_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_stm32_gate *gate = to_clk_stm32_gate(hw);
+
+ return stm32_gate_is_enabled(gate->base, gate->clock_data, gate->gate_id);
+}
+
+const struct clk_ops clk_stm32_gate_ops = {
+ .enable = clk_stm32_gate_enable,
+ .disable = clk_stm32_gate_disable,
+ .is_enabled = clk_stm32_gate_is_enabled,
+};
+
+static int clk_stm32_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_stm32_div *div = to_clk_stm32_divider(hw);
+ unsigned long flags = 0;
+ int ret;
+
+ if (div->div_id == NO_STM32_DIV)
+ return rate;
+
+ spin_lock_irqsave(div->lock, flags);
+
+ ret = stm32_divider_set_rate(div->base, div->clock_data, div->div_id, rate, parent_rate);
+
+ spin_unlock_irqrestore(div->lock, flags);
+
+ return ret;
+}
+
+static long clk_stm32_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_stm32_div *div = to_clk_stm32_divider(hw);
+ const struct stm32_div_cfg *divider;
+
+ if (div->div_id == NO_STM32_DIV)
+ return rate;
+
+ divider = &div->clock_data->dividers[div->div_id];
+
+ /* if read only, just return current value */
+ if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+ u32 val;
+
+ val = readl(div->base + divider->offset) >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_ro_round_rate(hw, rate, prate, divider->table,
+ divider->width, divider->flags,
+ val);
+ }
+
+ return divider_round_rate_parent(hw, clk_hw_get_parent(hw),
+ rate, prate, divider->table,
+ divider->width, divider->flags);
+}
+
+static unsigned long clk_stm32_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_stm32_div *div = to_clk_stm32_divider(hw);
+
+ if (div->div_id == NO_STM32_DIV)
+ return parent_rate;
+
+ return stm32_divider_get_rate(div->base, div->clock_data, div->div_id, parent_rate);
+}
+
+const struct clk_ops clk_stm32_divider_ops = {
+ .recalc_rate = clk_stm32_divider_recalc_rate,
+ .round_rate = clk_stm32_divider_round_rate,
+ .set_rate = clk_stm32_divider_set_rate,
+};
+
+static int clk_stm32_composite_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ unsigned long flags = 0;
+ int ret;
+
+ if (composite->div_id == NO_STM32_DIV)
+ return rate;
+
+ spin_lock_irqsave(composite->lock, flags);
+
+ ret = stm32_divider_set_rate(composite->base, composite->clock_data,
+ composite->div_id, rate, parent_rate);
+
+ spin_unlock_irqrestore(composite->lock, flags);
+
+ return ret;
+}
+
+static unsigned long clk_stm32_composite_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+
+ if (composite->div_id == NO_STM32_DIV)
+ return parent_rate;
+
+ return stm32_divider_get_rate(composite->base, composite->clock_data,
+ composite->div_id, parent_rate);
+}
+
+static int clk_stm32_composite_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ const struct stm32_div_cfg *divider;
+ long rate;
+
+ if (composite->div_id == NO_STM32_DIV)
+ return 0;
+
+ divider = &composite->clock_data->dividers[composite->div_id];
+
+ /* if read only, just return current value */
+ if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+ u32 val;
+
+ val = readl(composite->base + divider->offset) >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ rate = divider_ro_round_rate(hw, req->rate, &req->best_parent_rate,
+ divider->table, divider->width, divider->flags,
+ val);
+ if (rate < 0)
+ return rate;
+
+ req->rate = rate;
+ return 0;
+ }
+
+ rate = divider_round_rate_parent(hw, clk_hw_get_parent(hw),
+ req->rate, &req->best_parent_rate,
+ divider->table, divider->width, divider->flags);
+ if (rate < 0)
+ return rate;
+
+ req->rate = rate;
+ return 0;
+}
+
+static long clk_stm32_composite_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_rate_request req = {};
+ int ret;
+
+ req.rate = rate;
+ req.best_parent_rate = *prate;
+
+ ret = clk_stm32_composite_determine_rate(hw, &req);
+ if (ret)
+ return ret;
+
+ *prate = req.best_parent_rate;
+
+ return req.rate;
+}
+
+static int clk_stm32_composite_get_parent(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+
+ return stm32_mux_get_parent(composite->base, composite->clock_data, composite->mux_id);
+}
+
+static int clk_stm32_composite_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(composite->lock, flags);
+
+ stm32_mux_set_parent(composite->base, composite->clock_data, composite->mux_id, index);
+
+ spin_unlock_irqrestore(composite->lock, flags);
+
+ if (composite->clock_data->is_multi_mux) {
+ struct clk_hw *other_mux_hw = composite->clock_data->is_multi_mux(hw);
+
+ if (other_mux_hw) {
+ struct clk_hw *hwp = clk_hw_get_parent_by_index(hw, index);
+
+ clk_hw_reparent(other_mux_hw, hwp);
+ }
+ }
+
+ return 0;
+}
+
+static int clk_stm32_composite_is_enabled(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+
+ if (composite->gate_id == NO_STM32_GATE)
+ return (__clk_get_enable_count(&hw->clk) > 0);
+
+ return stm32_gate_is_enabled(composite->base, composite->clock_data, composite->gate_id);
+}
+
+#define MUX_SAFE_POSITION 0
+
+static int clk_stm32_has_safe_mux(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ const struct stm32_mux_cfg *mux = &composite->clock_data->muxes[composite->mux_id];
+
+ return !!(mux->flags & MUX_SAFE);
+}
+
+static void clk_stm32_set_safe_position_mux(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+
+ if (!clk_stm32_composite_is_enabled(hw)) {
+ unsigned long flags = 0;
+
+ if (composite->clock_data->is_multi_mux) {
+ struct clk_hw *other_mux_hw = NULL;
+
+ other_mux_hw = composite->clock_data->is_multi_mux(hw);
+
+ if (!other_mux_hw || clk_stm32_composite_is_enabled(other_mux_hw))
+ return;
+ }
+
+ spin_lock_irqsave(composite->lock, flags);
+
+ stm32_mux_set_parent(composite->base, composite->clock_data,
+ composite->mux_id, MUX_SAFE_POSITION);
+
+ spin_unlock_irqrestore(composite->lock, flags);
+ }
+}
+
+static void clk_stm32_safe_restore_position_mux(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ int sel = clk_hw_get_parent_index(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(composite->lock, flags);
+
+ stm32_mux_set_parent(composite->base, composite->clock_data, composite->mux_id, sel);
+
+ spin_unlock_irqrestore(composite->lock, flags);
+}
+
+static void clk_stm32_composite_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(composite->lock, flags);
+
+ stm32_gate_endisable(composite->base, composite->clock_data, composite->gate_id, enable);
+
+ spin_unlock_irqrestore(composite->lock, flags);
+}
+
+static int clk_stm32_composite_gate_enable(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+
+ if (composite->gate_id == NO_STM32_GATE)
+ return 0;
+
+ clk_stm32_composite_gate_endisable(hw, 1);
+
+ if (composite->mux_id != NO_STM32_MUX && clk_stm32_has_safe_mux(hw))
+ clk_stm32_safe_restore_position_mux(hw);
+
+ return 0;
+}
+
+static void clk_stm32_composite_gate_disable(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+
+ if (composite->gate_id == NO_STM32_GATE)
+ return;
+
+ clk_stm32_composite_gate_endisable(hw, 0);
+
+ if (composite->mux_id != NO_STM32_MUX && clk_stm32_has_safe_mux(hw))
+ clk_stm32_set_safe_position_mux(hw);
+}
+
+const struct clk_ops clk_stm32_composite_ops = {
+ .set_rate = clk_stm32_composite_set_rate,
+ .recalc_rate = clk_stm32_composite_recalc_rate,
+ .round_rate = clk_stm32_composite_round_rate,
+ .get_parent = clk_stm32_composite_get_parent,
+ .set_parent = clk_stm32_composite_set_parent,
+ .enable = clk_stm32_composite_gate_enable,
+ .disable = clk_stm32_composite_gate_disable,
+ .is_enabled = clk_stm32_composite_is_enabled,
+};
+
+struct clk_hw *clk_stm32_mux_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct clk_stm32_mux *mux = cfg->clock_cfg;
+ struct clk_hw *hw = &mux->hw;
+ int err;
+
+ mux->base = base;
+ mux->lock = lock;
+ mux->clock_data = data->clock_data;
+
+ err = clk_hw_register(dev, hw);
+ if (err)
+ return ERR_PTR(err);
+
+ return hw;
+}
+
+struct clk_hw *clk_stm32_gate_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct clk_stm32_gate *gate = cfg->clock_cfg;
+ struct clk_hw *hw = &gate->hw;
+ int err;
+
+ gate->base = base;
+ gate->lock = lock;
+ gate->clock_data = data->clock_data;
+
+ err = clk_hw_register(dev, hw);
+ if (err)
+ return ERR_PTR(err);
+
+ return hw;
+}
+
+struct clk_hw *clk_stm32_div_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct clk_stm32_div *div = cfg->clock_cfg;
+ struct clk_hw *hw = &div->hw;
+ int err;
+
+ div->base = base;
+ div->lock = lock;
+ div->clock_data = data->clock_data;
+
+ err = clk_hw_register(dev, hw);
+ if (err)
+ return ERR_PTR(err);
+
+ return hw;
+}
+
+struct clk_hw *clk_stm32_composite_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct clk_stm32_composite *composite = cfg->clock_cfg;
+ struct clk_hw *hw = &composite->hw;
+ int err;
+
+ composite->base = base;
+ composite->lock = lock;
+ composite->clock_data = data->clock_data;
+
+ err = clk_hw_register(dev, hw);
+ if (err)
+ return ERR_PTR(err);
+
+ return hw;
+}
diff --git a/drivers/clk/stm32/clk-stm32-core.h b/drivers/clk/stm32/clk-stm32-core.h
new file mode 100644
index 0000000000..76cffda023
--- /dev/null
+++ b/drivers/clk/stm32/clk-stm32-core.h
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk-provider.h>
+
+struct stm32_rcc_match_data;
+
+struct stm32_mux_cfg {
+ u16 offset;
+ u8 shift;
+ u8 width;
+ u8 flags;
+ u32 *table;
+ u8 ready;
+};
+
+struct stm32_gate_cfg {
+ u16 offset;
+ u8 bit_idx;
+ u8 set_clr;
+};
+
+struct stm32_div_cfg {
+ u16 offset;
+ u8 shift;
+ u8 width;
+ u8 flags;
+ u8 ready;
+ const struct clk_div_table *table;
+};
+
+struct stm32_composite_cfg {
+ int mux;
+ int gate;
+ int div;
+};
+
+#define NO_ID 0xFFFFFFFF
+
+#define NO_STM32_MUX 0xFFFF
+#define NO_STM32_DIV 0xFFFF
+#define NO_STM32_GATE 0xFFFF
+
+struct clock_config {
+ unsigned long id;
+ int sec_id;
+ void *clock_cfg;
+
+ struct clk_hw *(*func)(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg);
+};
+
+struct clk_stm32_clock_data {
+ u16 *gate_cpt;
+ const struct stm32_gate_cfg *gates;
+ const struct stm32_mux_cfg *muxes;
+ const struct stm32_div_cfg *dividers;
+ struct clk_hw *(*is_multi_mux)(struct clk_hw *hw);
+};
+
+struct stm32_rcc_match_data {
+ struct clk_hw_onecell_data *hw_clks;
+ unsigned int num_clocks;
+ const struct clock_config *tab_clocks;
+ unsigned int maxbinding;
+ struct clk_stm32_clock_data *clock_data;
+ u32 clear_offset;
+ int (*check_security)(void __iomem *base,
+ const struct clock_config *cfg);
+ int (*multi_mux)(void __iomem *base, const struct clock_config *cfg);
+};
+
+int stm32_rcc_reset_init(struct device *dev, const struct of_device_id *match,
+ void __iomem *base);
+
+int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data,
+ void __iomem *base);
+
+/* MUX define */
+#define MUX_NO_RDY 0xFF
+#define MUX_SAFE BIT(7)
+
+/* DIV define */
+#define DIV_NO_RDY 0xFF
+
+/* Definition of clock structure */
+struct clk_stm32_mux {
+ u16 mux_id;
+ struct clk_hw hw;
+ void __iomem *base;
+ struct clk_stm32_clock_data *clock_data;
+ spinlock_t *lock; /* spin lock */
+};
+
+#define to_clk_stm32_mux(_hw) container_of(_hw, struct clk_stm32_mux, hw)
+
+struct clk_stm32_gate {
+ u16 gate_id;
+ struct clk_hw hw;
+ void __iomem *base;
+ struct clk_stm32_clock_data *clock_data;
+ spinlock_t *lock; /* spin lock */
+};
+
+#define to_clk_stm32_gate(_hw) container_of(_hw, struct clk_stm32_gate, hw)
+
+struct clk_stm32_div {
+ u16 div_id;
+ struct clk_hw hw;
+ void __iomem *base;
+ struct clk_stm32_clock_data *clock_data;
+ spinlock_t *lock; /* spin lock */
+};
+
+#define to_clk_stm32_divider(_hw) container_of(_hw, struct clk_stm32_div, hw)
+
+struct clk_stm32_composite {
+ u16 gate_id;
+ u16 mux_id;
+ u16 div_id;
+ struct clk_hw hw;
+ void __iomem *base;
+ struct clk_stm32_clock_data *clock_data;
+ spinlock_t *lock; /* spin lock */
+};
+
+#define to_clk_stm32_composite(_hw) container_of(_hw, struct clk_stm32_composite, hw)
+
+/* Clock operators */
+extern const struct clk_ops clk_stm32_mux_ops;
+extern const struct clk_ops clk_stm32_gate_ops;
+extern const struct clk_ops clk_stm32_divider_ops;
+extern const struct clk_ops clk_stm32_composite_ops;
+
+/* Clock registering */
+struct clk_hw *clk_stm32_mux_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg);
+
+struct clk_hw *clk_stm32_gate_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg);
+
+struct clk_hw *clk_stm32_div_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg);
+
+struct clk_hw *clk_stm32_composite_register(struct device *dev,
+ const struct stm32_rcc_match_data *data,
+ void __iomem *base,
+ spinlock_t *lock,
+ const struct clock_config *cfg);
+
+#define STM32_CLOCK_CFG(_binding, _clk, _sec_id, _struct, _register)\
+{\
+ .id = (_binding),\
+ .sec_id = (_sec_id),\
+ .clock_cfg = (_struct) {_clk},\
+ .func = (_register),\
+}
+
+#define STM32_MUX_CFG(_binding, _clk, _sec_id)\
+ STM32_CLOCK_CFG(_binding, &(_clk), _sec_id, struct clk_stm32_mux *,\
+ &clk_stm32_mux_register)
+
+#define STM32_GATE_CFG(_binding, _clk, _sec_id)\
+ STM32_CLOCK_CFG(_binding, &(_clk), _sec_id, struct clk_stm32_gate *,\
+ &clk_stm32_gate_register)
+
+#define STM32_DIV_CFG(_binding, _clk, _sec_id)\
+ STM32_CLOCK_CFG(_binding, &(_clk), _sec_id, struct clk_stm32_div *,\
+ &clk_stm32_div_register)
+
+#define STM32_COMPOSITE_CFG(_binding, _clk, _sec_id)\
+ STM32_CLOCK_CFG(_binding, &(_clk), _sec_id, struct clk_stm32_composite *,\
+ &clk_stm32_composite_register)
diff --git a/drivers/clk/stm32/clk-stm32mp13.c b/drivers/clk/stm32/clk-stm32mp13.c
new file mode 100644
index 0000000000..7816aa16e1
--- /dev/null
+++ b/drivers/clk/stm32/clk-stm32mp13.c
@@ -0,0 +1,1611 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <init.h>
+#include <of_address.h>
+#include <linux/device.h>
+#include <dt-bindings/clock/stm32mp13-clks.h>
+#include "clk-stm32-core.h"
+#include "stm32mp13_rcc.h"
+
+#define RCC_CLR_OFFSET 0x4
+
+/* STM32 Gates definition */
+enum enum_gate_cfg {
+ GATE_MCO1,
+ GATE_MCO2,
+ GATE_DBGCK,
+ GATE_TRACECK,
+ GATE_DDRC1,
+ GATE_DDRC1LP,
+ GATE_DDRPHYC,
+ GATE_DDRPHYCLP,
+ GATE_DDRCAPB,
+ GATE_DDRCAPBLP,
+ GATE_AXIDCG,
+ GATE_DDRPHYCAPB,
+ GATE_DDRPHYCAPBLP,
+ GATE_TIM2,
+ GATE_TIM3,
+ GATE_TIM4,
+ GATE_TIM5,
+ GATE_TIM6,
+ GATE_TIM7,
+ GATE_LPTIM1,
+ GATE_SPI2,
+ GATE_SPI3,
+ GATE_USART3,
+ GATE_UART4,
+ GATE_UART5,
+ GATE_UART7,
+ GATE_UART8,
+ GATE_I2C1,
+ GATE_I2C2,
+ GATE_SPDIF,
+ GATE_TIM1,
+ GATE_TIM8,
+ GATE_SPI1,
+ GATE_USART6,
+ GATE_SAI1,
+ GATE_SAI2,
+ GATE_DFSDM,
+ GATE_ADFSDM,
+ GATE_FDCAN,
+ GATE_LPTIM2,
+ GATE_LPTIM3,
+ GATE_LPTIM4,
+ GATE_LPTIM5,
+ GATE_VREF,
+ GATE_DTS,
+ GATE_PMBCTRL,
+ GATE_HDP,
+ GATE_SYSCFG,
+ GATE_DCMIPP,
+ GATE_DDRPERFM,
+ GATE_IWDG2APB,
+ GATE_USBPHY,
+ GATE_STGENRO,
+ GATE_LTDC,
+ GATE_RTCAPB,
+ GATE_TZC,
+ GATE_ETZPC,
+ GATE_IWDG1APB,
+ GATE_BSEC,
+ GATE_STGENC,
+ GATE_USART1,
+ GATE_USART2,
+ GATE_SPI4,
+ GATE_SPI5,
+ GATE_I2C3,
+ GATE_I2C4,
+ GATE_I2C5,
+ GATE_TIM12,
+ GATE_TIM13,
+ GATE_TIM14,
+ GATE_TIM15,
+ GATE_TIM16,
+ GATE_TIM17,
+ GATE_DMA1,
+ GATE_DMA2,
+ GATE_DMAMUX1,
+ GATE_DMA3,
+ GATE_DMAMUX2,
+ GATE_ADC1,
+ GATE_ADC2,
+ GATE_USBO,
+ GATE_TSC,
+ GATE_GPIOA,
+ GATE_GPIOB,
+ GATE_GPIOC,
+ GATE_GPIOD,
+ GATE_GPIOE,
+ GATE_GPIOF,
+ GATE_GPIOG,
+ GATE_GPIOH,
+ GATE_GPIOI,
+ GATE_PKA,
+ GATE_SAES,
+ GATE_CRYP1,
+ GATE_HASH1,
+ GATE_RNG1,
+ GATE_BKPSRAM,
+ GATE_AXIMC,
+ GATE_MCE,
+ GATE_ETH1CK,
+ GATE_ETH1TX,
+ GATE_ETH1RX,
+ GATE_ETH1MAC,
+ GATE_FMC,
+ GATE_QSPI,
+ GATE_SDMMC1,
+ GATE_SDMMC2,
+ GATE_CRC1,
+ GATE_USBH,
+ GATE_ETH2CK,
+ GATE_ETH2TX,
+ GATE_ETH2RX,
+ GATE_ETH2MAC,
+ GATE_ETH1STP,
+ GATE_ETH2STP,
+ GATE_MDMA,
+ GATE_NB
+};
+
+#define _CFG_GATE(_id, _offset, _bit_idx, _offset_clr)\
+ [(_id)] = {\
+ .offset = (_offset),\
+ .bit_idx = (_bit_idx),\
+ .set_clr = (_offset_clr),\
+ }
+
+#define CFG_GATE(_id, _offset, _bit_idx)\
+ _CFG_GATE(_id, _offset, _bit_idx, 0)
+
+#define CFG_GATE_SETCLR(_id, _offset, _bit_idx)\
+ _CFG_GATE(_id, _offset, _bit_idx, RCC_CLR_OFFSET)
+
+static struct stm32_gate_cfg stm32mp13_gates[] = {
+ CFG_GATE(GATE_MCO1, RCC_MCO1CFGR, 12),
+ CFG_GATE(GATE_MCO2, RCC_MCO2CFGR, 12),
+ CFG_GATE(GATE_DBGCK, RCC_DBGCFGR, 8),
+ CFG_GATE(GATE_TRACECK, RCC_DBGCFGR, 9),
+ CFG_GATE(GATE_DDRC1, RCC_DDRITFCR, 0),
+ CFG_GATE(GATE_DDRC1LP, RCC_DDRITFCR, 1),
+ CFG_GATE(GATE_DDRPHYC, RCC_DDRITFCR, 4),
+ CFG_GATE(GATE_DDRPHYCLP, RCC_DDRITFCR, 5),
+ CFG_GATE(GATE_DDRCAPB, RCC_DDRITFCR, 6),
+ CFG_GATE(GATE_DDRCAPBLP, RCC_DDRITFCR, 7),
+ CFG_GATE(GATE_AXIDCG, RCC_DDRITFCR, 8),
+ CFG_GATE(GATE_DDRPHYCAPB, RCC_DDRITFCR, 9),
+ CFG_GATE(GATE_DDRPHYCAPBLP, RCC_DDRITFCR, 10),
+ CFG_GATE_SETCLR(GATE_TIM2, RCC_MP_APB1ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_TIM3, RCC_MP_APB1ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_TIM4, RCC_MP_APB1ENSETR, 2),
+ CFG_GATE_SETCLR(GATE_TIM5, RCC_MP_APB1ENSETR, 3),
+ CFG_GATE_SETCLR(GATE_TIM6, RCC_MP_APB1ENSETR, 4),
+ CFG_GATE_SETCLR(GATE_TIM7, RCC_MP_APB1ENSETR, 5),
+ CFG_GATE_SETCLR(GATE_LPTIM1, RCC_MP_APB1ENSETR, 9),
+ CFG_GATE_SETCLR(GATE_SPI2, RCC_MP_APB1ENSETR, 11),
+ CFG_GATE_SETCLR(GATE_SPI3, RCC_MP_APB1ENSETR, 12),
+ CFG_GATE_SETCLR(GATE_USART3, RCC_MP_APB1ENSETR, 15),
+ CFG_GATE_SETCLR(GATE_UART4, RCC_MP_APB1ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_UART5, RCC_MP_APB1ENSETR, 17),
+ CFG_GATE_SETCLR(GATE_UART7, RCC_MP_APB1ENSETR, 18),
+ CFG_GATE_SETCLR(GATE_UART8, RCC_MP_APB1ENSETR, 19),
+ CFG_GATE_SETCLR(GATE_I2C1, RCC_MP_APB1ENSETR, 21),
+ CFG_GATE_SETCLR(GATE_I2C2, RCC_MP_APB1ENSETR, 22),
+ CFG_GATE_SETCLR(GATE_SPDIF, RCC_MP_APB1ENSETR, 26),
+ CFG_GATE_SETCLR(GATE_TIM1, RCC_MP_APB2ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_TIM8, RCC_MP_APB2ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_SPI1, RCC_MP_APB2ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_USART6, RCC_MP_APB2ENSETR, 13),
+ CFG_GATE_SETCLR(GATE_SAI1, RCC_MP_APB2ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_SAI2, RCC_MP_APB2ENSETR, 17),
+ CFG_GATE_SETCLR(GATE_DFSDM, RCC_MP_APB2ENSETR, 20),
+ CFG_GATE_SETCLR(GATE_ADFSDM, RCC_MP_APB2ENSETR, 21),
+ CFG_GATE_SETCLR(GATE_FDCAN, RCC_MP_APB2ENSETR, 24),
+ CFG_GATE_SETCLR(GATE_LPTIM2, RCC_MP_APB3ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_LPTIM3, RCC_MP_APB3ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_LPTIM4, RCC_MP_APB3ENSETR, 2),
+ CFG_GATE_SETCLR(GATE_LPTIM5, RCC_MP_APB3ENSETR, 3),
+ CFG_GATE_SETCLR(GATE_VREF, RCC_MP_APB3ENSETR, 13),
+ CFG_GATE_SETCLR(GATE_DTS, RCC_MP_APB3ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_PMBCTRL, RCC_MP_APB3ENSETR, 17),
+ CFG_GATE_SETCLR(GATE_HDP, RCC_MP_APB3ENSETR, 20),
+ CFG_GATE_SETCLR(GATE_SYSCFG, RCC_MP_NS_APB3ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_DCMIPP, RCC_MP_APB4ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_DDRPERFM, RCC_MP_APB4ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_IWDG2APB, RCC_MP_APB4ENSETR, 15),
+ CFG_GATE_SETCLR(GATE_USBPHY, RCC_MP_APB4ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_STGENRO, RCC_MP_APB4ENSETR, 20),
+ CFG_GATE_SETCLR(GATE_LTDC, RCC_MP_NS_APB4ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_RTCAPB, RCC_MP_APB5ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_TZC, RCC_MP_APB5ENSETR, 11),
+ CFG_GATE_SETCLR(GATE_ETZPC, RCC_MP_APB5ENSETR, 13),
+ CFG_GATE_SETCLR(GATE_IWDG1APB, RCC_MP_APB5ENSETR, 15),
+ CFG_GATE_SETCLR(GATE_BSEC, RCC_MP_APB5ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_STGENC, RCC_MP_APB5ENSETR, 20),
+ CFG_GATE_SETCLR(GATE_USART1, RCC_MP_APB6ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_USART2, RCC_MP_APB6ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_SPI4, RCC_MP_APB6ENSETR, 2),
+ CFG_GATE_SETCLR(GATE_SPI5, RCC_MP_APB6ENSETR, 3),
+ CFG_GATE_SETCLR(GATE_I2C3, RCC_MP_APB6ENSETR, 4),
+ CFG_GATE_SETCLR(GATE_I2C4, RCC_MP_APB6ENSETR, 5),
+ CFG_GATE_SETCLR(GATE_I2C5, RCC_MP_APB6ENSETR, 6),
+ CFG_GATE_SETCLR(GATE_TIM12, RCC_MP_APB6ENSETR, 7),
+ CFG_GATE_SETCLR(GATE_TIM13, RCC_MP_APB6ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_TIM14, RCC_MP_APB6ENSETR, 9),
+ CFG_GATE_SETCLR(GATE_TIM15, RCC_MP_APB6ENSETR, 10),
+ CFG_GATE_SETCLR(GATE_TIM16, RCC_MP_APB6ENSETR, 11),
+ CFG_GATE_SETCLR(GATE_TIM17, RCC_MP_APB6ENSETR, 12),
+ CFG_GATE_SETCLR(GATE_DMA1, RCC_MP_AHB2ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_DMA2, RCC_MP_AHB2ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_DMAMUX1, RCC_MP_AHB2ENSETR, 2),
+ CFG_GATE_SETCLR(GATE_DMA3, RCC_MP_AHB2ENSETR, 3),
+ CFG_GATE_SETCLR(GATE_DMAMUX2, RCC_MP_AHB2ENSETR, 4),
+ CFG_GATE_SETCLR(GATE_ADC1, RCC_MP_AHB2ENSETR, 5),
+ CFG_GATE_SETCLR(GATE_ADC2, RCC_MP_AHB2ENSETR, 6),
+ CFG_GATE_SETCLR(GATE_USBO, RCC_MP_AHB2ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_TSC, RCC_MP_AHB4ENSETR, 15),
+ CFG_GATE_SETCLR(GATE_GPIOA, RCC_MP_NS_AHB4ENSETR, 0),
+ CFG_GATE_SETCLR(GATE_GPIOB, RCC_MP_NS_AHB4ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_GPIOC, RCC_MP_NS_AHB4ENSETR, 2),
+ CFG_GATE_SETCLR(GATE_GPIOD, RCC_MP_NS_AHB4ENSETR, 3),
+ CFG_GATE_SETCLR(GATE_GPIOE, RCC_MP_NS_AHB4ENSETR, 4),
+ CFG_GATE_SETCLR(GATE_GPIOF, RCC_MP_NS_AHB4ENSETR, 5),
+ CFG_GATE_SETCLR(GATE_GPIOG, RCC_MP_NS_AHB4ENSETR, 6),
+ CFG_GATE_SETCLR(GATE_GPIOH, RCC_MP_NS_AHB4ENSETR, 7),
+ CFG_GATE_SETCLR(GATE_GPIOI, RCC_MP_NS_AHB4ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_PKA, RCC_MP_AHB5ENSETR, 2),
+ CFG_GATE_SETCLR(GATE_SAES, RCC_MP_AHB5ENSETR, 3),
+ CFG_GATE_SETCLR(GATE_CRYP1, RCC_MP_AHB5ENSETR, 4),
+ CFG_GATE_SETCLR(GATE_HASH1, RCC_MP_AHB5ENSETR, 5),
+ CFG_GATE_SETCLR(GATE_RNG1, RCC_MP_AHB5ENSETR, 6),
+ CFG_GATE_SETCLR(GATE_BKPSRAM, RCC_MP_AHB5ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_AXIMC, RCC_MP_AHB5ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_MCE, RCC_MP_AHB6ENSETR, 1),
+ CFG_GATE_SETCLR(GATE_ETH1CK, RCC_MP_AHB6ENSETR, 7),
+ CFG_GATE_SETCLR(GATE_ETH1TX, RCC_MP_AHB6ENSETR, 8),
+ CFG_GATE_SETCLR(GATE_ETH1RX, RCC_MP_AHB6ENSETR, 9),
+ CFG_GATE_SETCLR(GATE_ETH1MAC, RCC_MP_AHB6ENSETR, 10),
+ CFG_GATE_SETCLR(GATE_FMC, RCC_MP_AHB6ENSETR, 12),
+ CFG_GATE_SETCLR(GATE_QSPI, RCC_MP_AHB6ENSETR, 14),
+ CFG_GATE_SETCLR(GATE_SDMMC1, RCC_MP_AHB6ENSETR, 16),
+ CFG_GATE_SETCLR(GATE_SDMMC2, RCC_MP_AHB6ENSETR, 17),
+ CFG_GATE_SETCLR(GATE_CRC1, RCC_MP_AHB6ENSETR, 20),
+ CFG_GATE_SETCLR(GATE_USBH, RCC_MP_AHB6ENSETR, 24),
+ CFG_GATE_SETCLR(GATE_ETH2CK, RCC_MP_AHB6ENSETR, 27),
+ CFG_GATE_SETCLR(GATE_ETH2TX, RCC_MP_AHB6ENSETR, 28),
+ CFG_GATE_SETCLR(GATE_ETH2RX, RCC_MP_AHB6ENSETR, 29),
+ CFG_GATE_SETCLR(GATE_ETH2MAC, RCC_MP_AHB6ENSETR, 30),
+ CFG_GATE_SETCLR(GATE_ETH1STP, RCC_MP_AHB6LPENSETR, 11),
+ CFG_GATE_SETCLR(GATE_ETH2STP, RCC_MP_AHB6LPENSETR, 31),
+ CFG_GATE_SETCLR(GATE_MDMA, RCC_MP_NS_AHB6ENSETR, 0),
+};
+
+/* STM32 Divivers definition */
+enum enum_div_cfg {
+ DIV_RTC,
+ DIV_HSI,
+ DIV_MCO1,
+ DIV_MCO2,
+ DIV_TRACE,
+ DIV_ETH1PTP,
+ DIV_ETH2PTP,
+ DIV_NB
+};
+
+static const struct clk_div_table ck_trace_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 },
+ { 0 },
+};
+
+#define CFG_DIV(_id, _offset, _shift, _width, _flags, _table, _ready)\
+ [(_id)] = {\
+ .offset = (_offset),\
+ .shift = (_shift),\
+ .width = (_width),\
+ .flags = (_flags),\
+ .table = (_table),\
+ .ready = (_ready),\
+ }
+
+static const struct stm32_div_cfg stm32mp13_dividers[DIV_NB] = {
+ CFG_DIV(DIV_RTC, RCC_RTCDIVR, 0, 6, 0, NULL, DIV_NO_RDY),
+ CFG_DIV(DIV_MCO1, RCC_MCO1CFGR, 4, 4, 0, NULL, DIV_NO_RDY),
+ CFG_DIV(DIV_MCO2, RCC_MCO2CFGR, 4, 4, 0, NULL, DIV_NO_RDY),
+ CFG_DIV(DIV_TRACE, RCC_DBGCFGR, 0, 3, 0, ck_trace_div_table, DIV_NO_RDY),
+ CFG_DIV(DIV_ETH1PTP, RCC_ETH12CKSELR, 4, 4, 0, NULL, DIV_NO_RDY),
+ CFG_DIV(DIV_ETH2PTP, RCC_ETH12CKSELR, 12, 4, 0, NULL, DIV_NO_RDY),
+};
+
+/* STM32 Muxes definition */
+enum enum_mux_cfg {
+ MUX_ADC1,
+ MUX_ADC2,
+ MUX_DCMIPP,
+ MUX_ETH1,
+ MUX_ETH2,
+ MUX_FDCAN,
+ MUX_FMC,
+ MUX_I2C12,
+ MUX_I2C3,
+ MUX_I2C4,
+ MUX_I2C5,
+ MUX_LPTIM1,
+ MUX_LPTIM2,
+ MUX_LPTIM3,
+ MUX_LPTIM45,
+ MUX_MCO1,
+ MUX_MCO2,
+ MUX_QSPI,
+ MUX_RNG1,
+ MUX_SAES,
+ MUX_SAI1,
+ MUX_SAI2,
+ MUX_SDMMC1,
+ MUX_SDMMC2,
+ MUX_SPDIF,
+ MUX_SPI1,
+ MUX_SPI23,
+ MUX_SPI4,
+ MUX_SPI5,
+ MUX_STGEN,
+ MUX_UART1,
+ MUX_UART2,
+ MUX_UART4,
+ MUX_UART6,
+ MUX_UART35,
+ MUX_UART78,
+ MUX_USBO,
+ MUX_USBPHY,
+ MUX_NB
+};
+
+#define _CFG_MUX(_id, _offset, _shift, _witdh, _ready, _flags)\
+ [_id] = {\
+ .offset = (_offset),\
+ .shift = (_shift),\
+ .width = (_witdh),\
+ .ready = (_ready),\
+ .flags = (_flags),\
+ }
+
+#define CFG_MUX(_id, _offset, _shift, _witdh)\
+ _CFG_MUX(_id, _offset, _shift, _witdh, MUX_NO_RDY, 0)
+
+#define CFG_MUX_SAFE(_id, _offset, _shift, _witdh)\
+ _CFG_MUX(_id, _offset, _shift, _witdh, MUX_NO_RDY, MUX_SAFE)
+
+static const struct stm32_mux_cfg stm32mp13_muxes[] = {
+ CFG_MUX(MUX_I2C12, RCC_I2C12CKSELR, 0, 3),
+ CFG_MUX(MUX_LPTIM45, RCC_LPTIM45CKSELR, 0, 3),
+ CFG_MUX(MUX_SPI23, RCC_SPI2S23CKSELR, 0, 3),
+ CFG_MUX(MUX_UART35, RCC_UART35CKSELR, 0, 3),
+ CFG_MUX(MUX_UART78, RCC_UART78CKSELR, 0, 3),
+ CFG_MUX(MUX_ADC1, RCC_ADC12CKSELR, 0, 2),
+ CFG_MUX(MUX_ADC2, RCC_ADC12CKSELR, 2, 2),
+ CFG_MUX(MUX_DCMIPP, RCC_DCMIPPCKSELR, 0, 2),
+ CFG_MUX(MUX_ETH1, RCC_ETH12CKSELR, 0, 2),
+ CFG_MUX(MUX_ETH2, RCC_ETH12CKSELR, 8, 2),
+ CFG_MUX(MUX_FDCAN, RCC_FDCANCKSELR, 0, 2),
+ CFG_MUX(MUX_I2C3, RCC_I2C345CKSELR, 0, 3),
+ CFG_MUX(MUX_I2C4, RCC_I2C345CKSELR, 3, 3),
+ CFG_MUX(MUX_I2C5, RCC_I2C345CKSELR, 6, 3),
+ CFG_MUX(MUX_LPTIM1, RCC_LPTIM1CKSELR, 0, 3),
+ CFG_MUX(MUX_LPTIM2, RCC_LPTIM23CKSELR, 0, 3),
+ CFG_MUX(MUX_LPTIM3, RCC_LPTIM23CKSELR, 3, 3),
+ CFG_MUX(MUX_MCO1, RCC_MCO1CFGR, 0, 3),
+ CFG_MUX(MUX_MCO2, RCC_MCO2CFGR, 0, 3),
+ CFG_MUX(MUX_RNG1, RCC_RNG1CKSELR, 0, 2),
+ CFG_MUX(MUX_SAES, RCC_SAESCKSELR, 0, 2),
+ CFG_MUX(MUX_SAI1, RCC_SAI1CKSELR, 0, 3),
+ CFG_MUX(MUX_SAI2, RCC_SAI2CKSELR, 0, 3),
+ CFG_MUX(MUX_SPDIF, RCC_SPDIFCKSELR, 0, 2),
+ CFG_MUX(MUX_SPI1, RCC_SPI2S1CKSELR, 0, 3),
+ CFG_MUX(MUX_SPI4, RCC_SPI45CKSELR, 0, 3),
+ CFG_MUX(MUX_SPI5, RCC_SPI45CKSELR, 3, 3),
+ CFG_MUX(MUX_STGEN, RCC_STGENCKSELR, 0, 2),
+ CFG_MUX(MUX_UART1, RCC_UART12CKSELR, 0, 3),
+ CFG_MUX(MUX_UART2, RCC_UART12CKSELR, 3, 3),
+ CFG_MUX(MUX_UART4, RCC_UART4CKSELR, 0, 3),
+ CFG_MUX(MUX_UART6, RCC_UART6CKSELR, 0, 3),
+ CFG_MUX(MUX_USBO, RCC_USBCKSELR, 4, 1),
+ CFG_MUX(MUX_USBPHY, RCC_USBCKSELR, 0, 2),
+ CFG_MUX_SAFE(MUX_FMC, RCC_FMCCKSELR, 0, 2),
+ CFG_MUX_SAFE(MUX_QSPI, RCC_QSPICKSELR, 0, 2),
+ CFG_MUX_SAFE(MUX_SDMMC1, RCC_SDMMC12CKSELR, 0, 3),
+ CFG_MUX_SAFE(MUX_SDMMC2, RCC_SDMMC12CKSELR, 3, 3),
+};
+
+struct clk_stm32_securiy {
+ u32 offset;
+ u8 bit_idx;
+ unsigned long scmi_id;
+};
+
+enum security_clk {
+ SECF_NONE,
+ SECF_LPTIM2,
+ SECF_LPTIM3,
+ SECF_VREF,
+ SECF_DCMIPP,
+ SECF_USBPHY,
+ SECF_TZC,
+ SECF_ETZPC,
+ SECF_IWDG1,
+ SECF_BSEC,
+ SECF_STGENC,
+ SECF_STGENRO,
+ SECF_USART1,
+ SECF_USART2,
+ SECF_SPI4,
+ SECF_SPI5,
+ SECF_I2C3,
+ SECF_I2C4,
+ SECF_I2C5,
+ SECF_TIM12,
+ SECF_TIM13,
+ SECF_TIM14,
+ SECF_TIM15,
+ SECF_TIM16,
+ SECF_TIM17,
+ SECF_DMA3,
+ SECF_DMAMUX2,
+ SECF_ADC1,
+ SECF_ADC2,
+ SECF_USBO,
+ SECF_TSC,
+ SECF_PKA,
+ SECF_SAES,
+ SECF_CRYP1,
+ SECF_HASH1,
+ SECF_RNG1,
+ SECF_BKPSRAM,
+ SECF_MCE,
+ SECF_FMC,
+ SECF_QSPI,
+ SECF_SDMMC1,
+ SECF_SDMMC2,
+ SECF_ETH1CK,
+ SECF_ETH1TX,
+ SECF_ETH1RX,
+ SECF_ETH1MAC,
+ SECF_ETH1STP,
+ SECF_ETH2CK,
+ SECF_ETH2TX,
+ SECF_ETH2RX,
+ SECF_ETH2MAC,
+ SECF_ETH2STP,
+ SECF_MCO1,
+ SECF_MCO2
+};
+
+#define SECF(_sec_id, _offset, _bit_idx)[_sec_id] = {\
+ .offset = _offset,\
+ .bit_idx = _bit_idx,\
+ .scmi_id = -1,\
+}
+
+static const struct clk_stm32_securiy stm32mp13_security[] = {
+ SECF(SECF_LPTIM2, RCC_APB3SECSR, RCC_APB3SECSR_LPTIM2SECF),
+ SECF(SECF_LPTIM3, RCC_APB3SECSR, RCC_APB3SECSR_LPTIM3SECF),
+ SECF(SECF_VREF, RCC_APB3SECSR, RCC_APB3SECSR_VREFSECF),
+ SECF(SECF_DCMIPP, RCC_APB4SECSR, RCC_APB4SECSR_DCMIPPSECF),
+ SECF(SECF_USBPHY, RCC_APB4SECSR, RCC_APB4SECSR_USBPHYSECF),
+ SECF(SECF_TZC, RCC_APB5SECSR, RCC_APB5SECSR_TZCSECF),
+ SECF(SECF_ETZPC, RCC_APB5SECSR, RCC_APB5SECSR_ETZPCSECF),
+ SECF(SECF_IWDG1, RCC_APB5SECSR, RCC_APB5SECSR_IWDG1SECF),
+ SECF(SECF_BSEC, RCC_APB5SECSR, RCC_APB5SECSR_BSECSECF),
+ SECF(SECF_STGENC, RCC_APB5SECSR, RCC_APB5SECSR_STGENCSECF),
+ SECF(SECF_STGENRO, RCC_APB5SECSR, RCC_APB5SECSR_STGENROSECF),
+ SECF(SECF_USART1, RCC_APB6SECSR, RCC_APB6SECSR_USART1SECF),
+ SECF(SECF_USART2, RCC_APB6SECSR, RCC_APB6SECSR_USART2SECF),
+ SECF(SECF_SPI4, RCC_APB6SECSR, RCC_APB6SECSR_SPI4SECF),
+ SECF(SECF_SPI5, RCC_APB6SECSR, RCC_APB6SECSR_SPI5SECF),
+ SECF(SECF_I2C3, RCC_APB6SECSR, RCC_APB6SECSR_I2C3SECF),
+ SECF(SECF_I2C4, RCC_APB6SECSR, RCC_APB6SECSR_I2C4SECF),
+ SECF(SECF_I2C5, RCC_APB6SECSR, RCC_APB6SECSR_I2C5SECF),
+ SECF(SECF_TIM12, RCC_APB6SECSR, RCC_APB6SECSR_TIM12SECF),
+ SECF(SECF_TIM13, RCC_APB6SECSR, RCC_APB6SECSR_TIM13SECF),
+ SECF(SECF_TIM14, RCC_APB6SECSR, RCC_APB6SECSR_TIM14SECF),
+ SECF(SECF_TIM15, RCC_APB6SECSR, RCC_APB6SECSR_TIM15SECF),
+ SECF(SECF_TIM16, RCC_APB6SECSR, RCC_APB6SECSR_TIM16SECF),
+ SECF(SECF_TIM17, RCC_APB6SECSR, RCC_APB6SECSR_TIM17SECF),
+ SECF(SECF_DMA3, RCC_AHB2SECSR, RCC_AHB2SECSR_DMA3SECF),
+ SECF(SECF_DMAMUX2, RCC_AHB2SECSR, RCC_AHB2SECSR_DMAMUX2SECF),
+ SECF(SECF_ADC1, RCC_AHB2SECSR, RCC_AHB2SECSR_ADC1SECF),
+ SECF(SECF_ADC2, RCC_AHB2SECSR, RCC_AHB2SECSR_ADC2SECF),
+ SECF(SECF_USBO, RCC_AHB2SECSR, RCC_AHB2SECSR_USBOSECF),
+ SECF(SECF_TSC, RCC_AHB4SECSR, RCC_AHB4SECSR_TSCSECF),
+ SECF(SECF_PKA, RCC_AHB5SECSR, RCC_AHB5SECSR_PKASECF),
+ SECF(SECF_SAES, RCC_AHB5SECSR, RCC_AHB5SECSR_SAESSECF),
+ SECF(SECF_CRYP1, RCC_AHB5SECSR, RCC_AHB5SECSR_CRYP1SECF),
+ SECF(SECF_HASH1, RCC_AHB5SECSR, RCC_AHB5SECSR_HASH1SECF),
+ SECF(SECF_RNG1, RCC_AHB5SECSR, RCC_AHB5SECSR_RNG1SECF),
+ SECF(SECF_BKPSRAM, RCC_AHB5SECSR, RCC_AHB5SECSR_BKPSRAMSECF),
+ SECF(SECF_MCE, RCC_AHB6SECSR, RCC_AHB6SECSR_MCESECF),
+ SECF(SECF_FMC, RCC_AHB6SECSR, RCC_AHB6SECSR_FMCSECF),
+ SECF(SECF_QSPI, RCC_AHB6SECSR, RCC_AHB6SECSR_QSPISECF),
+ SECF(SECF_SDMMC1, RCC_AHB6SECSR, RCC_AHB6SECSR_SDMMC1SECF),
+ SECF(SECF_SDMMC2, RCC_AHB6SECSR, RCC_AHB6SECSR_SDMMC2SECF),
+ SECF(SECF_ETH1CK, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH1CKSECF),
+ SECF(SECF_ETH1TX, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH1TXSECF),
+ SECF(SECF_ETH1RX, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH1RXSECF),
+ SECF(SECF_ETH1MAC, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH1MACSECF),
+ SECF(SECF_ETH1STP, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH1STPSECF),
+ SECF(SECF_ETH2CK, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH2CKSECF),
+ SECF(SECF_ETH2TX, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH2TXSECF),
+ SECF(SECF_ETH2RX, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH2RXSECF),
+ SECF(SECF_ETH2MAC, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH2MACSECF),
+ SECF(SECF_ETH2STP, RCC_AHB6SECSR, RCC_AHB6SECSR_ETH2STPSECF),
+ SECF(SECF_MCO1, RCC_SECCFGR, RCC_SECCFGR_MCO1SEC),
+ SECF(SECF_MCO2, RCC_SECCFGR, RCC_SECCFGR_MCO2SEC),
+};
+
+static const char * const adc12_src[] = {
+ "pll4_r", "ck_per", "pll3_q"
+};
+
+static const char * const dcmipp_src[] = {
+ "ck_axi", "pll2_q", "pll4_p", "ck_per",
+};
+
+static const char * const eth12_src[] = {
+ "pll4_p", "pll3_q"
+};
+
+static const char * const fdcan_src[] = {
+ "ck_hse", "pll3_q", "pll4_q", "pll4_r"
+};
+
+static const char * const fmc_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_per"
+};
+
+static const char * const i2c12_src[] = {
+ "pclk1", "pll4_r", "ck_hsi", "ck_csi"
+};
+
+static const char * const i2c345_src[] = {
+ "pclk6", "pll4_r", "ck_hsi", "ck_csi"
+};
+
+static const char * const lptim1_src[] = {
+ "pclk1", "pll4_p", "pll3_q", "ck_lse", "ck_lsi", "ck_per"
+};
+
+static const char * const lptim23_src[] = {
+ "pclk3", "pll4_q", "ck_per", "ck_lse", "ck_lsi"
+};
+
+static const char * const lptim45_src[] = {
+ "pclk3", "pll4_p", "pll3_q", "ck_lse", "ck_lsi", "ck_per"
+};
+
+static const char * const mco1_src[] = {
+ "ck_hsi", "ck_hse", "ck_csi", "ck_lsi", "ck_lse"
+};
+
+static const char * const mco2_src[] = {
+ "ck_mpu", "ck_axi", "ck_mlahb", "pll4_p", "ck_hse", "ck_hsi"
+};
+
+static const char * const qspi_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_per"
+};
+
+static const char * const rng1_src[] = {
+ "ck_csi", "pll4_r", "ck_lse", "ck_lsi"
+};
+
+static const char * const saes_src[] = {
+ "ck_axi", "ck_per", "pll4_r", "ck_lsi"
+};
+
+static const char * const sai1_src[] = {
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
+};
+
+static const char * const sai2_src[] = {
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb", "pll3_r"
+};
+
+static const char * const sdmmc12_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_hsi"
+};
+
+static const char * const spdif_src[] = {
+ "pll4_p", "pll3_q", "ck_hsi"
+};
+
+static const char * const spi123_src[] = {
+ "pll4_p", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
+};
+
+static const char * const spi4_src[] = {
+ "pclk6", "pll4_q", "ck_hsi", "ck_csi", "ck_hse", "i2s_ckin"
+};
+
+static const char * const spi5_src[] = {
+ "pclk6", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const stgen_src[] = {
+ "ck_hsi", "ck_hse"
+};
+
+static const char * const usart12_src[] = {
+ "pclk6", "pll3_q", "ck_hsi", "ck_csi", "pll4_q", "ck_hse"
+};
+
+static const char * const usart34578_src[] = {
+ "pclk1", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const usart6_src[] = {
+ "pclk2", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const usbo_src[] = {
+ "pll4_r", "ck_usbo_48m"
+};
+
+static const char * const usbphy_src[] = {
+ "ck_hse", "pll4_r", "clk-hse-div2"
+};
+
+/* Timer clocks */
+static struct clk_stm32_gate tim2_k = {
+ .gate_id = GATE_TIM2,
+ .hw.init = CLK_HW_INIT("tim2_k", "timg1_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim3_k = {
+ .gate_id = GATE_TIM3,
+ .hw.init = CLK_HW_INIT("tim3_k", "timg1_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim4_k = {
+ .gate_id = GATE_TIM4,
+ .hw.init = CLK_HW_INIT("tim4_k", "timg1_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim5_k = {
+ .gate_id = GATE_TIM5,
+ .hw.init = CLK_HW_INIT("tim5_k", "timg1_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim6_k = {
+ .gate_id = GATE_TIM6,
+ .hw.init = CLK_HW_INIT("tim6_k", "timg1_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim7_k = {
+ .gate_id = GATE_TIM7,
+ .hw.init = CLK_HW_INIT("tim7_k", "timg1_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim1_k = {
+ .gate_id = GATE_TIM1,
+ .hw.init = CLK_HW_INIT("tim1_k", "timg2_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim8_k = {
+ .gate_id = GATE_TIM8,
+ .hw.init = CLK_HW_INIT("tim8_k", "timg2_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim12_k = {
+ .gate_id = GATE_TIM12,
+ .hw.init = CLK_HW_INIT("tim12_k", "timg3_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim13_k = {
+ .gate_id = GATE_TIM13,
+ .hw.init = CLK_HW_INIT("tim13_k", "timg3_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim14_k = {
+ .gate_id = GATE_TIM14,
+ .hw.init = CLK_HW_INIT("tim14_k", "timg3_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim15_k = {
+ .gate_id = GATE_TIM15,
+ .hw.init = CLK_HW_INIT("tim15_k", "timg3_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim16_k = {
+ .gate_id = GATE_TIM16,
+ .hw.init = CLK_HW_INIT("tim16_k", "timg3_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_gate tim17_k = {
+ .gate_id = GATE_TIM17,
+ .hw.init = CLK_HW_INIT("tim17_k", "timg3_ck", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+/* Peripheral clocks */
+static struct clk_stm32_gate sai1 = {
+ .gate_id = GATE_SAI1,
+ .hw.init = CLK_HW_INIT("sai1", "pclk2", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate sai2 = {
+ .gate_id = GATE_SAI2,
+ .hw.init = CLK_HW_INIT("sai2", "pclk2", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate syscfg = {
+ .gate_id = GATE_SYSCFG,
+ .hw.init = CLK_HW_INIT("syscfg", "pclk3", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate vref = {
+ .gate_id = GATE_VREF,
+ .hw.init = CLK_HW_INIT("vref", "pclk3", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate dts = {
+ .gate_id = GATE_DTS,
+ .hw.init = CLK_HW_INIT("dts", "pclk3", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate pmbctrl = {
+ .gate_id = GATE_PMBCTRL,
+ .hw.init = CLK_HW_INIT("pmbctrl", "pclk3", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate hdp = {
+ .gate_id = GATE_HDP,
+ .hw.init = CLK_HW_INIT("hdp", "pclk3", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate iwdg2 = {
+ .gate_id = GATE_IWDG2APB,
+ .hw.init = CLK_HW_INIT("iwdg2", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate stgenro = {
+ .gate_id = GATE_STGENRO,
+ .hw.init = CLK_HW_INIT("stgenro", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpioa = {
+ .gate_id = GATE_GPIOA,
+ .hw.init = CLK_HW_INIT("gpioa", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpiob = {
+ .gate_id = GATE_GPIOB,
+ .hw.init = CLK_HW_INIT("gpiob", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpioc = {
+ .gate_id = GATE_GPIOC,
+ .hw.init = CLK_HW_INIT("gpioc", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpiod = {
+ .gate_id = GATE_GPIOD,
+ .hw.init = CLK_HW_INIT("gpiod", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpioe = {
+ .gate_id = GATE_GPIOE,
+ .hw.init = CLK_HW_INIT("gpioe", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpiof = {
+ .gate_id = GATE_GPIOF,
+ .hw.init = CLK_HW_INIT("gpiof", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpiog = {
+ .gate_id = GATE_GPIOG,
+ .hw.init = CLK_HW_INIT("gpiog", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpioh = {
+ .gate_id = GATE_GPIOH,
+ .hw.init = CLK_HW_INIT("gpioh", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate gpioi = {
+ .gate_id = GATE_GPIOI,
+ .hw.init = CLK_HW_INIT("gpioi", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate tsc = {
+ .gate_id = GATE_TSC,
+ .hw.init = CLK_HW_INIT("tsc", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate ddrperfm = {
+ .gate_id = GATE_DDRPERFM,
+ .hw.init = CLK_HW_INIT("ddrperfm", "pclk4", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate tzpc = {
+ .gate_id = GATE_TZC,
+ .hw.init = CLK_HW_INIT("tzpc", "pclk5", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate iwdg1 = {
+ .gate_id = GATE_IWDG1APB,
+ .hw.init = CLK_HW_INIT("iwdg1", "pclk5", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate bsec = {
+ .gate_id = GATE_BSEC,
+ .hw.init = CLK_HW_INIT("bsec", "pclk5", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate dma1 = {
+ .gate_id = GATE_DMA1,
+ .hw.init = CLK_HW_INIT("dma1", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate dma2 = {
+ .gate_id = GATE_DMA2,
+ .hw.init = CLK_HW_INIT("dma2", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate dmamux1 = {
+ .gate_id = GATE_DMAMUX1,
+ .hw.init = CLK_HW_INIT("dmamux1", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate dma3 = {
+ .gate_id = GATE_DMA3,
+ .hw.init = CLK_HW_INIT("dma3", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate dmamux2 = {
+ .gate_id = GATE_DMAMUX2,
+ .hw.init = CLK_HW_INIT("dmamux2", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate adc1 = {
+ .gate_id = GATE_ADC1,
+ .hw.init = CLK_HW_INIT("adc1", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate adc2 = {
+ .gate_id = GATE_ADC2,
+ .hw.init = CLK_HW_INIT("adc2", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate pka = {
+ .gate_id = GATE_PKA,
+ .hw.init = CLK_HW_INIT("pka", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate cryp1 = {
+ .gate_id = GATE_CRYP1,
+ .hw.init = CLK_HW_INIT("cryp1", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate hash1 = {
+ .gate_id = GATE_HASH1,
+ .hw.init = CLK_HW_INIT("hash1", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate bkpsram = {
+ .gate_id = GATE_BKPSRAM,
+ .hw.init = CLK_HW_INIT("bkpsram", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate mdma = {
+ .gate_id = GATE_MDMA,
+ .hw.init = CLK_HW_INIT("mdma", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth1tx = {
+ .gate_id = GATE_ETH1TX,
+ .hw.init = CLK_HW_INIT("eth1tx", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth1rx = {
+ .gate_id = GATE_ETH1RX,
+ .hw.init = CLK_HW_INIT("eth1rx", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth1mac = {
+ .gate_id = GATE_ETH1MAC,
+ .hw.init = CLK_HW_INIT("eth1mac", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth2tx = {
+ .gate_id = GATE_ETH2TX,
+ .hw.init = CLK_HW_INIT("eth2tx", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth2rx = {
+ .gate_id = GATE_ETH2RX,
+ .hw.init = CLK_HW_INIT("eth2rx", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth2mac = {
+ .gate_id = GATE_ETH2MAC,
+ .hw.init = CLK_HW_INIT("eth2mac", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate crc1 = {
+ .gate_id = GATE_CRC1,
+ .hw.init = CLK_HW_INIT("crc1", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate usbh = {
+ .gate_id = GATE_USBH,
+ .hw.init = CLK_HW_INIT("usbh", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth1stp = {
+ .gate_id = GATE_ETH1STP,
+ .hw.init = CLK_HW_INIT("eth1stp", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate eth2stp = {
+ .gate_id = GATE_ETH2STP,
+ .hw.init = CLK_HW_INIT("eth2stp", "ck_axi", &clk_stm32_gate_ops, 0),
+};
+
+/* Kernel clocks */
+static struct clk_stm32_composite sdmmc1_k = {
+ .gate_id = GATE_SDMMC1,
+ .mux_id = MUX_SDMMC1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("sdmmc1_k", sdmmc12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite sdmmc2_k = {
+ .gate_id = GATE_SDMMC2,
+ .mux_id = MUX_SDMMC2,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("sdmmc2_k", sdmmc12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite fmc_k = {
+ .gate_id = GATE_FMC,
+ .mux_id = MUX_FMC,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("fmc_k", fmc_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite qspi_k = {
+ .gate_id = GATE_QSPI,
+ .mux_id = MUX_QSPI,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("qspi_k", qspi_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite spi2_k = {
+ .gate_id = GATE_SPI2,
+ .mux_id = MUX_SPI23,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("spi2_k", spi123_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite spi3_k = {
+ .gate_id = GATE_SPI3,
+ .mux_id = MUX_SPI23,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("spi3_k", spi123_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite i2c1_k = {
+ .gate_id = GATE_I2C1,
+ .mux_id = MUX_I2C12,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("i2c1_k", i2c12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite i2c2_k = {
+ .gate_id = GATE_I2C2,
+ .mux_id = MUX_I2C12,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("i2c2_k", i2c12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite lptim4_k = {
+ .gate_id = GATE_LPTIM4,
+ .mux_id = MUX_LPTIM45,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("lptim4_k", lptim45_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite lptim5_k = {
+ .gate_id = GATE_LPTIM5,
+ .mux_id = MUX_LPTIM45,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("lptim5_k", lptim45_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite usart3_k = {
+ .gate_id = GATE_USART3,
+ .mux_id = MUX_UART35,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("usart3_k", usart34578_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite uart5_k = {
+ .gate_id = GATE_UART5,
+ .mux_id = MUX_UART35,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("uart5_k", usart34578_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite uart7_k = {
+ .gate_id = GATE_UART7,
+ .mux_id = MUX_UART78,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("uart7_k", usart34578_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite uart8_k = {
+ .gate_id = GATE_UART8,
+ .mux_id = MUX_UART78,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("uart8_k", usart34578_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite sai1_k = {
+ .gate_id = GATE_SAI1,
+ .mux_id = MUX_SAI1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("sai1_k", sai1_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite adfsdm_k = {
+ .gate_id = GATE_ADFSDM,
+ .mux_id = MUX_SAI1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("adfsdm_k", sai1_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite sai2_k = {
+ .gate_id = GATE_SAI2,
+ .mux_id = MUX_SAI2,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("sai2_k", sai2_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite adc1_k = {
+ .gate_id = GATE_ADC1,
+ .mux_id = MUX_ADC1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("adc1_k", adc12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite adc2_k = {
+ .gate_id = GATE_ADC2,
+ .mux_id = MUX_ADC2,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("adc2_k", adc12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite rng1_k = {
+ .gate_id = GATE_RNG1,
+ .mux_id = MUX_RNG1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("rng1_k", rng1_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite usbphy_k = {
+ .gate_id = GATE_USBPHY,
+ .mux_id = MUX_USBPHY,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("usbphy_k", usbphy_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite stgen_k = {
+ .gate_id = GATE_STGENC,
+ .mux_id = MUX_STGEN,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("stgen_k", stgen_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite spdif_k = {
+ .gate_id = GATE_SPDIF,
+ .mux_id = MUX_SPDIF,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("spdif_k", spdif_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite spi1_k = {
+ .gate_id = GATE_SPI1,
+ .mux_id = MUX_SPI1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("spi1_k", spi123_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite spi4_k = {
+ .gate_id = GATE_SPI4,
+ .mux_id = MUX_SPI4,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("spi4_k", spi4_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite spi5_k = {
+ .gate_id = GATE_SPI5,
+ .mux_id = MUX_SPI5,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("spi5_k", spi5_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite i2c3_k = {
+ .gate_id = GATE_I2C3,
+ .mux_id = MUX_I2C3,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("i2c3_k", i2c345_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite i2c4_k = {
+ .gate_id = GATE_I2C4,
+ .mux_id = MUX_I2C4,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("i2c4_k", i2c345_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite i2c5_k = {
+ .gate_id = GATE_I2C5,
+ .mux_id = MUX_I2C5,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("i2c5_k", i2c345_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite lptim1_k = {
+ .gate_id = GATE_LPTIM1,
+ .mux_id = MUX_LPTIM1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("lptim1_k", lptim1_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite lptim2_k = {
+ .gate_id = GATE_LPTIM2,
+ .mux_id = MUX_LPTIM2,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("lptim2_k", lptim23_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite lptim3_k = {
+ .gate_id = GATE_LPTIM3,
+ .mux_id = MUX_LPTIM3,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("lptim3_k", lptim23_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite usart1_k = {
+ .gate_id = GATE_USART1,
+ .mux_id = MUX_UART1,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("usart1_k", usart12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite usart2_k = {
+ .gate_id = GATE_USART2,
+ .mux_id = MUX_UART2,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("usart2_k", usart12_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite uart4_k = {
+ .gate_id = GATE_UART4,
+ .mux_id = MUX_UART4,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("uart4_k", usart34578_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite uart6_k = {
+ .gate_id = GATE_USART6,
+ .mux_id = MUX_UART6,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("uart6_k", usart6_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite fdcan_k = {
+ .gate_id = GATE_FDCAN,
+ .mux_id = MUX_FDCAN,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("fdcan_k", fdcan_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite dcmipp_k = {
+ .gate_id = GATE_DCMIPP,
+ .mux_id = MUX_DCMIPP,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("dcmipp_k", dcmipp_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite usbo_k = {
+ .gate_id = GATE_USBO,
+ .mux_id = MUX_USBO,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("usbo_k", usbo_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite saes_k = {
+ .gate_id = GATE_SAES,
+ .mux_id = MUX_SAES,
+ .div_id = NO_STM32_DIV,
+ .hw.init = CLK_HW_INIT_PARENTS("saes_k", saes_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_gate dfsdm_k = {
+ .gate_id = GATE_DFSDM,
+ .hw.init = CLK_HW_INIT("dfsdm_k", "ck_mlahb", &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_gate ltdc_px = {
+ .gate_id = GATE_LTDC,
+ .hw.init = CLK_HW_INIT("ltdc_px", "pll4_q", &clk_stm32_gate_ops, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_stm32_mux ck_ker_eth1 = {
+ .mux_id = MUX_ETH1,
+ .hw.init = CLK_HW_INIT_PARENTS("ck_ker_eth1", eth12_src, &clk_stm32_mux_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_gate eth1ck_k = {
+ .gate_id = GATE_ETH1CK,
+ .hw.init = CLK_HW_INIT_HW("eth1ck_k", &ck_ker_eth1.hw, &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_div eth1ptp_k = {
+ .div_id = DIV_ETH1PTP,
+ .hw.init = CLK_HW_INIT_HW("eth1ptp_k", &ck_ker_eth1.hw, &clk_stm32_divider_ops,
+ CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_mux ck_ker_eth2 = {
+ .mux_id = MUX_ETH2,
+ .hw.init = CLK_HW_INIT_PARENTS("ck_ker_eth2", eth12_src, &clk_stm32_mux_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_gate eth2ck_k = {
+ .gate_id = GATE_ETH2CK,
+ .hw.init = CLK_HW_INIT_HW("eth2ck_k", &ck_ker_eth2.hw, &clk_stm32_gate_ops, 0),
+};
+
+static struct clk_stm32_div eth2ptp_k = {
+ .div_id = DIV_ETH2PTP,
+ .hw.init = CLK_HW_INIT_HW("eth2ptp_k", &ck_ker_eth2.hw, &clk_stm32_divider_ops,
+ CLK_SET_RATE_NO_REPARENT),
+};
+
+static struct clk_stm32_composite ck_mco1 = {
+ .gate_id = GATE_MCO1,
+ .mux_id = MUX_MCO1,
+ .div_id = DIV_MCO1,
+ .hw.init = CLK_HW_INIT_PARENTS("ck_mco1", mco1_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT |
+ CLK_IGNORE_UNUSED),
+};
+
+static struct clk_stm32_composite ck_mco2 = {
+ .gate_id = GATE_MCO2,
+ .mux_id = MUX_MCO2,
+ .div_id = DIV_MCO2,
+ .hw.init = CLK_HW_INIT_PARENTS("ck_mco2", mco2_src, &clk_stm32_composite_ops,
+ CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_NO_REPARENT |
+ CLK_IGNORE_UNUSED),
+};
+
+/* Debug clocks */
+static struct clk_stm32_gate ck_sys_dbg = {
+ .gate_id = GATE_DBGCK,
+ .hw.init = CLK_HW_INIT("ck_sys_dbg", "ck_axi", &clk_stm32_gate_ops, CLK_IS_CRITICAL),
+};
+
+static struct clk_stm32_composite ck_trace = {
+ .gate_id = GATE_TRACECK,
+ .mux_id = NO_STM32_MUX,
+ .div_id = DIV_TRACE,
+ .hw.init = CLK_HW_INIT("ck_trace", "ck_axi", &clk_stm32_composite_ops, CLK_IGNORE_UNUSED),
+};
+
+static const struct clock_config stm32mp13_clock_cfg[] = {
+ /* Timer clocks */
+ STM32_GATE_CFG(TIM2_K, tim2_k, SECF_NONE),
+ STM32_GATE_CFG(TIM3_K, tim3_k, SECF_NONE),
+ STM32_GATE_CFG(TIM4_K, tim4_k, SECF_NONE),
+ STM32_GATE_CFG(TIM5_K, tim5_k, SECF_NONE),
+ STM32_GATE_CFG(TIM6_K, tim6_k, SECF_NONE),
+ STM32_GATE_CFG(TIM7_K, tim7_k, SECF_NONE),
+ STM32_GATE_CFG(TIM1_K, tim1_k, SECF_NONE),
+ STM32_GATE_CFG(TIM8_K, tim8_k, SECF_NONE),
+ STM32_GATE_CFG(TIM12_K, tim12_k, SECF_TIM12),
+ STM32_GATE_CFG(TIM13_K, tim13_k, SECF_TIM13),
+ STM32_GATE_CFG(TIM14_K, tim14_k, SECF_TIM14),
+ STM32_GATE_CFG(TIM15_K, tim15_k, SECF_TIM15),
+ STM32_GATE_CFG(TIM16_K, tim16_k, SECF_TIM16),
+ STM32_GATE_CFG(TIM17_K, tim17_k, SECF_TIM17),
+
+ /* Peripheral clocks */
+ STM32_GATE_CFG(SAI1, sai1, SECF_NONE),
+ STM32_GATE_CFG(SAI2, sai2, SECF_NONE),
+ STM32_GATE_CFG(SYSCFG, syscfg, SECF_NONE),
+ STM32_GATE_CFG(VREF, vref, SECF_VREF),
+ STM32_GATE_CFG(DTS, dts, SECF_NONE),
+ STM32_GATE_CFG(PMBCTRL, pmbctrl, SECF_NONE),
+ STM32_GATE_CFG(HDP, hdp, SECF_NONE),
+ STM32_GATE_CFG(IWDG2, iwdg2, SECF_NONE),
+ STM32_GATE_CFG(STGENRO, stgenro, SECF_STGENRO),
+ STM32_GATE_CFG(TZPC, tzpc, SECF_TZC),
+ STM32_GATE_CFG(IWDG1, iwdg1, SECF_IWDG1),
+ STM32_GATE_CFG(BSEC, bsec, SECF_BSEC),
+ STM32_GATE_CFG(DMA1, dma1, SECF_NONE),
+ STM32_GATE_CFG(DMA2, dma2, SECF_NONE),
+ STM32_GATE_CFG(DMAMUX1, dmamux1, SECF_NONE),
+ STM32_GATE_CFG(DMA3, dma3, SECF_DMA3),
+ STM32_GATE_CFG(DMAMUX2, dmamux2, SECF_DMAMUX2),
+ STM32_GATE_CFG(ADC1, adc1, SECF_ADC1),
+ STM32_GATE_CFG(ADC2, adc2, SECF_ADC2),
+ STM32_GATE_CFG(GPIOA, gpioa, SECF_NONE),
+ STM32_GATE_CFG(GPIOB, gpiob, SECF_NONE),
+ STM32_GATE_CFG(GPIOC, gpioc, SECF_NONE),
+ STM32_GATE_CFG(GPIOD, gpiod, SECF_NONE),
+ STM32_GATE_CFG(GPIOE, gpioe, SECF_NONE),
+ STM32_GATE_CFG(GPIOF, gpiof, SECF_NONE),
+ STM32_GATE_CFG(GPIOG, gpiog, SECF_NONE),
+ STM32_GATE_CFG(GPIOH, gpioh, SECF_NONE),
+ STM32_GATE_CFG(GPIOI, gpioi, SECF_NONE),
+ STM32_GATE_CFG(TSC, tsc, SECF_TZC),
+ STM32_GATE_CFG(PKA, pka, SECF_PKA),
+ STM32_GATE_CFG(CRYP1, cryp1, SECF_CRYP1),
+ STM32_GATE_CFG(HASH1, hash1, SECF_HASH1),
+ STM32_GATE_CFG(BKPSRAM, bkpsram, SECF_BKPSRAM),
+ STM32_GATE_CFG(MDMA, mdma, SECF_NONE),
+ STM32_GATE_CFG(ETH1TX, eth1tx, SECF_ETH1TX),
+ STM32_GATE_CFG(ETH1RX, eth1rx, SECF_ETH1RX),
+ STM32_GATE_CFG(ETH1MAC, eth1mac, SECF_ETH1MAC),
+ STM32_GATE_CFG(ETH2TX, eth2tx, SECF_ETH2TX),
+ STM32_GATE_CFG(ETH2RX, eth2rx, SECF_ETH2RX),
+ STM32_GATE_CFG(ETH2MAC, eth2mac, SECF_ETH2MAC),
+ STM32_GATE_CFG(CRC1, crc1, SECF_NONE),
+ STM32_GATE_CFG(USBH, usbh, SECF_NONE),
+ STM32_GATE_CFG(DDRPERFM, ddrperfm, SECF_NONE),
+ STM32_GATE_CFG(ETH1STP, eth1stp, SECF_ETH1STP),
+ STM32_GATE_CFG(ETH2STP, eth2stp, SECF_ETH2STP),
+
+ /* Kernel clocks */
+ STM32_COMPOSITE_CFG(SDMMC1_K, sdmmc1_k, SECF_SDMMC1),
+ STM32_COMPOSITE_CFG(SDMMC2_K, sdmmc2_k, SECF_SDMMC2),
+ STM32_COMPOSITE_CFG(FMC_K, fmc_k, SECF_FMC),
+ STM32_COMPOSITE_CFG(QSPI_K, qspi_k, SECF_QSPI),
+ STM32_COMPOSITE_CFG(SPI2_K, spi2_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(SPI3_K, spi3_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(I2C1_K, i2c1_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(I2C2_K, i2c2_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(LPTIM4_K, lptim4_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(LPTIM5_K, lptim5_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(USART3_K, usart3_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(UART5_K, uart5_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(UART7_K, uart7_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(UART8_K, uart8_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(SAI1_K, sai1_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(SAI2_K, sai2_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(ADFSDM_K, adfsdm_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(ADC1_K, adc1_k, SECF_ADC1),
+ STM32_COMPOSITE_CFG(ADC2_K, adc2_k, SECF_ADC2),
+ STM32_COMPOSITE_CFG(RNG1_K, rng1_k, SECF_RNG1),
+ STM32_COMPOSITE_CFG(USBPHY_K, usbphy_k, SECF_USBPHY),
+ STM32_COMPOSITE_CFG(STGEN_K, stgen_k, SECF_STGENC),
+ STM32_COMPOSITE_CFG(SPDIF_K, spdif_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(SPI1_K, spi1_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(SPI4_K, spi4_k, SECF_SPI4),
+ STM32_COMPOSITE_CFG(SPI5_K, spi5_k, SECF_SPI5),
+ STM32_COMPOSITE_CFG(I2C3_K, i2c3_k, SECF_I2C3),
+ STM32_COMPOSITE_CFG(I2C4_K, i2c4_k, SECF_I2C4),
+ STM32_COMPOSITE_CFG(I2C5_K, i2c5_k, SECF_I2C5),
+ STM32_COMPOSITE_CFG(LPTIM1_K, lptim1_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(LPTIM2_K, lptim2_k, SECF_LPTIM2),
+ STM32_COMPOSITE_CFG(LPTIM3_K, lptim3_k, SECF_LPTIM3),
+ STM32_COMPOSITE_CFG(USART1_K, usart1_k, SECF_USART1),
+ STM32_COMPOSITE_CFG(USART2_K, usart2_k, SECF_USART2),
+ STM32_COMPOSITE_CFG(UART4_K, uart4_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(USART6_K, uart6_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(FDCAN_K, fdcan_k, SECF_NONE),
+ STM32_COMPOSITE_CFG(DCMIPP_K, dcmipp_k, SECF_DCMIPP),
+ STM32_COMPOSITE_CFG(USBO_K, usbo_k, SECF_USBO),
+ STM32_COMPOSITE_CFG(SAES_K, saes_k, SECF_SAES),
+ STM32_GATE_CFG(DFSDM_K, dfsdm_k, SECF_NONE),
+ STM32_GATE_CFG(LTDC_PX, ltdc_px, SECF_NONE),
+
+ STM32_MUX_CFG(NO_ID, ck_ker_eth1, SECF_ETH1CK),
+ STM32_GATE_CFG(ETH1CK_K, eth1ck_k, SECF_ETH1CK),
+ STM32_DIV_CFG(ETH1PTP_K, eth1ptp_k, SECF_ETH1CK),
+
+ STM32_MUX_CFG(NO_ID, ck_ker_eth2, SECF_ETH2CK),
+ STM32_GATE_CFG(ETH2CK_K, eth2ck_k, SECF_ETH2CK),
+ STM32_DIV_CFG(ETH2PTP_K, eth2ptp_k, SECF_ETH2CK),
+
+ STM32_GATE_CFG(CK_DBG, ck_sys_dbg, SECF_NONE),
+ STM32_COMPOSITE_CFG(CK_TRACE, ck_trace, SECF_NONE),
+
+ STM32_COMPOSITE_CFG(CK_MCO1, ck_mco1, SECF_MCO1),
+ STM32_COMPOSITE_CFG(CK_MCO2, ck_mco2, SECF_MCO2),
+};
+
+static int stm32mp13_clock_is_provided_by_secure(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ int sec_id = cfg->sec_id;
+
+ if (sec_id != SECF_NONE) {
+ const struct clk_stm32_securiy *secf;
+
+ secf = &stm32mp13_security[sec_id];
+
+ return !!(readl(base + secf->offset) & BIT(secf->bit_idx));
+ }
+
+ return 0;
+}
+
+struct multi_mux {
+ struct clk_hw *hw1;
+ struct clk_hw *hw2;
+};
+
+static struct multi_mux *stm32_mp13_multi_mux[MUX_NB] = {
+ [MUX_SPI23] = &(struct multi_mux){ &spi2_k.hw, &spi3_k.hw },
+ [MUX_I2C12] = &(struct multi_mux){ &i2c1_k.hw, &i2c2_k.hw },
+ [MUX_LPTIM45] = &(struct multi_mux){ &lptim4_k.hw, &lptim5_k.hw },
+ [MUX_UART35] = &(struct multi_mux){ &usart3_k.hw, &uart5_k.hw },
+ [MUX_UART78] = &(struct multi_mux){ &uart7_k.hw, &uart8_k.hw },
+ [MUX_SAI1] = &(struct multi_mux){ &sai1_k.hw, &adfsdm_k.hw },
+};
+
+static struct clk_hw *stm32mp13_is_multi_mux(struct clk_hw *hw)
+{
+ struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
+ struct multi_mux *mmux = stm32_mp13_multi_mux[composite->mux_id];
+
+ if (mmux) {
+ if (!(mmux->hw1 == hw))
+ return mmux->hw1;
+ else
+ return mmux->hw2;
+ }
+
+ return NULL;
+}
+
+static u16 stm32mp13_cpt_gate[GATE_NB];
+
+static struct clk_stm32_clock_data stm32mp13_clock_data = {
+ .gate_cpt = stm32mp13_cpt_gate,
+ .gates = stm32mp13_gates,
+ .muxes = stm32mp13_muxes,
+ .dividers = stm32mp13_dividers,
+ .is_multi_mux = stm32mp13_is_multi_mux,
+};
+
+static const struct stm32_rcc_match_data stm32mp13_data = {
+ .tab_clocks = stm32mp13_clock_cfg,
+ .num_clocks = ARRAY_SIZE(stm32mp13_clock_cfg),
+ .clock_data = &stm32mp13_clock_data,
+ .check_security = &stm32mp13_clock_is_provided_by_secure,
+ .maxbinding = STM32MP1_LAST_CLK,
+ .clear_offset = RCC_CLR_OFFSET,
+};
+
+static const struct of_device_id stm32mp13_match_data[] = {
+ {
+ .compatible = "st,stm32mp13-rcc",
+ .data = &stm32mp13_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stm32mp13_match_data);
+
+static int stm32mp1_rcc_init(struct device *dev)
+{
+ void __iomem *rcc_base;
+ int ret = -ENOMEM;
+
+ rcc_base = of_iomap(dev_of_node(dev), 0);
+ if (!rcc_base) {
+ dev_err(dev, "%pOFn: unable to map resource", dev_of_node(dev));
+ goto out;
+ }
+
+ ret = stm32_rcc_init(dev, stm32mp13_match_data, rcc_base);
+out:
+ if (ret) {
+ of_node_put(dev_of_node(dev));
+ }
+
+ return ret;
+}
+
+static int get_clock_deps(struct device *dev)
+{
+ static const char * const clock_deps_name[] = {
+ "hsi", "hse", "csi", "lsi", "lse",
+ };
+ size_t deps_size = sizeof(struct clk *) * ARRAY_SIZE(clock_deps_name);
+ struct clk **clk_deps;
+ int i;
+
+ clk_deps = devm_kzalloc(dev, deps_size, GFP_KERNEL);
+ if (!clk_deps)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(clock_deps_name); i++) {
+ struct clk *clk = of_clk_get_by_name(dev_of_node(dev),
+ clock_deps_name[i]);
+
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) != -EINVAL && PTR_ERR(clk) != -ENOENT)
+ return PTR_ERR(clk);
+ } else {
+ /* Device gets a reference count on the clock */
+ clk_deps[i] = clk_get(dev, __clk_get_name(clk));
+ clk_put(clk);
+ }
+ }
+
+ return 0;
+}
+
+static int stm32mp1_rcc_clocks_probe(struct device *dev)
+{
+ int ret = get_clock_deps(dev);
+
+ if (!ret)
+ ret = stm32mp1_rcc_init(dev);
+
+ return ret;
+}
+
+static void stm32mp1_rcc_clocks_remove(struct device *dev)
+{
+ struct device_node *child, *np = dev_of_node(dev);
+
+ for_each_available_child_of_node(np, child)
+ of_clk_del_provider(child);
+}
+
+static struct driver stm32mp13_rcc_clocks_driver = {
+ .name = "stm32mp13_rcc",
+ .of_match_table = stm32mp13_match_data,
+ .probe = stm32mp1_rcc_clocks_probe,
+ .remove = stm32mp1_rcc_clocks_remove,
+};
+
+static int __init stm32mp13_clocks_init(void)
+{
+ return platform_driver_register(&stm32mp13_rcc_clocks_driver);
+}
+core_initcall(stm32mp13_clocks_init);
diff --git a/drivers/clk/stm32/reset-stm32.c b/drivers/clk/stm32/reset-stm32.c
new file mode 100644
index 0000000000..534afd0942
--- /dev/null
+++ b/drivers/clk/stm32/reset-stm32.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
+ */
+
+#include <of.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "clk-stm32-core.h"
+
+#define STM32_RESET_ID_MASK GENMASK(15, 0)
+
+struct stm32_reset_data {
+ /* reset lock */
+ spinlock_t lock;
+ struct reset_controller_dev rcdev;
+ void __iomem *membase;
+ u32 clear_offset;
+};
+
+static inline struct stm32_reset_data *
+to_stm32_reset_data(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct stm32_reset_data, rcdev);
+}
+
+static int stm32_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+
+ if (data->clear_offset) {
+ void __iomem *addr;
+
+ addr = data->membase + (bank * reg_width);
+ if (!assert)
+ addr += data->clear_offset;
+
+ writel(BIT(offset), addr);
+
+ } else {
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ reg = readl(data->membase + (bank * reg_width));
+
+ if (assert)
+ reg |= BIT(offset);
+ else
+ reg &= ~BIT(offset);
+
+ writel(reg, data->membase + (bank * reg_width));
+
+ spin_unlock_irqrestore(&data->lock, flags);
+ }
+
+ return 0;
+}
+
+static int stm32_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return stm32_reset_update(rcdev, id, true);
+}
+
+static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return stm32_reset_update(rcdev, id, false);
+}
+
+static int stm32_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+ u32 reg;
+
+ reg = readl(data->membase + (bank * reg_width));
+
+ return !!(reg & BIT(offset));
+}
+
+static const struct reset_control_ops stm32_reset_ops = {
+ .assert = stm32_reset_assert,
+ .deassert = stm32_reset_deassert,
+ .status = stm32_reset_status,
+};
+
+int stm32_rcc_reset_init(struct device *dev, const struct of_device_id *match,
+ void __iomem *base)
+{
+ const struct stm32_rcc_match_data *data = match->data;
+ struct stm32_reset_data *reset_data = NULL;
+
+ data = match->data;
+
+ reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+ if (!reset_data)
+ return -ENOMEM;
+
+ spin_lock_init(&reset_data->lock);
+ reset_data->membase = base;
+ reset_data->rcdev.ops = &stm32_reset_ops;
+ reset_data->rcdev.of_node = dev_of_node(dev);
+ reset_data->rcdev.nr_resets = STM32_RESET_ID_MASK;
+ reset_data->clear_offset = data->clear_offset;
+
+ return reset_controller_register(&reset_data->rcdev);
+}
diff --git a/drivers/clk/stm32/reset-stm32.h b/drivers/clk/stm32/reset-stm32.h
new file mode 100644
index 0000000000..6eb6ea4b55
--- /dev/null
+++ b/drivers/clk/stm32/reset-stm32.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
+ */
+
+int stm32_rcc_reset_init(struct device *dev, const struct of_device_id *match,
+ void __iomem *base);
diff --git a/drivers/clk/stm32/stm32mp13_rcc.h b/drivers/clk/stm32/stm32mp13_rcc.h
new file mode 100644
index 0000000000..a82512ae08
--- /dev/null
+++ b/drivers/clk/stm32/stm32mp13_rcc.h
@@ -0,0 +1,1748 @@
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
+/*
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
+ *
+ * Configuration settings for the STM32MP13x CPU
+ */
+
+#ifndef STM32MP13_RCC_H
+#define STM32MP13_RCC_H
+/* RCC registers */
+#define RCC_SECCFGR 0x0
+#define RCC_MP_SREQSETR 0x100
+#define RCC_MP_SREQCLRR 0x104
+#define RCC_MP_APRSTCR 0x108
+#define RCC_MP_APRSTSR 0x10c
+#define RCC_PWRLPDLYCR 0x110
+#define RCC_MP_GRSTCSETR 0x114
+#define RCC_BR_RSTSCLRR 0x118
+#define RCC_MP_RSTSSETR 0x11c
+#define RCC_MP_RSTSCLRR 0x120
+#define RCC_MP_IWDGFZSETR 0x124
+#define RCC_MP_IWDGFZCLRR 0x128
+#define RCC_MP_CIER 0x200
+#define RCC_MP_CIFR 0x204
+#define RCC_BDCR 0x400
+#define RCC_RDLSICR 0x404
+#define RCC_OCENSETR 0x420
+#define RCC_OCENCLRR 0x424
+#define RCC_OCRDYR 0x428
+#define RCC_HSICFGR 0x440
+#define RCC_CSICFGR 0x444
+#define RCC_MCO1CFGR 0x460
+#define RCC_MCO2CFGR 0x464
+#define RCC_DBGCFGR 0x468
+#define RCC_RCK12SELR 0x480
+#define RCC_RCK3SELR 0x484
+#define RCC_RCK4SELR 0x488
+#define RCC_PLL1CR 0x4a0
+#define RCC_PLL1CFGR1 0x4a4
+#define RCC_PLL1CFGR2 0x4a8
+#define RCC_PLL1FRACR 0x4ac
+#define RCC_PLL1CSGR 0x4b0
+#define RCC_PLL2CR 0x4d0
+#define RCC_PLL2CFGR1 0x4d4
+#define RCC_PLL2CFGR2 0x4d8
+#define RCC_PLL2FRACR 0x4dc
+#define RCC_PLL2CSGR 0x4e0
+#define RCC_PLL3CR 0x500
+#define RCC_PLL3CFGR1 0x504
+#define RCC_PLL3CFGR2 0x508
+#define RCC_PLL3FRACR 0x50c
+#define RCC_PLL3CSGR 0x510
+#define RCC_PLL4CR 0x520
+#define RCC_PLL4CFGR1 0x524
+#define RCC_PLL4CFGR2 0x528
+#define RCC_PLL4FRACR 0x52c
+#define RCC_PLL4CSGR 0x530
+#define RCC_MPCKSELR 0x540
+#define RCC_ASSCKSELR 0x544
+#define RCC_MSSCKSELR 0x548
+#define RCC_CPERCKSELR 0x54c
+#define RCC_RTCDIVR 0x560
+#define RCC_MPCKDIVR 0x564
+#define RCC_AXIDIVR 0x568
+#define RCC_MLAHBDIVR 0x56c
+#define RCC_APB1DIVR 0x570
+#define RCC_APB2DIVR 0x574
+#define RCC_APB3DIVR 0x578
+#define RCC_APB4DIVR 0x57c
+#define RCC_APB5DIVR 0x580
+#define RCC_APB6DIVR 0x584
+#define RCC_TIMG1PRER 0x5a0
+#define RCC_TIMG2PRER 0x5a4
+#define RCC_TIMG3PRER 0x5a8
+#define RCC_DDRITFCR 0x5c0
+#define RCC_I2C12CKSELR 0x600
+#define RCC_I2C345CKSELR 0x604
+#define RCC_SPI2S1CKSELR 0x608
+#define RCC_SPI2S23CKSELR 0x60c
+#define RCC_SPI45CKSELR 0x610
+#define RCC_UART12CKSELR 0x614
+#define RCC_UART35CKSELR 0x618
+#define RCC_UART4CKSELR 0x61c
+#define RCC_UART6CKSELR 0x620
+#define RCC_UART78CKSELR 0x624
+#define RCC_LPTIM1CKSELR 0x628
+#define RCC_LPTIM23CKSELR 0x62c
+#define RCC_LPTIM45CKSELR 0x630
+#define RCC_SAI1CKSELR 0x634
+#define RCC_SAI2CKSELR 0x638
+#define RCC_FDCANCKSELR 0x63c
+#define RCC_SPDIFCKSELR 0x640
+#define RCC_ADC12CKSELR 0x644
+#define RCC_SDMMC12CKSELR 0x648
+#define RCC_ETH12CKSELR 0x64c
+#define RCC_USBCKSELR 0x650
+#define RCC_QSPICKSELR 0x654
+#define RCC_FMCCKSELR 0x658
+#define RCC_RNG1CKSELR 0x65c
+#define RCC_STGENCKSELR 0x660
+#define RCC_DCMIPPCKSELR 0x664
+#define RCC_SAESCKSELR 0x668
+#define RCC_APB1RSTSETR 0x6a0
+#define RCC_APB1RSTCLRR 0x6a4
+#define RCC_APB2RSTSETR 0x6a8
+#define RCC_APB2RSTCLRR 0x6ac
+#define RCC_APB3RSTSETR 0x6b0
+#define RCC_APB3RSTCLRR 0x6b4
+#define RCC_APB4RSTSETR 0x6b8
+#define RCC_APB4RSTCLRR 0x6bc
+#define RCC_APB5RSTSETR 0x6c0
+#define RCC_APB5RSTCLRR 0x6c4
+#define RCC_APB6RSTSETR 0x6c8
+#define RCC_APB6RSTCLRR 0x6cc
+#define RCC_AHB2RSTSETR 0x6d0
+#define RCC_AHB2RSTCLRR 0x6d4
+#define RCC_AHB4RSTSETR 0x6e0
+#define RCC_AHB4RSTCLRR 0x6e4
+#define RCC_AHB5RSTSETR 0x6e8
+#define RCC_AHB5RSTCLRR 0x6ec
+#define RCC_AHB6RSTSETR 0x6f0
+#define RCC_AHB6RSTCLRR 0x6f4
+#define RCC_MP_APB1ENSETR 0x700
+#define RCC_MP_APB1ENCLRR 0x704
+#define RCC_MP_APB2ENSETR 0x708
+#define RCC_MP_APB2ENCLRR 0x70c
+#define RCC_MP_APB3ENSETR 0x710
+#define RCC_MP_APB3ENCLRR 0x714
+#define RCC_MP_S_APB3ENSETR 0x718
+#define RCC_MP_S_APB3ENCLRR 0x71c
+#define RCC_MP_NS_APB3ENSETR 0x720
+#define RCC_MP_NS_APB3ENCLRR 0x724
+#define RCC_MP_APB4ENSETR 0x728
+#define RCC_MP_APB4ENCLRR 0x72c
+#define RCC_MP_S_APB4ENSETR 0x730
+#define RCC_MP_S_APB4ENCLRR 0x734
+#define RCC_MP_NS_APB4ENSETR 0x738
+#define RCC_MP_NS_APB4ENCLRR 0x73c
+#define RCC_MP_APB5ENSETR 0x740
+#define RCC_MP_APB5ENCLRR 0x744
+#define RCC_MP_APB6ENSETR 0x748
+#define RCC_MP_APB6ENCLRR 0x74c
+#define RCC_MP_AHB2ENSETR 0x750
+#define RCC_MP_AHB2ENCLRR 0x754
+#define RCC_MP_AHB4ENSETR 0x760
+#define RCC_MP_AHB4ENCLRR 0x764
+#define RCC_MP_S_AHB4ENSETR 0x768
+#define RCC_MP_S_AHB4ENCLRR 0x76c
+#define RCC_MP_NS_AHB4ENSETR 0x770
+#define RCC_MP_NS_AHB4ENCLRR 0x774
+#define RCC_MP_AHB5ENSETR 0x778
+#define RCC_MP_AHB5ENCLRR 0x77c
+#define RCC_MP_AHB6ENSETR 0x780
+#define RCC_MP_AHB6ENCLRR 0x784
+#define RCC_MP_S_AHB6ENSETR 0x788
+#define RCC_MP_S_AHB6ENCLRR 0x78c
+#define RCC_MP_NS_AHB6ENSETR 0x790
+#define RCC_MP_NS_AHB6ENCLRR 0x794
+#define RCC_MP_APB1LPENSETR 0x800
+#define RCC_MP_APB1LPENCLRR 0x804
+#define RCC_MP_APB2LPENSETR 0x808
+#define RCC_MP_APB2LPENCLRR 0x80c
+#define RCC_MP_APB3LPENSETR 0x810
+#define RCC_MP_APB3LPENCLRR 0x814
+#define RCC_MP_S_APB3LPENSETR 0x818
+#define RCC_MP_S_APB3LPENCLRR 0x81c
+#define RCC_MP_NS_APB3LPENSETR 0x820
+#define RCC_MP_NS_APB3LPENCLRR 0x824
+#define RCC_MP_APB4LPENSETR 0x828
+#define RCC_MP_APB4LPENCLRR 0x82c
+#define RCC_MP_S_APB4LPENSETR 0x830
+#define RCC_MP_S_APB4LPENCLRR 0x834
+#define RCC_MP_NS_APB4LPENSETR 0x838
+#define RCC_MP_NS_APB4LPENCLRR 0x83c
+#define RCC_MP_APB5LPENSETR 0x840
+#define RCC_MP_APB5LPENCLRR 0x844
+#define RCC_MP_APB6LPENSETR 0x848
+#define RCC_MP_APB6LPENCLRR 0x84c
+#define RCC_MP_AHB2LPENSETR 0x850
+#define RCC_MP_AHB2LPENCLRR 0x854
+#define RCC_MP_AHB4LPENSETR 0x858
+#define RCC_MP_AHB4LPENCLRR 0x85c
+#define RCC_MP_S_AHB4LPENSETR 0x868
+#define RCC_MP_S_AHB4LPENCLRR 0x86c
+#define RCC_MP_NS_AHB4LPENSETR 0x870
+#define RCC_MP_NS_AHB4LPENCLRR 0x874
+#define RCC_MP_AHB5LPENSETR 0x878
+#define RCC_MP_AHB5LPENCLRR 0x87c
+#define RCC_MP_AHB6LPENSETR 0x880
+#define RCC_MP_AHB6LPENCLRR 0x884
+#define RCC_MP_S_AHB6LPENSETR 0x888
+#define RCC_MP_S_AHB6LPENCLRR 0x88c
+#define RCC_MP_NS_AHB6LPENSETR 0x890
+#define RCC_MP_NS_AHB6LPENCLRR 0x894
+#define RCC_MP_S_AXIMLPENSETR 0x898
+#define RCC_MP_S_AXIMLPENCLRR 0x89c
+#define RCC_MP_NS_AXIMLPENSETR 0x8a0
+#define RCC_MP_NS_AXIMLPENCLRR 0x8a4
+#define RCC_MP_MLAHBLPENSETR 0x8a8
+#define RCC_MP_MLAHBLPENCLRR 0x8ac
+#define RCC_APB3SECSR 0x8c0
+#define RCC_APB4SECSR 0x8c4
+#define RCC_APB5SECSR 0x8c8
+#define RCC_APB6SECSR 0x8cc
+#define RCC_AHB2SECSR 0x8d0
+#define RCC_AHB4SECSR 0x8d4
+#define RCC_AHB5SECSR 0x8d8
+#define RCC_AHB6SECSR 0x8dc
+#define RCC_VERR 0xff4
+#define RCC_IDR 0xff8
+#define RCC_SIDR 0xffc
+
+/* RCC_SECCFGR register fields */
+#define RCC_SECCFGR_HSISEC 0
+#define RCC_SECCFGR_CSISEC 1
+#define RCC_SECCFGR_HSESEC 2
+#define RCC_SECCFGR_LSISEC 3
+#define RCC_SECCFGR_LSESEC 4
+#define RCC_SECCFGR_PLL12SEC 8
+#define RCC_SECCFGR_PLL3SEC 9
+#define RCC_SECCFGR_PLL4SEC 10
+#define RCC_SECCFGR_MPUSEC 11
+#define RCC_SECCFGR_AXISEC 12
+#define RCC_SECCFGR_MLAHBSEC 13
+#define RCC_SECCFGR_APB3DIVSEC 16
+#define RCC_SECCFGR_APB4DIVSEC 17
+#define RCC_SECCFGR_APB5DIVSEC 18
+#define RCC_SECCFGR_APB6DIVSEC 19
+#define RCC_SECCFGR_TIMG3SEC 20
+#define RCC_SECCFGR_CPERSEC 21
+#define RCC_SECCFGR_MCO1SEC 22
+#define RCC_SECCFGR_MCO2SEC 23
+#define RCC_SECCFGR_STPSEC 24
+#define RCC_SECCFGR_RSTSEC 25
+#define RCC_SECCFGR_PWRSEC 31
+
+/* RCC_MP_SREQSETR register fields */
+#define RCC_MP_SREQSETR_STPREQ_P0 BIT(0)
+
+/* RCC_MP_SREQCLRR register fields */
+#define RCC_MP_SREQCLRR_STPREQ_P0 BIT(0)
+
+/* RCC_MP_APRSTCR register fields */
+#define RCC_MP_APRSTCR_RDCTLEN BIT(0)
+#define RCC_MP_APRSTCR_RSTTO_MASK GENMASK(14, 8)
+#define RCC_MP_APRSTCR_RSTTO_SHIFT 8
+
+/* RCC_MP_APRSTSR register fields */
+#define RCC_MP_APRSTSR_RSTTOV_MASK GENMASK(14, 8)
+#define RCC_MP_APRSTSR_RSTTOV_SHIFT 8
+
+/* RCC_PWRLPDLYCR register fields */
+#define RCC_PWRLPDLYCR_PWRLP_DLY_MASK GENMASK(21, 0)
+#define RCC_PWRLPDLYCR_PWRLP_DLY_SHIFT 0
+
+/* RCC_MP_GRSTCSETR register fields */
+#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0)
+#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4)
+
+/* RCC_BR_RSTSCLRR register fields */
+#define RCC_BR_RSTSCLRR_PORRSTF BIT(0)
+#define RCC_BR_RSTSCLRR_BORRSTF BIT(1)
+#define RCC_BR_RSTSCLRR_PADRSTF BIT(2)
+#define RCC_BR_RSTSCLRR_HCSSRSTF BIT(3)
+#define RCC_BR_RSTSCLRR_VCORERSTF BIT(4)
+#define RCC_BR_RSTSCLRR_VCPURSTF BIT(5)
+#define RCC_BR_RSTSCLRR_MPSYSRSTF BIT(6)
+#define RCC_BR_RSTSCLRR_IWDG1RSTF BIT(8)
+#define RCC_BR_RSTSCLRR_IWDG2RSTF BIT(9)
+#define RCC_BR_RSTSCLRR_MPUP0RSTF BIT(13)
+
+/* RCC_MP_RSTSSETR register fields */
+#define RCC_MP_RSTSSETR_PORRSTF BIT(0)
+#define RCC_MP_RSTSSETR_BORRSTF BIT(1)
+#define RCC_MP_RSTSSETR_PADRSTF BIT(2)
+#define RCC_MP_RSTSSETR_HCSSRSTF BIT(3)
+#define RCC_MP_RSTSSETR_VCORERSTF BIT(4)
+#define RCC_MP_RSTSSETR_VCPURSTF BIT(5)
+#define RCC_MP_RSTSSETR_MPSYSRSTF BIT(6)
+#define RCC_MP_RSTSSETR_IWDG1RSTF BIT(8)
+#define RCC_MP_RSTSSETR_IWDG2RSTF BIT(9)
+#define RCC_MP_RSTSSETR_STP2RSTF BIT(10)
+#define RCC_MP_RSTSSETR_STDBYRSTF BIT(11)
+#define RCC_MP_RSTSSETR_CSTDBYRSTF BIT(12)
+#define RCC_MP_RSTSSETR_MPUP0RSTF BIT(13)
+#define RCC_MP_RSTSSETR_SPARE BIT(15)
+
+/* RCC_MP_RSTSCLRR register fields */
+#define RCC_MP_RSTSCLRR_PORRSTF BIT(0)
+#define RCC_MP_RSTSCLRR_BORRSTF BIT(1)
+#define RCC_MP_RSTSCLRR_PADRSTF BIT(2)
+#define RCC_MP_RSTSCLRR_HCSSRSTF BIT(3)
+#define RCC_MP_RSTSCLRR_VCORERSTF BIT(4)
+#define RCC_MP_RSTSCLRR_VCPURSTF BIT(5)
+#define RCC_MP_RSTSCLRR_MPSYSRSTF BIT(6)
+#define RCC_MP_RSTSCLRR_IWDG1RSTF BIT(8)
+#define RCC_MP_RSTSCLRR_IWDG2RSTF BIT(9)
+#define RCC_MP_RSTSCLRR_STP2RSTF BIT(10)
+#define RCC_MP_RSTSCLRR_STDBYRSTF BIT(11)
+#define RCC_MP_RSTSCLRR_CSTDBYRSTF BIT(12)
+#define RCC_MP_RSTSCLRR_MPUP0RSTF BIT(13)
+#define RCC_MP_RSTSCLRR_SPARE BIT(15)
+
+/* RCC_MP_IWDGFZSETR register fields */
+#define RCC_MP_IWDGFZSETR_FZ_IWDG1 BIT(0)
+#define RCC_MP_IWDGFZSETR_FZ_IWDG2 BIT(1)
+
+/* RCC_MP_IWDGFZCLRR register fields */
+#define RCC_MP_IWDGFZCLRR_FZ_IWDG1 BIT(0)
+#define RCC_MP_IWDGFZCLRR_FZ_IWDG2 BIT(1)
+
+/* RCC_MP_CIER register fields */
+#define RCC_MP_CIER_LSIRDYIE BIT(0)
+#define RCC_MP_CIER_LSERDYIE BIT(1)
+#define RCC_MP_CIER_HSIRDYIE BIT(2)
+#define RCC_MP_CIER_HSERDYIE BIT(3)
+#define RCC_MP_CIER_CSIRDYIE BIT(4)
+#define RCC_MP_CIER_PLL1DYIE BIT(8)
+#define RCC_MP_CIER_PLL2DYIE BIT(9)
+#define RCC_MP_CIER_PLL3DYIE BIT(10)
+#define RCC_MP_CIER_PLL4DYIE BIT(11)
+#define RCC_MP_CIER_LSECSSIE BIT(16)
+#define RCC_MP_CIER_WKUPIE BIT(20)
+
+/* RCC_MP_CIFR register fields */
+#define RCC_MP_CIFR_LSIRDYF BIT(0)
+#define RCC_MP_CIFR_LSERDYF BIT(1)
+#define RCC_MP_CIFR_HSIRDYF BIT(2)
+#define RCC_MP_CIFR_HSERDYF BIT(3)
+#define RCC_MP_CIFR_CSIRDYF BIT(4)
+#define RCC_MP_CIFR_PLL1DYF BIT(8)
+#define RCC_MP_CIFR_PLL2DYF BIT(9)
+#define RCC_MP_CIFR_PLL3DYF BIT(10)
+#define RCC_MP_CIFR_PLL4DYF BIT(11)
+#define RCC_MP_CIFR_LSECSSF BIT(16)
+#define RCC_MP_CIFR_WKUPF BIT(20)
+
+/* RCC_BDCR register fields */
+#define RCC_BDCR_LSEON BIT(0)
+#define RCC_BDCR_LSEBYP BIT(1)
+#define RCC_BDCR_LSERDY BIT(2)
+#define RCC_BDCR_DIGBYP BIT(3)
+#define RCC_BDCR_LSEDRV_MASK GENMASK(5, 4)
+#define RCC_BDCR_LSECSSON BIT(8)
+#define RCC_BDCR_LSECSSD BIT(9)
+#define RCC_BDCR_RTCSRC_MASK GENMASK(17, 16)
+#define RCC_BDCR_RTCCKEN BIT(20)
+#define RCC_BDCR_VSWRST BIT(31)
+#define RCC_BDCR_LSEDRV_SHIFT 4
+#define RCC_BDCR_RTCSRC_SHIFT 16
+
+/* RCC_RDLSICR register fields */
+#define RCC_RDLSICR_LSION BIT(0)
+#define RCC_RDLSICR_LSIRDY BIT(1)
+#define RCC_RDLSICR_MRD_MASK GENMASK(20, 16)
+#define RCC_RDLSICR_EADLY_MASK GENMASK(26, 24)
+#define RCC_RDLSICR_SPARE_MASK GENMASK(31, 27)
+#define RCC_RDLSICR_MRD_SHIFT 16
+#define RCC_RDLSICR_EADLY_SHIFT 24
+#define RCC_RDLSICR_SPARE_SHIFT 27
+
+/* RCC_OCENSETR register fields */
+#define RCC_OCENSETR_HSION BIT(0)
+#define RCC_OCENSETR_HSIKERON BIT(1)
+#define RCC_OCENSETR_CSION BIT(4)
+#define RCC_OCENSETR_CSIKERON BIT(5)
+#define RCC_OCENSETR_DIGBYP BIT(7)
+#define RCC_OCENSETR_HSEON BIT(8)
+#define RCC_OCENSETR_HSEKERON BIT(9)
+#define RCC_OCENSETR_HSEBYP BIT(10)
+#define RCC_OCENSETR_HSECSSON BIT(11)
+
+/* RCC_OCENCLRR register fields */
+#define RCC_OCENCLRR_HSION BIT(0)
+#define RCC_OCENCLRR_HSIKERON BIT(1)
+#define RCC_OCENCLRR_CSION BIT(4)
+#define RCC_OCENCLRR_CSIKERON BIT(5)
+#define RCC_OCENCLRR_DIGBYP BIT(7)
+#define RCC_OCENCLRR_HSEON BIT(8)
+#define RCC_OCENCLRR_HSEKERON BIT(9)
+#define RCC_OCENCLRR_HSEBYP BIT(10)
+
+/* RCC_OCRDYR register fields */
+#define RCC_OCRDYR_HSIRDY BIT(0)
+#define RCC_OCRDYR_HSIDIVRDY BIT(2)
+#define RCC_OCRDYR_CSIRDY BIT(4)
+#define RCC_OCRDYR_HSERDY BIT(8)
+#define RCC_OCRDYR_MPUCKRDY BIT(23)
+#define RCC_OCRDYR_AXICKRDY BIT(24)
+
+/* RCC_HSICFGR register fields */
+#define RCC_HSICFGR_HSIDIV_MASK GENMASK(1, 0)
+#define RCC_HSICFGR_HSITRIM_MASK GENMASK(14, 8)
+#define RCC_HSICFGR_HSICAL_MASK GENMASK(27, 16)
+#define RCC_HSICFGR_HSIDIV_SHIFT 0
+#define RCC_HSICFGR_HSITRIM_SHIFT 8
+#define RCC_HSICFGR_HSICAL_SHIFT 16
+
+/* RCC_CSICFGR register fields */
+#define RCC_CSICFGR_CSITRIM_MASK GENMASK(12, 8)
+#define RCC_CSICFGR_CSICAL_MASK GENMASK(23, 16)
+#define RCC_CSICFGR_CSITRIM_SHIFT 8
+#define RCC_CSICFGR_CSICAL_SHIFT 16
+
+/* RCC_MCO1CFGR register fields */
+#define RCC_MCO1CFGR_MCO1SEL_MASK GENMASK(2, 0)
+#define RCC_MCO1CFGR_MCO1DIV_MASK GENMASK(7, 4)
+#define RCC_MCO1CFGR_MCO1ON BIT(12)
+#define RCC_MCO1CFGR_MCO1SEL_SHIFT 0
+#define RCC_MCO1CFGR_MCO1DIV_SHIFT 4
+
+/* RCC_MCO2CFGR register fields */
+#define RCC_MCO2CFGR_MCO2SEL_MASK GENMASK(2, 0)
+#define RCC_MCO2CFGR_MCO2DIV_MASK GENMASK(7, 4)
+#define RCC_MCO2CFGR_MCO2ON BIT(12)
+#define RCC_MCO2CFGR_MCO2SEL_SHIFT 0
+#define RCC_MCO2CFGR_MCO2DIV_SHIFT 4
+
+/* RCC_DBGCFGR register fields */
+#define RCC_DBGCFGR_TRACEDIV_MASK GENMASK(2, 0)
+#define RCC_DBGCFGR_DBGCKEN BIT(8)
+#define RCC_DBGCFGR_TRACECKEN BIT(9)
+#define RCC_DBGCFGR_DBGRST BIT(12)
+#define RCC_DBGCFGR_TRACEDIV_SHIFT 0
+
+/* RCC_RCK12SELR register fields */
+#define RCC_RCK12SELR_PLL12SRC_MASK GENMASK(1, 0)
+#define RCC_RCK12SELR_PLL12SRCRDY BIT(31)
+#define RCC_RCK12SELR_PLL12SRC_SHIFT 0
+
+/* RCC_RCK3SELR register fields */
+#define RCC_RCK3SELR_PLL3SRC_MASK GENMASK(1, 0)
+#define RCC_RCK3SELR_PLL3SRCRDY BIT(31)
+#define RCC_RCK3SELR_PLL3SRC_SHIFT 0
+
+/* RCC_RCK4SELR register fields */
+#define RCC_RCK4SELR_PLL4SRC_MASK GENMASK(1, 0)
+#define RCC_RCK4SELR_PLL4SRCRDY BIT(31)
+#define RCC_RCK4SELR_PLL4SRC_SHIFT 0
+
+/* RCC_PLL1CR register fields */
+#define RCC_PLL1CR_PLLON BIT(0)
+#define RCC_PLL1CR_PLL1RDY BIT(1)
+#define RCC_PLL1CR_SSCG_CTRL BIT(2)
+#define RCC_PLL1CR_DIVPEN BIT(4)
+#define RCC_PLL1CR_DIVQEN BIT(5)
+#define RCC_PLL1CR_DIVREN BIT(6)
+
+/* RCC_PLL1CFGR1 register fields */
+#define RCC_PLL1CFGR1_DIVN_MASK GENMASK(8, 0)
+#define RCC_PLL1CFGR1_DIVM1_MASK GENMASK(21, 16)
+#define RCC_PLL1CFGR1_DIVN_SHIFT 0
+#define RCC_PLL1CFGR1_DIVM1_SHIFT 16
+
+/* RCC_PLL1CFGR2 register fields */
+#define RCC_PLL1CFGR2_DIVP_MASK GENMASK(6, 0)
+#define RCC_PLL1CFGR2_DIVQ_MASK GENMASK(14, 8)
+#define RCC_PLL1CFGR2_DIVR_MASK GENMASK(22, 16)
+#define RCC_PLL1CFGR2_DIVP_SHIFT 0
+#define RCC_PLL1CFGR2_DIVQ_SHIFT 8
+#define RCC_PLL1CFGR2_DIVR_SHIFT 16
+
+/* RCC_PLL1FRACR register fields */
+#define RCC_PLL1FRACR_FRACV_MASK GENMASK(15, 3)
+#define RCC_PLL1FRACR_FRACLE BIT(16)
+#define RCC_PLL1FRACR_FRACV_SHIFT 3
+
+/* RCC_PLL1CSGR register fields */
+#define RCC_PLL1CSGR_MOD_PER_MASK GENMASK(12, 0)
+#define RCC_PLL1CSGR_TPDFN_DIS BIT(13)
+#define RCC_PLL1CSGR_RPDFN_DIS BIT(14)
+#define RCC_PLL1CSGR_SSCG_MODE BIT(15)
+#define RCC_PLL1CSGR_INC_STEP_MASK GENMASK(30, 16)
+#define RCC_PLL1CSGR_MOD_PER_SHIFT 0
+#define RCC_PLL1CSGR_INC_STEP_SHIFT 16
+
+/* RCC_PLL2CR register fields */
+#define RCC_PLL2CR_PLLON BIT(0)
+#define RCC_PLL2CR_PLL2RDY BIT(1)
+#define RCC_PLL2CR_SSCG_CTRL BIT(2)
+#define RCC_PLL2CR_DIVPEN BIT(4)
+#define RCC_PLL2CR_DIVQEN BIT(5)
+#define RCC_PLL2CR_DIVREN BIT(6)
+
+/* RCC_PLL2CFGR1 register fields */
+#define RCC_PLL2CFGR1_DIVN_MASK GENMASK(8, 0)
+#define RCC_PLL2CFGR1_DIVM2_MASK GENMASK(21, 16)
+#define RCC_PLL2CFGR1_DIVN_SHIFT 0
+#define RCC_PLL2CFGR1_DIVM2_SHIFT 16
+
+/* RCC_PLL2CFGR2 register fields */
+#define RCC_PLL2CFGR2_DIVP_MASK GENMASK(6, 0)
+#define RCC_PLL2CFGR2_DIVQ_MASK GENMASK(14, 8)
+#define RCC_PLL2CFGR2_DIVR_MASK GENMASK(22, 16)
+#define RCC_PLL2CFGR2_DIVP_SHIFT 0
+#define RCC_PLL2CFGR2_DIVQ_SHIFT 8
+#define RCC_PLL2CFGR2_DIVR_SHIFT 16
+
+/* RCC_PLL2FRACR register fields */
+#define RCC_PLL2FRACR_FRACV_MASK GENMASK(15, 3)
+#define RCC_PLL2FRACR_FRACLE BIT(16)
+#define RCC_PLL2FRACR_FRACV_SHIFT 3
+
+/* RCC_PLL2CSGR register fields */
+#define RCC_PLL2CSGR_MOD_PER_MASK GENMASK(12, 0)
+#define RCC_PLL2CSGR_TPDFN_DIS BIT(13)
+#define RCC_PLL2CSGR_RPDFN_DIS BIT(14)
+#define RCC_PLL2CSGR_SSCG_MODE BIT(15)
+#define RCC_PLL2CSGR_INC_STEP_MASK GENMASK(30, 16)
+#define RCC_PLL2CSGR_MOD_PER_SHIFT 0
+#define RCC_PLL2CSGR_INC_STEP_SHIFT 16
+
+/* RCC_PLL3CR register fields */
+#define RCC_PLL3CR_PLLON BIT(0)
+#define RCC_PLL3CR_PLL3RDY BIT(1)
+#define RCC_PLL3CR_SSCG_CTRL BIT(2)
+#define RCC_PLL3CR_DIVPEN BIT(4)
+#define RCC_PLL3CR_DIVQEN BIT(5)
+#define RCC_PLL3CR_DIVREN BIT(6)
+
+/* RCC_PLL3CFGR1 register fields */
+#define RCC_PLL3CFGR1_DIVN_MASK GENMASK(8, 0)
+#define RCC_PLL3CFGR1_DIVM3_MASK GENMASK(21, 16)
+#define RCC_PLL3CFGR1_IFRGE_MASK GENMASK(25, 24)
+#define RCC_PLL3CFGR1_DIVN_SHIFT 0
+#define RCC_PLL3CFGR1_DIVM3_SHIFT 16
+#define RCC_PLL3CFGR1_IFRGE_SHIFT 24
+
+/* RCC_PLL3CFGR2 register fields */
+#define RCC_PLL3CFGR2_DIVP_MASK GENMASK(6, 0)
+#define RCC_PLL3CFGR2_DIVQ_MASK GENMASK(14, 8)
+#define RCC_PLL3CFGR2_DIVR_MASK GENMASK(22, 16)
+#define RCC_PLL3CFGR2_DIVP_SHIFT 0
+#define RCC_PLL3CFGR2_DIVQ_SHIFT 8
+#define RCC_PLL3CFGR2_DIVR_SHIFT 16
+
+/* RCC_PLL3FRACR register fields */
+#define RCC_PLL3FRACR_FRACV_MASK GENMASK(15, 3)
+#define RCC_PLL3FRACR_FRACLE BIT(16)
+#define RCC_PLL3FRACR_FRACV_SHIFT 3
+
+/* RCC_PLL3CSGR register fields */
+#define RCC_PLL3CSGR_MOD_PER_MASK GENMASK(12, 0)
+#define RCC_PLL3CSGR_TPDFN_DIS BIT(13)
+#define RCC_PLL3CSGR_RPDFN_DIS BIT(14)
+#define RCC_PLL3CSGR_SSCG_MODE BIT(15)
+#define RCC_PLL3CSGR_INC_STEP_MASK GENMASK(30, 16)
+#define RCC_PLL3CSGR_MOD_PER_SHIFT 0
+#define RCC_PLL3CSGR_INC_STEP_SHIFT 16
+
+/* RCC_PLL4CR register fields */
+#define RCC_PLL4CR_PLLON BIT(0)
+#define RCC_PLL4CR_PLL4RDY BIT(1)
+#define RCC_PLL4CR_SSCG_CTRL BIT(2)
+#define RCC_PLL4CR_DIVPEN BIT(4)
+#define RCC_PLL4CR_DIVQEN BIT(5)
+#define RCC_PLL4CR_DIVREN BIT(6)
+
+/* RCC_PLL4CFGR1 register fields */
+#define RCC_PLL4CFGR1_DIVN_MASK GENMASK(8, 0)
+#define RCC_PLL4CFGR1_DIVM4_MASK GENMASK(21, 16)
+#define RCC_PLL4CFGR1_IFRGE_MASK GENMASK(25, 24)
+#define RCC_PLL4CFGR1_DIVN_SHIFT 0
+#define RCC_PLL4CFGR1_DIVM4_SHIFT 16
+#define RCC_PLL4CFGR1_IFRGE_SHIFT 24
+
+/* RCC_PLL4CFGR2 register fields */
+#define RCC_PLL4CFGR2_DIVP_MASK GENMASK(6, 0)
+#define RCC_PLL4CFGR2_DIVQ_MASK GENMASK(14, 8)
+#define RCC_PLL4CFGR2_DIVR_MASK GENMASK(22, 16)
+#define RCC_PLL4CFGR2_DIVP_SHIFT 0
+#define RCC_PLL4CFGR2_DIVQ_SHIFT 8
+#define RCC_PLL4CFGR2_DIVR_SHIFT 16
+
+/* RCC_PLL4FRACR register fields */
+#define RCC_PLL4FRACR_FRACV_MASK GENMASK(15, 3)
+#define RCC_PLL4FRACR_FRACLE BIT(16)
+#define RCC_PLL4FRACR_FRACV_SHIFT 3
+
+/* RCC_PLL4CSGR register fields */
+#define RCC_PLL4CSGR_MOD_PER_MASK GENMASK(12, 0)
+#define RCC_PLL4CSGR_TPDFN_DIS BIT(13)
+#define RCC_PLL4CSGR_RPDFN_DIS BIT(14)
+#define RCC_PLL4CSGR_SSCG_MODE BIT(15)
+#define RCC_PLL4CSGR_INC_STEP_MASK GENMASK(30, 16)
+#define RCC_PLL4CSGR_MOD_PER_SHIFT 0
+#define RCC_PLL4CSGR_INC_STEP_SHIFT 16
+
+/* RCC_MPCKSELR register fields */
+#define RCC_MPCKSELR_MPUSRC_MASK GENMASK(1, 0)
+#define RCC_MPCKSELR_MPUSRCRDY BIT(31)
+#define RCC_MPCKSELR_MPUSRC_SHIFT 0
+
+/* RCC_ASSCKSELR register fields */
+#define RCC_ASSCKSELR_AXISSRC_MASK GENMASK(2, 0)
+#define RCC_ASSCKSELR_AXISSRCRDY BIT(31)
+#define RCC_ASSCKSELR_AXISSRC_SHIFT 0
+
+/* RCC_MSSCKSELR register fields */
+#define RCC_MSSCKSELR_MLAHBSSRC_MASK GENMASK(1, 0)
+#define RCC_MSSCKSELR_MLAHBSSRCRDY BIT(31)
+#define RCC_MSSCKSELR_MLAHBSSRC_SHIFT 0
+
+/* RCC_CPERCKSELR register fields */
+#define RCC_CPERCKSELR_CKPERSRC_MASK GENMASK(1, 0)
+#define RCC_CPERCKSELR_CKPERSRC_SHIFT 0
+
+/* RCC_RTCDIVR register fields */
+#define RCC_RTCDIVR_RTCDIV_MASK GENMASK(5, 0)
+#define RCC_RTCDIVR_RTCDIV_SHIFT 0
+
+/* RCC_MPCKDIVR register fields */
+#define RCC_MPCKDIVR_MPUDIV_MASK GENMASK(3, 0)
+#define RCC_MPCKDIVR_MPUDIVRDY BIT(31)
+#define RCC_MPCKDIVR_MPUDIV_SHIFT 0
+
+/* RCC_AXIDIVR register fields */
+#define RCC_AXIDIVR_AXIDIV_MASK GENMASK(2, 0)
+#define RCC_AXIDIVR_AXIDIVRDY BIT(31)
+#define RCC_AXIDIVR_AXIDIV_SHIFT 0
+
+/* RCC_MLAHBDIVR register fields */
+#define RCC_MLAHBDIVR_MLAHBDIV_MASK GENMASK(3, 0)
+#define RCC_MLAHBDIVR_MLAHBDIVRDY BIT(31)
+#define RCC_MLAHBDIVR_MLAHBDIV_SHIFT 0
+
+/* RCC_APB1DIVR register fields */
+#define RCC_APB1DIVR_APB1DIV_MASK GENMASK(2, 0)
+#define RCC_APB1DIVR_APB1DIVRDY BIT(31)
+#define RCC_APB1DIVR_APB1DIV_SHIFT 0
+
+/* RCC_APB2DIVR register fields */
+#define RCC_APB2DIVR_APB2DIV_MASK GENMASK(2, 0)
+#define RCC_APB2DIVR_APB2DIVRDY BIT(31)
+#define RCC_APB2DIVR_APB2DIV_SHIFT 0
+
+/* RCC_APB3DIVR register fields */
+#define RCC_APB3DIVR_APB3DIV_MASK GENMASK(2, 0)
+#define RCC_APB3DIVR_APB3DIVRDY BIT(31)
+#define RCC_APB3DIVR_APB3DIV_SHIFT 0
+
+/* RCC_APB4DIVR register fields */
+#define RCC_APB4DIVR_APB4DIV_MASK GENMASK(2, 0)
+#define RCC_APB4DIVR_APB4DIVRDY BIT(31)
+#define RCC_APB4DIVR_APB4DIV_SHIFT 0
+
+/* RCC_APB5DIVR register fields */
+#define RCC_APB5DIVR_APB5DIV_MASK GENMASK(2, 0)
+#define RCC_APB5DIVR_APB5DIVRDY BIT(31)
+#define RCC_APB5DIVR_APB5DIV_SHIFT 0
+
+/* RCC_APB6DIVR register fields */
+#define RCC_APB6DIVR_APB6DIV_MASK GENMASK(2, 0)
+#define RCC_APB6DIVR_APB6DIVRDY BIT(31)
+#define RCC_APB6DIVR_APB6DIV_SHIFT 0
+
+/* RCC_TIMG1PRER register fields */
+#define RCC_TIMG1PRER_TIMG1PRE BIT(0)
+#define RCC_TIMG1PRER_TIMG1PRERDY BIT(31)
+
+/* RCC_TIMG2PRER register fields */
+#define RCC_TIMG2PRER_TIMG2PRE BIT(0)
+#define RCC_TIMG2PRER_TIMG2PRERDY BIT(31)
+
+/* RCC_TIMG3PRER register fields */
+#define RCC_TIMG3PRER_TIMG3PRE BIT(0)
+#define RCC_TIMG3PRER_TIMG3PRERDY BIT(31)
+
+/* RCC_DDRITFCR register fields */
+#define RCC_DDRITFCR_DDRC1EN BIT(0)
+#define RCC_DDRITFCR_DDRC1LPEN BIT(1)
+#define RCC_DDRITFCR_DDRPHYCEN BIT(4)
+#define RCC_DDRITFCR_DDRPHYCLPEN BIT(5)
+#define RCC_DDRITFCR_DDRCAPBEN BIT(6)
+#define RCC_DDRITFCR_DDRCAPBLPEN BIT(7)
+#define RCC_DDRITFCR_AXIDCGEN BIT(8)
+#define RCC_DDRITFCR_DDRPHYCAPBEN BIT(9)
+#define RCC_DDRITFCR_DDRPHYCAPBLPEN BIT(10)
+#define RCC_DDRITFCR_KERDCG_DLY_MASK GENMASK(13, 11)
+#define RCC_DDRITFCR_DDRCAPBRST BIT(14)
+#define RCC_DDRITFCR_DDRCAXIRST BIT(15)
+#define RCC_DDRITFCR_DDRCORERST BIT(16)
+#define RCC_DDRITFCR_DPHYAPBRST BIT(17)
+#define RCC_DDRITFCR_DPHYRST BIT(18)
+#define RCC_DDRITFCR_DPHYCTLRST BIT(19)
+#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20)
+#define RCC_DDRITFCR_GSKPMOD BIT(23)
+#define RCC_DDRITFCR_GSKPCTRL BIT(24)
+#define RCC_DDRITFCR_DFILP_WIDTH_MASK GENMASK(27, 25)
+#define RCC_DDRITFCR_GSKP_DUR_MASK GENMASK(31, 28)
+#define RCC_DDRITFCR_KERDCG_DLY_SHIFT 11
+#define RCC_DDRITFCR_DDRCKMOD_SHIFT 20
+#define RCC_DDRITFCR_DFILP_WIDTH_SHIFT 25
+#define RCC_DDRITFCR_GSKP_DUR_SHIFT 28
+
+/* RCC_I2C12CKSELR register fields */
+#define RCC_I2C12CKSELR_I2C12SRC_MASK GENMASK(2, 0)
+#define RCC_I2C12CKSELR_I2C12SRC_SHIFT 0
+
+/* RCC_I2C345CKSELR register fields */
+#define RCC_I2C345CKSELR_I2C3SRC_MASK GENMASK(2, 0)
+#define RCC_I2C345CKSELR_I2C4SRC_MASK GENMASK(5, 3)
+#define RCC_I2C345CKSELR_I2C5SRC_MASK GENMASK(8, 6)
+#define RCC_I2C345CKSELR_I2C3SRC_SHIFT 0
+#define RCC_I2C345CKSELR_I2C4SRC_SHIFT 3
+#define RCC_I2C345CKSELR_I2C5SRC_SHIFT 6
+
+/* RCC_SPI2S1CKSELR register fields */
+#define RCC_SPI2S1CKSELR_SPI1SRC_MASK GENMASK(2, 0)
+#define RCC_SPI2S1CKSELR_SPI1SRC_SHIFT 0
+
+/* RCC_SPI2S23CKSELR register fields */
+#define RCC_SPI2S23CKSELR_SPI23SRC_MASK GENMASK(2, 0)
+#define RCC_SPI2S23CKSELR_SPI23SRC_SHIFT 0
+
+/* RCC_SPI45CKSELR register fields */
+#define RCC_SPI45CKSELR_SPI4SRC_MASK GENMASK(2, 0)
+#define RCC_SPI45CKSELR_SPI5SRC_MASK GENMASK(5, 3)
+#define RCC_SPI45CKSELR_SPI4SRC_SHIFT 0
+#define RCC_SPI45CKSELR_SPI5SRC_SHIFT 3
+
+/* RCC_UART12CKSELR register fields */
+#define RCC_UART12CKSELR_UART1SRC_MASK GENMASK(2, 0)
+#define RCC_UART12CKSELR_UART2SRC_MASK GENMASK(5, 3)
+#define RCC_UART12CKSELR_UART1SRC_SHIFT 0
+#define RCC_UART12CKSELR_UART2SRC_SHIFT 3
+
+/* RCC_UART35CKSELR register fields */
+#define RCC_UART35CKSELR_UART35SRC_MASK GENMASK(2, 0)
+#define RCC_UART35CKSELR_UART35SRC_SHIFT 0
+
+/* RCC_UART4CKSELR register fields */
+#define RCC_UART4CKSELR_UART4SRC_MASK GENMASK(2, 0)
+#define RCC_UART4CKSELR_UART4SRC_SHIFT 0
+
+/* RCC_UART6CKSELR register fields */
+#define RCC_UART6CKSELR_UART6SRC_MASK GENMASK(2, 0)
+#define RCC_UART6CKSELR_UART6SRC_SHIFT 0
+
+/* RCC_UART78CKSELR register fields */
+#define RCC_UART78CKSELR_UART78SRC_MASK GENMASK(2, 0)
+#define RCC_UART78CKSELR_UART78SRC_SHIFT 0
+
+/* RCC_LPTIM1CKSELR register fields */
+#define RCC_LPTIM1CKSELR_LPTIM1SRC_MASK GENMASK(2, 0)
+#define RCC_LPTIM1CKSELR_LPTIM1SRC_SHIFT 0
+
+/* RCC_LPTIM23CKSELR register fields */
+#define RCC_LPTIM23CKSELR_LPTIM2SRC_MASK GENMASK(2, 0)
+#define RCC_LPTIM23CKSELR_LPTIM3SRC_MASK GENMASK(5, 3)
+#define RCC_LPTIM23CKSELR_LPTIM2SRC_SHIFT 0
+#define RCC_LPTIM23CKSELR_LPTIM3SRC_SHIFT 3
+
+/* RCC_LPTIM45CKSELR register fields */
+#define RCC_LPTIM45CKSELR_LPTIM45SRC_MASK GENMASK(2, 0)
+#define RCC_LPTIM45CKSELR_LPTIM45SRC_SHIFT 0
+
+/* RCC_SAI1CKSELR register fields */
+#define RCC_SAI1CKSELR_SAI1SRC_MASK GENMASK(2, 0)
+#define RCC_SAI1CKSELR_SAI1SRC_SHIFT 0
+
+/* RCC_SAI2CKSELR register fields */
+#define RCC_SAI2CKSELR_SAI2SRC_MASK GENMASK(2, 0)
+#define RCC_SAI2CKSELR_SAI2SRC_SHIFT 0
+
+/* RCC_FDCANCKSELR register fields */
+#define RCC_FDCANCKSELR_FDCANSRC_MASK GENMASK(1, 0)
+#define RCC_FDCANCKSELR_FDCANSRC_SHIFT 0
+
+/* RCC_SPDIFCKSELR register fields */
+#define RCC_SPDIFCKSELR_SPDIFSRC_MASK GENMASK(1, 0)
+#define RCC_SPDIFCKSELR_SPDIFSRC_SHIFT 0
+
+/* RCC_ADC12CKSELR register fields */
+#define RCC_ADC12CKSELR_ADC1SRC_MASK GENMASK(1, 0)
+#define RCC_ADC12CKSELR_ADC2SRC_MASK GENMASK(3, 2)
+#define RCC_ADC12CKSELR_ADC1SRC_SHIFT 0
+#define RCC_ADC12CKSELR_ADC2SRC_SHIFT 2
+
+/* RCC_SDMMC12CKSELR register fields */
+#define RCC_SDMMC12CKSELR_SDMMC1SRC_MASK GENMASK(2, 0)
+#define RCC_SDMMC12CKSELR_SDMMC2SRC_MASK GENMASK(5, 3)
+#define RCC_SDMMC12CKSELR_SDMMC1SRC_SHIFT 0
+#define RCC_SDMMC12CKSELR_SDMMC2SRC_SHIFT 3
+
+/* RCC_ETH12CKSELR register fields */
+#define RCC_ETH12CKSELR_ETH1SRC_MASK GENMASK(1, 0)
+#define RCC_ETH12CKSELR_ETH1PTPDIV_MASK GENMASK(7, 4)
+#define RCC_ETH12CKSELR_ETH2SRC_MASK GENMASK(9, 8)
+#define RCC_ETH12CKSELR_ETH2PTPDIV_MASK GENMASK(15, 12)
+#define RCC_ETH12CKSELR_ETH1SRC_SHIFT 0
+#define RCC_ETH12CKSELR_ETH1PTPDIV_SHIFT 4
+#define RCC_ETH12CKSELR_ETH2SRC_SHIFT 8
+#define RCC_ETH12CKSELR_ETH2PTPDIV_SHIFT 12
+
+/* RCC_USBCKSELR register fields */
+#define RCC_USBCKSELR_USBPHYSRC_MASK GENMASK(1, 0)
+#define RCC_USBCKSELR_USBOSRC BIT(4)
+#define RCC_USBCKSELR_USBPHYSRC_SHIFT 0
+
+/* RCC_QSPICKSELR register fields */
+#define RCC_QSPICKSELR_QSPISRC_MASK GENMASK(1, 0)
+#define RCC_QSPICKSELR_QSPISRC_SHIFT 0
+
+/* RCC_FMCCKSELR register fields */
+#define RCC_FMCCKSELR_FMCSRC_MASK GENMASK(1, 0)
+#define RCC_FMCCKSELR_FMCSRC_SHIFT 0
+
+/* RCC_RNG1CKSELR register fields */
+#define RCC_RNG1CKSELR_RNG1SRC_MASK GENMASK(1, 0)
+#define RCC_RNG1CKSELR_RNG1SRC_SHIFT 0
+
+/* RCC_STGENCKSELR register fields */
+#define RCC_STGENCKSELR_STGENSRC_MASK GENMASK(1, 0)
+#define RCC_STGENCKSELR_STGENSRC_SHIFT 0
+
+/* RCC_DCMIPPCKSELR register fields */
+#define RCC_DCMIPPCKSELR_DCMIPPSRC_MASK GENMASK(1, 0)
+#define RCC_DCMIPPCKSELR_DCMIPPSRC_SHIFT 0
+
+/* RCC_SAESCKSELR register fields */
+#define RCC_SAESCKSELR_SAESSRC_MASK GENMASK(1, 0)
+#define RCC_SAESCKSELR_SAESSRC_SHIFT 0
+
+/* RCC_APB1RSTSETR register fields */
+#define RCC_APB1RSTSETR_TIM2RST BIT(0)
+#define RCC_APB1RSTSETR_TIM3RST BIT(1)
+#define RCC_APB1RSTSETR_TIM4RST BIT(2)
+#define RCC_APB1RSTSETR_TIM5RST BIT(3)
+#define RCC_APB1RSTSETR_TIM6RST BIT(4)
+#define RCC_APB1RSTSETR_TIM7RST BIT(5)
+#define RCC_APB1RSTSETR_LPTIM1RST BIT(9)
+#define RCC_APB1RSTSETR_SPI2RST BIT(11)
+#define RCC_APB1RSTSETR_SPI3RST BIT(12)
+#define RCC_APB1RSTSETR_USART3RST BIT(15)
+#define RCC_APB1RSTSETR_UART4RST BIT(16)
+#define RCC_APB1RSTSETR_UART5RST BIT(17)
+#define RCC_APB1RSTSETR_UART7RST BIT(18)
+#define RCC_APB1RSTSETR_UART8RST BIT(19)
+#define RCC_APB1RSTSETR_I2C1RST BIT(21)
+#define RCC_APB1RSTSETR_I2C2RST BIT(22)
+#define RCC_APB1RSTSETR_SPDIFRST BIT(26)
+
+/* RCC_APB1RSTCLRR register fields */
+#define RCC_APB1RSTCLRR_TIM2RST BIT(0)
+#define RCC_APB1RSTCLRR_TIM3RST BIT(1)
+#define RCC_APB1RSTCLRR_TIM4RST BIT(2)
+#define RCC_APB1RSTCLRR_TIM5RST BIT(3)
+#define RCC_APB1RSTCLRR_TIM6RST BIT(4)
+#define RCC_APB1RSTCLRR_TIM7RST BIT(5)
+#define RCC_APB1RSTCLRR_LPTIM1RST BIT(9)
+#define RCC_APB1RSTCLRR_SPI2RST BIT(11)
+#define RCC_APB1RSTCLRR_SPI3RST BIT(12)
+#define RCC_APB1RSTCLRR_USART3RST BIT(15)
+#define RCC_APB1RSTCLRR_UART4RST BIT(16)
+#define RCC_APB1RSTCLRR_UART5RST BIT(17)
+#define RCC_APB1RSTCLRR_UART7RST BIT(18)
+#define RCC_APB1RSTCLRR_UART8RST BIT(19)
+#define RCC_APB1RSTCLRR_I2C1RST BIT(21)
+#define RCC_APB1RSTCLRR_I2C2RST BIT(22)
+#define RCC_APB1RSTCLRR_SPDIFRST BIT(26)
+
+/* RCC_APB2RSTSETR register fields */
+#define RCC_APB2RSTSETR_TIM1RST BIT(0)
+#define RCC_APB2RSTSETR_TIM8RST BIT(1)
+#define RCC_APB2RSTSETR_SPI1RST BIT(8)
+#define RCC_APB2RSTSETR_USART6RST BIT(13)
+#define RCC_APB2RSTSETR_SAI1RST BIT(16)
+#define RCC_APB2RSTSETR_SAI2RST BIT(17)
+#define RCC_APB2RSTSETR_DFSDMRST BIT(20)
+#define RCC_APB2RSTSETR_FDCANRST BIT(24)
+
+/* RCC_APB2RSTCLRR register fields */
+#define RCC_APB2RSTCLRR_TIM1RST BIT(0)
+#define RCC_APB2RSTCLRR_TIM8RST BIT(1)
+#define RCC_APB2RSTCLRR_SPI1RST BIT(8)
+#define RCC_APB2RSTCLRR_USART6RST BIT(13)
+#define RCC_APB2RSTCLRR_SAI1RST BIT(16)
+#define RCC_APB2RSTCLRR_SAI2RST BIT(17)
+#define RCC_APB2RSTCLRR_DFSDMRST BIT(20)
+#define RCC_APB2RSTCLRR_FDCANRST BIT(24)
+
+/* RCC_APB3RSTSETR register fields */
+#define RCC_APB3RSTSETR_LPTIM2RST BIT(0)
+#define RCC_APB3RSTSETR_LPTIM3RST BIT(1)
+#define RCC_APB3RSTSETR_LPTIM4RST BIT(2)
+#define RCC_APB3RSTSETR_LPTIM5RST BIT(3)
+#define RCC_APB3RSTSETR_SYSCFGRST BIT(11)
+#define RCC_APB3RSTSETR_VREFRST BIT(13)
+#define RCC_APB3RSTSETR_DTSRST BIT(16)
+#define RCC_APB3RSTSETR_PMBCTRLRST BIT(17)
+
+/* RCC_APB3RSTCLRR register fields */
+#define RCC_APB3RSTCLRR_LPTIM2RST BIT(0)
+#define RCC_APB3RSTCLRR_LPTIM3RST BIT(1)
+#define RCC_APB3RSTCLRR_LPTIM4RST BIT(2)
+#define RCC_APB3RSTCLRR_LPTIM5RST BIT(3)
+#define RCC_APB3RSTCLRR_SYSCFGRST BIT(11)
+#define RCC_APB3RSTCLRR_VREFRST BIT(13)
+#define RCC_APB3RSTCLRR_DTSRST BIT(16)
+#define RCC_APB3RSTCLRR_PMBCTRLRST BIT(17)
+
+/* RCC_APB4RSTSETR register fields */
+#define RCC_APB4RSTSETR_LTDCRST BIT(0)
+#define RCC_APB4RSTSETR_DCMIPPRST BIT(1)
+#define RCC_APB4RSTSETR_DDRPERFMRST BIT(8)
+#define RCC_APB4RSTSETR_USBPHYRST BIT(16)
+
+/* RCC_APB4RSTCLRR register fields */
+#define RCC_APB4RSTCLRR_LTDCRST BIT(0)
+#define RCC_APB4RSTCLRR_DCMIPPRST BIT(1)
+#define RCC_APB4RSTCLRR_DDRPERFMRST BIT(8)
+#define RCC_APB4RSTCLRR_USBPHYRST BIT(16)
+
+/* RCC_APB5RSTSETR register fields */
+#define RCC_APB5RSTSETR_STGENRST BIT(20)
+
+/* RCC_APB5RSTCLRR register fields */
+#define RCC_APB5RSTCLRR_STGENRST BIT(20)
+
+/* RCC_APB6RSTSETR register fields */
+#define RCC_APB6RSTSETR_USART1RST BIT(0)
+#define RCC_APB6RSTSETR_USART2RST BIT(1)
+#define RCC_APB6RSTSETR_SPI4RST BIT(2)
+#define RCC_APB6RSTSETR_SPI5RST BIT(3)
+#define RCC_APB6RSTSETR_I2C3RST BIT(4)
+#define RCC_APB6RSTSETR_I2C4RST BIT(5)
+#define RCC_APB6RSTSETR_I2C5RST BIT(6)
+#define RCC_APB6RSTSETR_TIM12RST BIT(7)
+#define RCC_APB6RSTSETR_TIM13RST BIT(8)
+#define RCC_APB6RSTSETR_TIM14RST BIT(9)
+#define RCC_APB6RSTSETR_TIM15RST BIT(10)
+#define RCC_APB6RSTSETR_TIM16RST BIT(11)
+#define RCC_APB6RSTSETR_TIM17RST BIT(12)
+
+/* RCC_APB6RSTCLRR register fields */
+#define RCC_APB6RSTCLRR_USART1RST BIT(0)
+#define RCC_APB6RSTCLRR_USART2RST BIT(1)
+#define RCC_APB6RSTCLRR_SPI4RST BIT(2)
+#define RCC_APB6RSTCLRR_SPI5RST BIT(3)
+#define RCC_APB6RSTCLRR_I2C3RST BIT(4)
+#define RCC_APB6RSTCLRR_I2C4RST BIT(5)
+#define RCC_APB6RSTCLRR_I2C5RST BIT(6)
+#define RCC_APB6RSTCLRR_TIM12RST BIT(7)
+#define RCC_APB6RSTCLRR_TIM13RST BIT(8)
+#define RCC_APB6RSTCLRR_TIM14RST BIT(9)
+#define RCC_APB6RSTCLRR_TIM15RST BIT(10)
+#define RCC_APB6RSTCLRR_TIM16RST BIT(11)
+#define RCC_APB6RSTCLRR_TIM17RST BIT(12)
+
+/* RCC_AHB2RSTSETR register fields */
+#define RCC_AHB2RSTSETR_DMA1RST BIT(0)
+#define RCC_AHB2RSTSETR_DMA2RST BIT(1)
+#define RCC_AHB2RSTSETR_DMAMUX1RST BIT(2)
+#define RCC_AHB2RSTSETR_DMA3RST BIT(3)
+#define RCC_AHB2RSTSETR_DMAMUX2RST BIT(4)
+#define RCC_AHB2RSTSETR_ADC1RST BIT(5)
+#define RCC_AHB2RSTSETR_ADC2RST BIT(6)
+#define RCC_AHB2RSTSETR_USBORST BIT(8)
+
+/* RCC_AHB2RSTCLRR register fields */
+#define RCC_AHB2RSTCLRR_DMA1RST BIT(0)
+#define RCC_AHB2RSTCLRR_DMA2RST BIT(1)
+#define RCC_AHB2RSTCLRR_DMAMUX1RST BIT(2)
+#define RCC_AHB2RSTCLRR_DMA3RST BIT(3)
+#define RCC_AHB2RSTCLRR_DMAMUX2RST BIT(4)
+#define RCC_AHB2RSTCLRR_ADC1RST BIT(5)
+#define RCC_AHB2RSTCLRR_ADC2RST BIT(6)
+#define RCC_AHB2RSTCLRR_USBORST BIT(8)
+
+/* RCC_AHB4RSTSETR register fields */
+#define RCC_AHB4RSTSETR_GPIOARST BIT(0)
+#define RCC_AHB4RSTSETR_GPIOBRST BIT(1)
+#define RCC_AHB4RSTSETR_GPIOCRST BIT(2)
+#define RCC_AHB4RSTSETR_GPIODRST BIT(3)
+#define RCC_AHB4RSTSETR_GPIOERST BIT(4)
+#define RCC_AHB4RSTSETR_GPIOFRST BIT(5)
+#define RCC_AHB4RSTSETR_GPIOGRST BIT(6)
+#define RCC_AHB4RSTSETR_GPIOHRST BIT(7)
+#define RCC_AHB4RSTSETR_GPIOIRST BIT(8)
+#define RCC_AHB4RSTSETR_TSCRST BIT(15)
+
+/* RCC_AHB4RSTCLRR register fields */
+#define RCC_AHB4RSTCLRR_GPIOARST BIT(0)
+#define RCC_AHB4RSTCLRR_GPIOBRST BIT(1)
+#define RCC_AHB4RSTCLRR_GPIOCRST BIT(2)
+#define RCC_AHB4RSTCLRR_GPIODRST BIT(3)
+#define RCC_AHB4RSTCLRR_GPIOERST BIT(4)
+#define RCC_AHB4RSTCLRR_GPIOFRST BIT(5)
+#define RCC_AHB4RSTCLRR_GPIOGRST BIT(6)
+#define RCC_AHB4RSTCLRR_GPIOHRST BIT(7)
+#define RCC_AHB4RSTCLRR_GPIOIRST BIT(8)
+#define RCC_AHB4RSTCLRR_TSCRST BIT(15)
+
+/* RCC_AHB5RSTSETR register fields */
+#define RCC_AHB5RSTSETR_PKARST BIT(2)
+#define RCC_AHB5RSTSETR_SAESRST BIT(3)
+#define RCC_AHB5RSTSETR_CRYP1RST BIT(4)
+#define RCC_AHB5RSTSETR_HASH1RST BIT(5)
+#define RCC_AHB5RSTSETR_RNG1RST BIT(6)
+#define RCC_AHB5RSTSETR_AXIMCRST BIT(16)
+
+/* RCC_AHB5RSTCLRR register fields */
+#define RCC_AHB5RSTCLRR_PKARST BIT(2)
+#define RCC_AHB5RSTCLRR_SAESRST BIT(3)
+#define RCC_AHB5RSTCLRR_CRYP1RST BIT(4)
+#define RCC_AHB5RSTCLRR_HASH1RST BIT(5)
+#define RCC_AHB5RSTCLRR_RNG1RST BIT(6)
+#define RCC_AHB5RSTCLRR_AXIMCRST BIT(16)
+
+/* RCC_AHB6RSTSETR register fields */
+#define RCC_AHB6RSTSETR_MDMARST BIT(0)
+#define RCC_AHB6RSTSETR_MCERST BIT(1)
+#define RCC_AHB6RSTSETR_ETH1MACRST BIT(10)
+#define RCC_AHB6RSTSETR_FMCRST BIT(12)
+#define RCC_AHB6RSTSETR_QSPIRST BIT(14)
+#define RCC_AHB6RSTSETR_SDMMC1RST BIT(16)
+#define RCC_AHB6RSTSETR_SDMMC2RST BIT(17)
+#define RCC_AHB6RSTSETR_CRC1RST BIT(20)
+#define RCC_AHB6RSTSETR_USBHRST BIT(24)
+#define RCC_AHB6RSTSETR_ETH2MACRST BIT(30)
+
+/* RCC_AHB6RSTCLRR register fields */
+#define RCC_AHB6RSTCLRR_MDMARST BIT(0)
+#define RCC_AHB6RSTCLRR_MCERST BIT(1)
+#define RCC_AHB6RSTCLRR_ETH1MACRST BIT(10)
+#define RCC_AHB6RSTCLRR_FMCRST BIT(12)
+#define RCC_AHB6RSTCLRR_QSPIRST BIT(14)
+#define RCC_AHB6RSTCLRR_SDMMC1RST BIT(16)
+#define RCC_AHB6RSTCLRR_SDMMC2RST BIT(17)
+#define RCC_AHB6RSTCLRR_CRC1RST BIT(20)
+#define RCC_AHB6RSTCLRR_USBHRST BIT(24)
+#define RCC_AHB6RSTCLRR_ETH2MACRST BIT(30)
+
+/* RCC_MP_APB1ENSETR register fields */
+#define RCC_MP_APB1ENSETR_TIM2EN BIT(0)
+#define RCC_MP_APB1ENSETR_TIM3EN BIT(1)
+#define RCC_MP_APB1ENSETR_TIM4EN BIT(2)
+#define RCC_MP_APB1ENSETR_TIM5EN BIT(3)
+#define RCC_MP_APB1ENSETR_TIM6EN BIT(4)
+#define RCC_MP_APB1ENSETR_TIM7EN BIT(5)
+#define RCC_MP_APB1ENSETR_LPTIM1EN BIT(9)
+#define RCC_MP_APB1ENSETR_SPI2EN BIT(11)
+#define RCC_MP_APB1ENSETR_SPI3EN BIT(12)
+#define RCC_MP_APB1ENSETR_USART3EN BIT(15)
+#define RCC_MP_APB1ENSETR_UART4EN BIT(16)
+#define RCC_MP_APB1ENSETR_UART5EN BIT(17)
+#define RCC_MP_APB1ENSETR_UART7EN BIT(18)
+#define RCC_MP_APB1ENSETR_UART8EN BIT(19)
+#define RCC_MP_APB1ENSETR_I2C1EN BIT(21)
+#define RCC_MP_APB1ENSETR_I2C2EN BIT(22)
+#define RCC_MP_APB1ENSETR_SPDIFEN BIT(26)
+
+/* RCC_MP_APB1ENCLRR register fields */
+#define RCC_MP_APB1ENCLRR_TIM2EN BIT(0)
+#define RCC_MP_APB1ENCLRR_TIM3EN BIT(1)
+#define RCC_MP_APB1ENCLRR_TIM4EN BIT(2)
+#define RCC_MP_APB1ENCLRR_TIM5EN BIT(3)
+#define RCC_MP_APB1ENCLRR_TIM6EN BIT(4)
+#define RCC_MP_APB1ENCLRR_TIM7EN BIT(5)
+#define RCC_MP_APB1ENCLRR_LPTIM1EN BIT(9)
+#define RCC_MP_APB1ENCLRR_SPI2EN BIT(11)
+#define RCC_MP_APB1ENCLRR_SPI3EN BIT(12)
+#define RCC_MP_APB1ENCLRR_USART3EN BIT(15)
+#define RCC_MP_APB1ENCLRR_UART4EN BIT(16)
+#define RCC_MP_APB1ENCLRR_UART5EN BIT(17)
+#define RCC_MP_APB1ENCLRR_UART7EN BIT(18)
+#define RCC_MP_APB1ENCLRR_UART8EN BIT(19)
+#define RCC_MP_APB1ENCLRR_I2C1EN BIT(21)
+#define RCC_MP_APB1ENCLRR_I2C2EN BIT(22)
+#define RCC_MP_APB1ENCLRR_SPDIFEN BIT(26)
+
+/* RCC_MP_APB2ENSETR register fields */
+#define RCC_MP_APB2ENSETR_TIM1EN BIT(0)
+#define RCC_MP_APB2ENSETR_TIM8EN BIT(1)
+#define RCC_MP_APB2ENSETR_SPI1EN BIT(8)
+#define RCC_MP_APB2ENSETR_USART6EN BIT(13)
+#define RCC_MP_APB2ENSETR_SAI1EN BIT(16)
+#define RCC_MP_APB2ENSETR_SAI2EN BIT(17)
+#define RCC_MP_APB2ENSETR_DFSDMEN BIT(20)
+#define RCC_MP_APB2ENSETR_ADFSDMEN BIT(21)
+#define RCC_MP_APB2ENSETR_FDCANEN BIT(24)
+
+/* RCC_MP_APB2ENCLRR register fields */
+#define RCC_MP_APB2ENCLRR_TIM1EN BIT(0)
+#define RCC_MP_APB2ENCLRR_TIM8EN BIT(1)
+#define RCC_MP_APB2ENCLRR_SPI1EN BIT(8)
+#define RCC_MP_APB2ENCLRR_USART6EN BIT(13)
+#define RCC_MP_APB2ENCLRR_SAI1EN BIT(16)
+#define RCC_MP_APB2ENCLRR_SAI2EN BIT(17)
+#define RCC_MP_APB2ENCLRR_DFSDMEN BIT(20)
+#define RCC_MP_APB2ENCLRR_ADFSDMEN BIT(21)
+#define RCC_MP_APB2ENCLRR_FDCANEN BIT(24)
+
+/* RCC_MP_APB3ENSETR register fields */
+#define RCC_MP_APB3ENSETR_LPTIM2EN BIT(0)
+#define RCC_MP_APB3ENSETR_LPTIM3EN BIT(1)
+#define RCC_MP_APB3ENSETR_LPTIM4EN BIT(2)
+#define RCC_MP_APB3ENSETR_LPTIM5EN BIT(3)
+#define RCC_MP_APB3ENSETR_VREFEN BIT(13)
+#define RCC_MP_APB3ENSETR_DTSEN BIT(16)
+#define RCC_MP_APB3ENSETR_PMBCTRLEN BIT(17)
+#define RCC_MP_APB3ENSETR_HDPEN BIT(20)
+
+/* RCC_MP_APB3ENCLRR register fields */
+#define RCC_MP_APB3ENCLRR_LPTIM2EN BIT(0)
+#define RCC_MP_APB3ENCLRR_LPTIM3EN BIT(1)
+#define RCC_MP_APB3ENCLRR_LPTIM4EN BIT(2)
+#define RCC_MP_APB3ENCLRR_LPTIM5EN BIT(3)
+#define RCC_MP_APB3ENCLRR_VREFEN BIT(13)
+#define RCC_MP_APB3ENCLRR_DTSEN BIT(16)
+#define RCC_MP_APB3ENCLRR_PMBCTRLEN BIT(17)
+#define RCC_MP_APB3ENCLRR_HDPEN BIT(20)
+
+/* RCC_MP_S_APB3ENSETR register fields */
+#define RCC_MP_S_APB3ENSETR_SYSCFGEN BIT(0)
+
+/* RCC_MP_S_APB3ENCLRR register fields */
+#define RCC_MP_S_APB3ENCLRR_SYSCFGEN BIT(0)
+
+/* RCC_MP_NS_APB3ENSETR register fields */
+#define RCC_MP_NS_APB3ENSETR_SYSCFGEN BIT(0)
+
+/* RCC_MP_NS_APB3ENCLRR register fields */
+#define RCC_MP_NS_APB3ENCLRR_SYSCFGEN BIT(0)
+
+/* RCC_MP_APB4ENSETR register fields */
+#define RCC_MP_APB4ENSETR_DCMIPPEN BIT(1)
+#define RCC_MP_APB4ENSETR_DDRPERFMEN BIT(8)
+#define RCC_MP_APB4ENSETR_IWDG2APBEN BIT(15)
+#define RCC_MP_APB4ENSETR_USBPHYEN BIT(16)
+#define RCC_MP_APB4ENSETR_STGENROEN BIT(20)
+
+/* RCC_MP_APB4ENCLRR register fields */
+#define RCC_MP_APB4ENCLRR_DCMIPPEN BIT(1)
+#define RCC_MP_APB4ENCLRR_DDRPERFMEN BIT(8)
+#define RCC_MP_APB4ENCLRR_IWDG2APBEN BIT(15)
+#define RCC_MP_APB4ENCLRR_USBPHYEN BIT(16)
+#define RCC_MP_APB4ENCLRR_STGENROEN BIT(20)
+
+/* RCC_MP_S_APB4ENSETR register fields */
+#define RCC_MP_S_APB4ENSETR_LTDCEN BIT(0)
+
+/* RCC_MP_S_APB4ENCLRR register fields */
+#define RCC_MP_S_APB4ENCLRR_LTDCEN BIT(0)
+
+/* RCC_MP_NS_APB4ENSETR register fields */
+#define RCC_MP_NS_APB4ENSETR_LTDCEN BIT(0)
+
+/* RCC_MP_NS_APB4ENCLRR register fields */
+#define RCC_MP_NS_APB4ENCLRR_LTDCEN BIT(0)
+
+/* RCC_MP_APB5ENSETR register fields */
+#define RCC_MP_APB5ENSETR_RTCAPBEN BIT(8)
+#define RCC_MP_APB5ENSETR_TZCEN BIT(11)
+#define RCC_MP_APB5ENSETR_ETZPCEN BIT(13)
+#define RCC_MP_APB5ENSETR_IWDG1APBEN BIT(15)
+#define RCC_MP_APB5ENSETR_BSECEN BIT(16)
+#define RCC_MP_APB5ENSETR_STGENCEN BIT(20)
+
+/* RCC_MP_APB5ENCLRR register fields */
+#define RCC_MP_APB5ENCLRR_RTCAPBEN BIT(8)
+#define RCC_MP_APB5ENCLRR_TZCEN BIT(11)
+#define RCC_MP_APB5ENCLRR_ETZPCEN BIT(13)
+#define RCC_MP_APB5ENCLRR_IWDG1APBEN BIT(15)
+#define RCC_MP_APB5ENCLRR_BSECEN BIT(16)
+#define RCC_MP_APB5ENCLRR_STGENCEN BIT(20)
+
+/* RCC_MP_APB6ENSETR register fields */
+#define RCC_MP_APB6ENSETR_USART1EN BIT(0)
+#define RCC_MP_APB6ENSETR_USART2EN BIT(1)
+#define RCC_MP_APB6ENSETR_SPI4EN BIT(2)
+#define RCC_MP_APB6ENSETR_SPI5EN BIT(3)
+#define RCC_MP_APB6ENSETR_I2C3EN BIT(4)
+#define RCC_MP_APB6ENSETR_I2C4EN BIT(5)
+#define RCC_MP_APB6ENSETR_I2C5EN BIT(6)
+#define RCC_MP_APB6ENSETR_TIM12EN BIT(7)
+#define RCC_MP_APB6ENSETR_TIM13EN BIT(8)
+#define RCC_MP_APB6ENSETR_TIM14EN BIT(9)
+#define RCC_MP_APB6ENSETR_TIM15EN BIT(10)
+#define RCC_MP_APB6ENSETR_TIM16EN BIT(11)
+#define RCC_MP_APB6ENSETR_TIM17EN BIT(12)
+
+/* RCC_MP_APB6ENCLRR register fields */
+#define RCC_MP_APB6ENCLRR_USART1EN BIT(0)
+#define RCC_MP_APB6ENCLRR_USART2EN BIT(1)
+#define RCC_MP_APB6ENCLRR_SPI4EN BIT(2)
+#define RCC_MP_APB6ENCLRR_SPI5EN BIT(3)
+#define RCC_MP_APB6ENCLRR_I2C3EN BIT(4)
+#define RCC_MP_APB6ENCLRR_I2C4EN BIT(5)
+#define RCC_MP_APB6ENCLRR_I2C5EN BIT(6)
+#define RCC_MP_APB6ENCLRR_TIM12EN BIT(7)
+#define RCC_MP_APB6ENCLRR_TIM13EN BIT(8)
+#define RCC_MP_APB6ENCLRR_TIM14EN BIT(9)
+#define RCC_MP_APB6ENCLRR_TIM15EN BIT(10)
+#define RCC_MP_APB6ENCLRR_TIM16EN BIT(11)
+#define RCC_MP_APB6ENCLRR_TIM17EN BIT(12)
+
+/* RCC_MP_AHB2ENSETR register fields */
+#define RCC_MP_AHB2ENSETR_DMA1EN BIT(0)
+#define RCC_MP_AHB2ENSETR_DMA2EN BIT(1)
+#define RCC_MP_AHB2ENSETR_DMAMUX1EN BIT(2)
+#define RCC_MP_AHB2ENSETR_DMA3EN BIT(3)
+#define RCC_MP_AHB2ENSETR_DMAMUX2EN BIT(4)
+#define RCC_MP_AHB2ENSETR_ADC1EN BIT(5)
+#define RCC_MP_AHB2ENSETR_ADC2EN BIT(6)
+#define RCC_MP_AHB2ENSETR_USBOEN BIT(8)
+
+/* RCC_MP_AHB2ENCLRR register fields */
+#define RCC_MP_AHB2ENCLRR_DMA1EN BIT(0)
+#define RCC_MP_AHB2ENCLRR_DMA2EN BIT(1)
+#define RCC_MP_AHB2ENCLRR_DMAMUX1EN BIT(2)
+#define RCC_MP_AHB2ENCLRR_DMA3EN BIT(3)
+#define RCC_MP_AHB2ENCLRR_DMAMUX2EN BIT(4)
+#define RCC_MP_AHB2ENCLRR_ADC1EN BIT(5)
+#define RCC_MP_AHB2ENCLRR_ADC2EN BIT(6)
+#define RCC_MP_AHB2ENCLRR_USBOEN BIT(8)
+
+/* RCC_MP_AHB4ENSETR register fields */
+#define RCC_MP_AHB4ENSETR_TSCEN BIT(15)
+
+/* RCC_MP_AHB4ENCLRR register fields */
+#define RCC_MP_AHB4ENCLRR_TSCEN BIT(15)
+
+/* RCC_MP_S_AHB4ENSETR register fields */
+#define RCC_MP_S_AHB4ENSETR_GPIOAEN BIT(0)
+#define RCC_MP_S_AHB4ENSETR_GPIOBEN BIT(1)
+#define RCC_MP_S_AHB4ENSETR_GPIOCEN BIT(2)
+#define RCC_MP_S_AHB4ENSETR_GPIODEN BIT(3)
+#define RCC_MP_S_AHB4ENSETR_GPIOEEN BIT(4)
+#define RCC_MP_S_AHB4ENSETR_GPIOFEN BIT(5)
+#define RCC_MP_S_AHB4ENSETR_GPIOGEN BIT(6)
+#define RCC_MP_S_AHB4ENSETR_GPIOHEN BIT(7)
+#define RCC_MP_S_AHB4ENSETR_GPIOIEN BIT(8)
+
+/* RCC_MP_S_AHB4ENCLRR register fields */
+#define RCC_MP_S_AHB4ENCLRR_GPIOAEN BIT(0)
+#define RCC_MP_S_AHB4ENCLRR_GPIOBEN BIT(1)
+#define RCC_MP_S_AHB4ENCLRR_GPIOCEN BIT(2)
+#define RCC_MP_S_AHB4ENCLRR_GPIODEN BIT(3)
+#define RCC_MP_S_AHB4ENCLRR_GPIOEEN BIT(4)
+#define RCC_MP_S_AHB4ENCLRR_GPIOFEN BIT(5)
+#define RCC_MP_S_AHB4ENCLRR_GPIOGEN BIT(6)
+#define RCC_MP_S_AHB4ENCLRR_GPIOHEN BIT(7)
+#define RCC_MP_S_AHB4ENCLRR_GPIOIEN BIT(8)
+
+/* RCC_MP_NS_AHB4ENSETR register fields */
+#define RCC_MP_NS_AHB4ENSETR_GPIOAEN BIT(0)
+#define RCC_MP_NS_AHB4ENSETR_GPIOBEN BIT(1)
+#define RCC_MP_NS_AHB4ENSETR_GPIOCEN BIT(2)
+#define RCC_MP_NS_AHB4ENSETR_GPIODEN BIT(3)
+#define RCC_MP_NS_AHB4ENSETR_GPIOEEN BIT(4)
+#define RCC_MP_NS_AHB4ENSETR_GPIOFEN BIT(5)
+#define RCC_MP_NS_AHB4ENSETR_GPIOGEN BIT(6)
+#define RCC_MP_NS_AHB4ENSETR_GPIOHEN BIT(7)
+#define RCC_MP_NS_AHB4ENSETR_GPIOIEN BIT(8)
+
+/* RCC_MP_NS_AHB4ENCLRR register fields */
+#define RCC_MP_NS_AHB4ENCLRR_GPIOAEN BIT(0)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOBEN BIT(1)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOCEN BIT(2)
+#define RCC_MP_NS_AHB4ENCLRR_GPIODEN BIT(3)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOEEN BIT(4)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOFEN BIT(5)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOGEN BIT(6)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOHEN BIT(7)
+#define RCC_MP_NS_AHB4ENCLRR_GPIOIEN BIT(8)
+
+/* RCC_MP_AHB5ENSETR register fields */
+#define RCC_MP_AHB5ENSETR_PKAEN BIT(2)
+#define RCC_MP_AHB5ENSETR_SAESEN BIT(3)
+#define RCC_MP_AHB5ENSETR_CRYP1EN BIT(4)
+#define RCC_MP_AHB5ENSETR_HASH1EN BIT(5)
+#define RCC_MP_AHB5ENSETR_RNG1EN BIT(6)
+#define RCC_MP_AHB5ENSETR_BKPSRAMEN BIT(8)
+#define RCC_MP_AHB5ENSETR_AXIMCEN BIT(16)
+
+/* RCC_MP_AHB5ENCLRR register fields */
+#define RCC_MP_AHB5ENCLRR_PKAEN BIT(2)
+#define RCC_MP_AHB5ENCLRR_SAESEN BIT(3)
+#define RCC_MP_AHB5ENCLRR_CRYP1EN BIT(4)
+#define RCC_MP_AHB5ENCLRR_HASH1EN BIT(5)
+#define RCC_MP_AHB5ENCLRR_RNG1EN BIT(6)
+#define RCC_MP_AHB5ENCLRR_BKPSRAMEN BIT(8)
+#define RCC_MP_AHB5ENCLRR_AXIMCEN BIT(16)
+
+/* RCC_MP_AHB6ENSETR register fields */
+#define RCC_MP_AHB6ENSETR_MCEEN BIT(1)
+#define RCC_MP_AHB6ENSETR_ETH1CKEN BIT(7)
+#define RCC_MP_AHB6ENSETR_ETH1TXEN BIT(8)
+#define RCC_MP_AHB6ENSETR_ETH1RXEN BIT(9)
+#define RCC_MP_AHB6ENSETR_ETH1MACEN BIT(10)
+#define RCC_MP_AHB6ENSETR_FMCEN BIT(12)
+#define RCC_MP_AHB6ENSETR_QSPIEN BIT(14)
+#define RCC_MP_AHB6ENSETR_SDMMC1EN BIT(16)
+#define RCC_MP_AHB6ENSETR_SDMMC2EN BIT(17)
+#define RCC_MP_AHB6ENSETR_CRC1EN BIT(20)
+#define RCC_MP_AHB6ENSETR_USBHEN BIT(24)
+#define RCC_MP_AHB6ENSETR_ETH2CKEN BIT(27)
+#define RCC_MP_AHB6ENSETR_ETH2TXEN BIT(28)
+#define RCC_MP_AHB6ENSETR_ETH2RXEN BIT(29)
+#define RCC_MP_AHB6ENSETR_ETH2MACEN BIT(30)
+
+/* RCC_MP_AHB6ENCLRR register fields */
+#define RCC_MP_AHB6ENCLRR_MCEEN BIT(1)
+#define RCC_MP_AHB6ENCLRR_ETH1CKEN BIT(7)
+#define RCC_MP_AHB6ENCLRR_ETH1TXEN BIT(8)
+#define RCC_MP_AHB6ENCLRR_ETH1RXEN BIT(9)
+#define RCC_MP_AHB6ENCLRR_ETH1MACEN BIT(10)
+#define RCC_MP_AHB6ENCLRR_FMCEN BIT(12)
+#define RCC_MP_AHB6ENCLRR_QSPIEN BIT(14)
+#define RCC_MP_AHB6ENCLRR_SDMMC1EN BIT(16)
+#define RCC_MP_AHB6ENCLRR_SDMMC2EN BIT(17)
+#define RCC_MP_AHB6ENCLRR_CRC1EN BIT(20)
+#define RCC_MP_AHB6ENCLRR_USBHEN BIT(24)
+#define RCC_MP_AHB6ENCLRR_ETH2CKEN BIT(27)
+#define RCC_MP_AHB6ENCLRR_ETH2TXEN BIT(28)
+#define RCC_MP_AHB6ENCLRR_ETH2RXEN BIT(29)
+#define RCC_MP_AHB6ENCLRR_ETH2MACEN BIT(30)
+
+/* RCC_MP_S_AHB6ENSETR register fields */
+#define RCC_MP_S_AHB6ENSETR_MDMAEN BIT(0)
+
+/* RCC_MP_S_AHB6ENCLRR register fields */
+#define RCC_MP_S_AHB6ENCLRR_MDMAEN BIT(0)
+
+/* RCC_MP_NS_AHB6ENSETR register fields */
+#define RCC_MP_NS_AHB6ENSETR_MDMAEN BIT(0)
+
+/* RCC_MP_NS_AHB6ENCLRR register fields */
+#define RCC_MP_NS_AHB6ENCLRR_MDMAEN BIT(0)
+
+/* RCC_MP_APB1LPENSETR register fields */
+#define RCC_MP_APB1LPENSETR_TIM2LPEN BIT(0)
+#define RCC_MP_APB1LPENSETR_TIM3LPEN BIT(1)
+#define RCC_MP_APB1LPENSETR_TIM4LPEN BIT(2)
+#define RCC_MP_APB1LPENSETR_TIM5LPEN BIT(3)
+#define RCC_MP_APB1LPENSETR_TIM6LPEN BIT(4)
+#define RCC_MP_APB1LPENSETR_TIM7LPEN BIT(5)
+#define RCC_MP_APB1LPENSETR_LPTIM1LPEN BIT(9)
+#define RCC_MP_APB1LPENSETR_SPI2LPEN BIT(11)
+#define RCC_MP_APB1LPENSETR_SPI3LPEN BIT(12)
+#define RCC_MP_APB1LPENSETR_USART3LPEN BIT(15)
+#define RCC_MP_APB1LPENSETR_UART4LPEN BIT(16)
+#define RCC_MP_APB1LPENSETR_UART5LPEN BIT(17)
+#define RCC_MP_APB1LPENSETR_UART7LPEN BIT(18)
+#define RCC_MP_APB1LPENSETR_UART8LPEN BIT(19)
+#define RCC_MP_APB1LPENSETR_I2C1LPEN BIT(21)
+#define RCC_MP_APB1LPENSETR_I2C2LPEN BIT(22)
+#define RCC_MP_APB1LPENSETR_SPDIFLPEN BIT(26)
+
+/* RCC_MP_APB1LPENCLRR register fields */
+#define RCC_MP_APB1LPENCLRR_TIM2LPEN BIT(0)
+#define RCC_MP_APB1LPENCLRR_TIM3LPEN BIT(1)
+#define RCC_MP_APB1LPENCLRR_TIM4LPEN BIT(2)
+#define RCC_MP_APB1LPENCLRR_TIM5LPEN BIT(3)
+#define RCC_MP_APB1LPENCLRR_TIM6LPEN BIT(4)
+#define RCC_MP_APB1LPENCLRR_TIM7LPEN BIT(5)
+#define RCC_MP_APB1LPENCLRR_LPTIM1LPEN BIT(9)
+#define RCC_MP_APB1LPENCLRR_SPI2LPEN BIT(11)
+#define RCC_MP_APB1LPENCLRR_SPI3LPEN BIT(12)
+#define RCC_MP_APB1LPENCLRR_USART3LPEN BIT(15)
+#define RCC_MP_APB1LPENCLRR_UART4LPEN BIT(16)
+#define RCC_MP_APB1LPENCLRR_UART5LPEN BIT(17)
+#define RCC_MP_APB1LPENCLRR_UART7LPEN BIT(18)
+#define RCC_MP_APB1LPENCLRR_UART8LPEN BIT(19)
+#define RCC_MP_APB1LPENCLRR_I2C1LPEN BIT(21)
+#define RCC_MP_APB1LPENCLRR_I2C2LPEN BIT(22)
+#define RCC_MP_APB1LPENCLRR_SPDIFLPEN BIT(26)
+
+/* RCC_MP_APB2LPENSETR register fields */
+#define RCC_MP_APB2LPENSETR_TIM1LPEN BIT(0)
+#define RCC_MP_APB2LPENSETR_TIM8LPEN BIT(1)
+#define RCC_MP_APB2LPENSETR_SPI1LPEN BIT(8)
+#define RCC_MP_APB2LPENSETR_USART6LPEN BIT(13)
+#define RCC_MP_APB2LPENSETR_SAI1LPEN BIT(16)
+#define RCC_MP_APB2LPENSETR_SAI2LPEN BIT(17)
+#define RCC_MP_APB2LPENSETR_DFSDMLPEN BIT(20)
+#define RCC_MP_APB2LPENSETR_ADFSDMLPEN BIT(21)
+#define RCC_MP_APB2LPENSETR_FDCANLPEN BIT(24)
+
+/* RCC_MP_APB2LPENCLRR register fields */
+#define RCC_MP_APB2LPENCLRR_TIM1LPEN BIT(0)
+#define RCC_MP_APB2LPENCLRR_TIM8LPEN BIT(1)
+#define RCC_MP_APB2LPENCLRR_SPI1LPEN BIT(8)
+#define RCC_MP_APB2LPENCLRR_USART6LPEN BIT(13)
+#define RCC_MP_APB2LPENCLRR_SAI1LPEN BIT(16)
+#define RCC_MP_APB2LPENCLRR_SAI2LPEN BIT(17)
+#define RCC_MP_APB2LPENCLRR_DFSDMLPEN BIT(20)
+#define RCC_MP_APB2LPENCLRR_ADFSDMLPEN BIT(21)
+#define RCC_MP_APB2LPENCLRR_FDCANLPEN BIT(24)
+
+/* RCC_MP_APB3LPENSETR register fields */
+#define RCC_MP_APB3LPENSETR_LPTIM2LPEN BIT(0)
+#define RCC_MP_APB3LPENSETR_LPTIM3LPEN BIT(1)
+#define RCC_MP_APB3LPENSETR_LPTIM4LPEN BIT(2)
+#define RCC_MP_APB3LPENSETR_LPTIM5LPEN BIT(3)
+#define RCC_MP_APB3LPENSETR_VREFLPEN BIT(13)
+#define RCC_MP_APB3LPENSETR_DTSLPEN BIT(16)
+#define RCC_MP_APB3LPENSETR_PMBCTRLLPEN BIT(17)
+
+/* RCC_MP_APB3LPENCLRR register fields */
+#define RCC_MP_APB3LPENCLRR_LPTIM2LPEN BIT(0)
+#define RCC_MP_APB3LPENCLRR_LPTIM3LPEN BIT(1)
+#define RCC_MP_APB3LPENCLRR_LPTIM4LPEN BIT(2)
+#define RCC_MP_APB3LPENCLRR_LPTIM5LPEN BIT(3)
+#define RCC_MP_APB3LPENCLRR_VREFLPEN BIT(13)
+#define RCC_MP_APB3LPENCLRR_DTSLPEN BIT(16)
+#define RCC_MP_APB3LPENCLRR_PMBCTRLLPEN BIT(17)
+
+/* RCC_MP_S_APB3LPENSETR register fields */
+#define RCC_MP_S_APB3LPENSETR_SYSCFGLPEN BIT(0)
+
+/* RCC_MP_S_APB3LPENCLRR register fields */
+#define RCC_MP_S_APB3LPENCLRR_SYSCFGLPEN BIT(0)
+
+/* RCC_MP_NS_APB3LPENSETR register fields */
+#define RCC_MP_NS_APB3LPENSETR_SYSCFGLPEN BIT(0)
+
+/* RCC_MP_NS_APB3LPENCLRR register fields */
+#define RCC_MP_NS_APB3LPENCLRR_SYSCFGLPEN BIT(0)
+
+/* RCC_MP_APB4LPENSETR register fields */
+#define RCC_MP_APB4LPENSETR_DCMIPPLPEN BIT(1)
+#define RCC_MP_APB4LPENSETR_DDRPERFMLPEN BIT(8)
+#define RCC_MP_APB4LPENSETR_IWDG2APBLPEN BIT(15)
+#define RCC_MP_APB4LPENSETR_USBPHYLPEN BIT(16)
+#define RCC_MP_APB4LPENSETR_STGENROLPEN BIT(20)
+#define RCC_MP_APB4LPENSETR_STGENROSTPEN BIT(21)
+
+/* RCC_MP_APB4LPENCLRR register fields */
+#define RCC_MP_APB4LPENCLRR_DCMIPPLPEN BIT(1)
+#define RCC_MP_APB4LPENCLRR_DDRPERFMLPEN BIT(8)
+#define RCC_MP_APB4LPENCLRR_IWDG2APBLPEN BIT(15)
+#define RCC_MP_APB4LPENCLRR_USBPHYLPEN BIT(16)
+#define RCC_MP_APB4LPENCLRR_STGENROLPEN BIT(20)
+#define RCC_MP_APB4LPENCLRR_STGENROSTPEN BIT(21)
+
+/* RCC_MP_S_APB4LPENSETR register fields */
+#define RCC_MP_S_APB4LPENSETR_LTDCLPEN BIT(0)
+
+/* RCC_MP_S_APB4LPENCLRR register fields */
+#define RCC_MP_S_APB4LPENCLRR_LTDCLPEN BIT(0)
+
+/* RCC_MP_NS_APB4LPENSETR register fields */
+#define RCC_MP_NS_APB4LPENSETR_LTDCLPEN BIT(0)
+
+/* RCC_MP_NS_APB4LPENCLRR register fields */
+#define RCC_MP_NS_APB4LPENCLRR_LTDCLPEN BIT(0)
+
+/* RCC_MP_APB5LPENSETR register fields */
+#define RCC_MP_APB5LPENSETR_RTCAPBLPEN BIT(8)
+#define RCC_MP_APB5LPENSETR_TZCLPEN BIT(11)
+#define RCC_MP_APB5LPENSETR_ETZPCLPEN BIT(13)
+#define RCC_MP_APB5LPENSETR_IWDG1APBLPEN BIT(15)
+#define RCC_MP_APB5LPENSETR_BSECLPEN BIT(16)
+#define RCC_MP_APB5LPENSETR_STGENCLPEN BIT(20)
+#define RCC_MP_APB5LPENSETR_STGENCSTPEN BIT(21)
+
+/* RCC_MP_APB5LPENCLRR register fields */
+#define RCC_MP_APB5LPENCLRR_RTCAPBLPEN BIT(8)
+#define RCC_MP_APB5LPENCLRR_TZCLPEN BIT(11)
+#define RCC_MP_APB5LPENCLRR_ETZPCLPEN BIT(13)
+#define RCC_MP_APB5LPENCLRR_IWDG1APBLPEN BIT(15)
+#define RCC_MP_APB5LPENCLRR_BSECLPEN BIT(16)
+#define RCC_MP_APB5LPENCLRR_STGENCLPEN BIT(20)
+#define RCC_MP_APB5LPENCLRR_STGENCSTPEN BIT(21)
+
+/* RCC_MP_APB6LPENSETR register fields */
+#define RCC_MP_APB6LPENSETR_USART1LPEN BIT(0)
+#define RCC_MP_APB6LPENSETR_USART2LPEN BIT(1)
+#define RCC_MP_APB6LPENSETR_SPI4LPEN BIT(2)
+#define RCC_MP_APB6LPENSETR_SPI5LPEN BIT(3)
+#define RCC_MP_APB6LPENSETR_I2C3LPEN BIT(4)
+#define RCC_MP_APB6LPENSETR_I2C4LPEN BIT(5)
+#define RCC_MP_APB6LPENSETR_I2C5LPEN BIT(6)
+#define RCC_MP_APB6LPENSETR_TIM12LPEN BIT(7)
+#define RCC_MP_APB6LPENSETR_TIM13LPEN BIT(8)
+#define RCC_MP_APB6LPENSETR_TIM14LPEN BIT(9)
+#define RCC_MP_APB6LPENSETR_TIM15LPEN BIT(10)
+#define RCC_MP_APB6LPENSETR_TIM16LPEN BIT(11)
+#define RCC_MP_APB6LPENSETR_TIM17LPEN BIT(12)
+
+/* RCC_MP_APB6LPENCLRR register fields */
+#define RCC_MP_APB6LPENCLRR_USART1LPEN BIT(0)
+#define RCC_MP_APB6LPENCLRR_USART2LPEN BIT(1)
+#define RCC_MP_APB6LPENCLRR_SPI4LPEN BIT(2)
+#define RCC_MP_APB6LPENCLRR_SPI5LPEN BIT(3)
+#define RCC_MP_APB6LPENCLRR_I2C3LPEN BIT(4)
+#define RCC_MP_APB6LPENCLRR_I2C4LPEN BIT(5)
+#define RCC_MP_APB6LPENCLRR_I2C5LPEN BIT(6)
+#define RCC_MP_APB6LPENCLRR_TIM12LPEN BIT(7)
+#define RCC_MP_APB6LPENCLRR_TIM13LPEN BIT(8)
+#define RCC_MP_APB6LPENCLRR_TIM14LPEN BIT(9)
+#define RCC_MP_APB6LPENCLRR_TIM15LPEN BIT(10)
+#define RCC_MP_APB6LPENCLRR_TIM16LPEN BIT(11)
+#define RCC_MP_APB6LPENCLRR_TIM17LPEN BIT(12)
+
+/* RCC_MP_AHB2LPENSETR register fields */
+#define RCC_MP_AHB2LPENSETR_DMA1LPEN BIT(0)
+#define RCC_MP_AHB2LPENSETR_DMA2LPEN BIT(1)
+#define RCC_MP_AHB2LPENSETR_DMAMUX1LPEN BIT(2)
+#define RCC_MP_AHB2LPENSETR_DMA3LPEN BIT(3)
+#define RCC_MP_AHB2LPENSETR_DMAMUX2LPEN BIT(4)
+#define RCC_MP_AHB2LPENSETR_ADC1LPEN BIT(5)
+#define RCC_MP_AHB2LPENSETR_ADC2LPEN BIT(6)
+#define RCC_MP_AHB2LPENSETR_USBOLPEN BIT(8)
+
+/* RCC_MP_AHB2LPENCLRR register fields */
+#define RCC_MP_AHB2LPENCLRR_DMA1LPEN BIT(0)
+#define RCC_MP_AHB2LPENCLRR_DMA2LPEN BIT(1)
+#define RCC_MP_AHB2LPENCLRR_DMAMUX1LPEN BIT(2)
+#define RCC_MP_AHB2LPENCLRR_DMA3LPEN BIT(3)
+#define RCC_MP_AHB2LPENCLRR_DMAMUX2LPEN BIT(4)
+#define RCC_MP_AHB2LPENCLRR_ADC1LPEN BIT(5)
+#define RCC_MP_AHB2LPENCLRR_ADC2LPEN BIT(6)
+#define RCC_MP_AHB2LPENCLRR_USBOLPEN BIT(8)
+
+/* RCC_MP_AHB4LPENSETR register fields */
+#define RCC_MP_AHB4LPENSETR_TSCLPEN BIT(15)
+
+/* RCC_MP_AHB4LPENCLRR register fields */
+#define RCC_MP_AHB4LPENCLRR_TSCLPEN BIT(15)
+
+/* RCC_MP_S_AHB4LPENSETR register fields */
+#define RCC_MP_S_AHB4LPENSETR_GPIOALPEN BIT(0)
+#define RCC_MP_S_AHB4LPENSETR_GPIOBLPEN BIT(1)
+#define RCC_MP_S_AHB4LPENSETR_GPIOCLPEN BIT(2)
+#define RCC_MP_S_AHB4LPENSETR_GPIODLPEN BIT(3)
+#define RCC_MP_S_AHB4LPENSETR_GPIOELPEN BIT(4)
+#define RCC_MP_S_AHB4LPENSETR_GPIOFLPEN BIT(5)
+#define RCC_MP_S_AHB4LPENSETR_GPIOGLPEN BIT(6)
+#define RCC_MP_S_AHB4LPENSETR_GPIOHLPEN BIT(7)
+#define RCC_MP_S_AHB4LPENSETR_GPIOILPEN BIT(8)
+
+/* RCC_MP_S_AHB4LPENCLRR register fields */
+#define RCC_MP_S_AHB4LPENCLRR_GPIOALPEN BIT(0)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOBLPEN BIT(1)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOCLPEN BIT(2)
+#define RCC_MP_S_AHB4LPENCLRR_GPIODLPEN BIT(3)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOELPEN BIT(4)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOFLPEN BIT(5)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOGLPEN BIT(6)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOHLPEN BIT(7)
+#define RCC_MP_S_AHB4LPENCLRR_GPIOILPEN BIT(8)
+
+/* RCC_MP_NS_AHB4LPENSETR register fields */
+#define RCC_MP_NS_AHB4LPENSETR_GPIOALPEN BIT(0)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOBLPEN BIT(1)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOCLPEN BIT(2)
+#define RCC_MP_NS_AHB4LPENSETR_GPIODLPEN BIT(3)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOELPEN BIT(4)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOFLPEN BIT(5)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOGLPEN BIT(6)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOHLPEN BIT(7)
+#define RCC_MP_NS_AHB4LPENSETR_GPIOILPEN BIT(8)
+
+/* RCC_MP_NS_AHB4LPENCLRR register fields */
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOALPEN BIT(0)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOBLPEN BIT(1)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOCLPEN BIT(2)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIODLPEN BIT(3)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOELPEN BIT(4)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOFLPEN BIT(5)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOGLPEN BIT(6)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOHLPEN BIT(7)
+#define RCC_MP_NS_AHB4LPENCLRR_GPIOILPEN BIT(8)
+
+/* RCC_MP_AHB5LPENSETR register fields */
+#define RCC_MP_AHB5LPENSETR_PKALPEN BIT(2)
+#define RCC_MP_AHB5LPENSETR_SAESLPEN BIT(3)
+#define RCC_MP_AHB5LPENSETR_CRYP1LPEN BIT(4)
+#define RCC_MP_AHB5LPENSETR_HASH1LPEN BIT(5)
+#define RCC_MP_AHB5LPENSETR_RNG1LPEN BIT(6)
+#define RCC_MP_AHB5LPENSETR_BKPSRAMLPEN BIT(8)
+
+/* RCC_MP_AHB5LPENCLRR register fields */
+#define RCC_MP_AHB5LPENCLRR_PKALPEN BIT(2)
+#define RCC_MP_AHB5LPENCLRR_SAESLPEN BIT(3)
+#define RCC_MP_AHB5LPENCLRR_CRYP1LPEN BIT(4)
+#define RCC_MP_AHB5LPENCLRR_HASH1LPEN BIT(5)
+#define RCC_MP_AHB5LPENCLRR_RNG1LPEN BIT(6)
+#define RCC_MP_AHB5LPENCLRR_BKPSRAMLPEN BIT(8)
+
+/* RCC_MP_AHB6LPENSETR register fields */
+#define RCC_MP_AHB6LPENSETR_MCELPEN BIT(1)
+#define RCC_MP_AHB6LPENSETR_ETH1CKLPEN BIT(7)
+#define RCC_MP_AHB6LPENSETR_ETH1TXLPEN BIT(8)
+#define RCC_MP_AHB6LPENSETR_ETH1RXLPEN BIT(9)
+#define RCC_MP_AHB6LPENSETR_ETH1MACLPEN BIT(10)
+#define RCC_MP_AHB6LPENSETR_ETH1STPEN BIT(11)
+#define RCC_MP_AHB6LPENSETR_FMCLPEN BIT(12)
+#define RCC_MP_AHB6LPENSETR_QSPILPEN BIT(14)
+#define RCC_MP_AHB6LPENSETR_SDMMC1LPEN BIT(16)
+#define RCC_MP_AHB6LPENSETR_SDMMC2LPEN BIT(17)
+#define RCC_MP_AHB6LPENSETR_CRC1LPEN BIT(20)
+#define RCC_MP_AHB6LPENSETR_USBHLPEN BIT(24)
+#define RCC_MP_AHB6LPENSETR_ETH2CKLPEN BIT(27)
+#define RCC_MP_AHB6LPENSETR_ETH2TXLPEN BIT(28)
+#define RCC_MP_AHB6LPENSETR_ETH2RXLPEN BIT(29)
+#define RCC_MP_AHB6LPENSETR_ETH2MACLPEN BIT(30)
+#define RCC_MP_AHB6LPENSETR_ETH2STPEN BIT(31)
+
+/* RCC_MP_AHB6LPENCLRR register fields */
+#define RCC_MP_AHB6LPENCLRR_MCELPEN BIT(1)
+#define RCC_MP_AHB6LPENCLRR_ETH1CKLPEN BIT(7)
+#define RCC_MP_AHB6LPENCLRR_ETH1TXLPEN BIT(8)
+#define RCC_MP_AHB6LPENCLRR_ETH1RXLPEN BIT(9)
+#define RCC_MP_AHB6LPENCLRR_ETH1MACLPEN BIT(10)
+#define RCC_MP_AHB6LPENCLRR_ETH1STPEN BIT(11)
+#define RCC_MP_AHB6LPENCLRR_FMCLPEN BIT(12)
+#define RCC_MP_AHB6LPENCLRR_QSPILPEN BIT(14)
+#define RCC_MP_AHB6LPENCLRR_SDMMC1LPEN BIT(16)
+#define RCC_MP_AHB6LPENCLRR_SDMMC2LPEN BIT(17)
+#define RCC_MP_AHB6LPENCLRR_CRC1LPEN BIT(20)
+#define RCC_MP_AHB6LPENCLRR_USBHLPEN BIT(24)
+#define RCC_MP_AHB6LPENCLRR_ETH2CKLPEN BIT(27)
+#define RCC_MP_AHB6LPENCLRR_ETH2TXLPEN BIT(28)
+#define RCC_MP_AHB6LPENCLRR_ETH2RXLPEN BIT(29)
+#define RCC_MP_AHB6LPENCLRR_ETH2MACLPEN BIT(30)
+#define RCC_MP_AHB6LPENCLRR_ETH2STPEN BIT(31)
+
+/* RCC_MP_S_AHB6LPENSETR register fields */
+#define RCC_MP_S_AHB6LPENSETR_MDMALPEN BIT(0)
+
+/* RCC_MP_S_AHB6LPENCLRR register fields */
+#define RCC_MP_S_AHB6LPENCLRR_MDMALPEN BIT(0)
+
+/* RCC_MP_NS_AHB6LPENSETR register fields */
+#define RCC_MP_NS_AHB6LPENSETR_MDMALPEN BIT(0)
+
+/* RCC_MP_NS_AHB6LPENCLRR register fields */
+#define RCC_MP_NS_AHB6LPENCLRR_MDMALPEN BIT(0)
+
+/* RCC_MP_S_AXIMLPENSETR register fields */
+#define RCC_MP_S_AXIMLPENSETR_SYSRAMLPEN BIT(0)
+
+/* RCC_MP_S_AXIMLPENCLRR register fields */
+#define RCC_MP_S_AXIMLPENCLRR_SYSRAMLPEN BIT(0)
+
+/* RCC_MP_NS_AXIMLPENSETR register fields */
+#define RCC_MP_NS_AXIMLPENSETR_SYSRAMLPEN BIT(0)
+
+/* RCC_MP_NS_AXIMLPENCLRR register fields */
+#define RCC_MP_NS_AXIMLPENCLRR_SYSRAMLPEN BIT(0)
+
+/* RCC_MP_MLAHBLPENSETR register fields */
+#define RCC_MP_MLAHBLPENSETR_SRAM1LPEN BIT(0)
+#define RCC_MP_MLAHBLPENSETR_SRAM2LPEN BIT(1)
+#define RCC_MP_MLAHBLPENSETR_SRAM3LPEN BIT(2)
+
+/* RCC_MP_MLAHBLPENCLRR register fields */
+#define RCC_MP_MLAHBLPENCLRR_SRAM1LPEN BIT(0)
+#define RCC_MP_MLAHBLPENCLRR_SRAM2LPEN BIT(1)
+#define RCC_MP_MLAHBLPENCLRR_SRAM3LPEN BIT(2)
+
+/* RCC_APB3SECSR register fields */
+#define RCC_APB3SECSR_LPTIM2SECF 0
+#define RCC_APB3SECSR_LPTIM3SECF 1
+#define RCC_APB3SECSR_VREFSECF 13
+
+/* RCC_APB4SECSR register fields */
+#define RCC_APB4SECSR_DCMIPPSECF 1
+#define RCC_APB4SECSR_USBPHYSECF 16
+
+/* RCC_APB5SECSR register fields */
+#define RCC_APB5SECSR_RTCSECF 8
+#define RCC_APB5SECSR_TZCSECF 11
+#define RCC_APB5SECSR_ETZPCSECF 13
+#define RCC_APB5SECSR_IWDG1SECF 15
+#define RCC_APB5SECSR_BSECSECF 16
+#define RCC_APB5SECSR_STGENCSECF_MASK GENMASK(21, 20)
+#define RCC_APB5SECSR_STGENCSECF 20
+#define RCC_APB5SECSR_STGENROSECF 21
+
+/* RCC_APB6SECSR register fields */
+#define RCC_APB6SECSR_USART1SECF 0
+#define RCC_APB6SECSR_USART2SECF 1
+#define RCC_APB6SECSR_SPI4SECF 2
+#define RCC_APB6SECSR_SPI5SECF 3
+#define RCC_APB6SECSR_I2C3SECF 4
+#define RCC_APB6SECSR_I2C4SECF 5
+#define RCC_APB6SECSR_I2C5SECF 6
+#define RCC_APB6SECSR_TIM12SECF 7
+#define RCC_APB6SECSR_TIM13SECF 8
+#define RCC_APB6SECSR_TIM14SECF 9
+#define RCC_APB6SECSR_TIM15SECF 10
+#define RCC_APB6SECSR_TIM16SECF 11
+#define RCC_APB6SECSR_TIM17SECF 12
+
+/* RCC_AHB2SECSR register fields */
+#define RCC_AHB2SECSR_DMA3SECF 3
+#define RCC_AHB2SECSR_DMAMUX2SECF 4
+#define RCC_AHB2SECSR_ADC1SECF 5
+#define RCC_AHB2SECSR_ADC2SECF 6
+#define RCC_AHB2SECSR_USBOSECF 8
+
+/* RCC_AHB4SECSR register fields */
+#define RCC_AHB4SECSR_TSCSECF 15
+
+/* RCC_AHB5SECSR register fields */
+#define RCC_AHB5SECSR_PKASECF 2
+#define RCC_AHB5SECSR_SAESSECF 3
+#define RCC_AHB5SECSR_CRYP1SECF 4
+#define RCC_AHB5SECSR_HASH1SECF 5
+#define RCC_AHB5SECSR_RNG1SECF 6
+#define RCC_AHB5SECSR_BKPSRAMSECF 8
+
+/* RCC_AHB6SECSR register fields */
+#define RCC_AHB6SECSR_MCESECF 1
+#define RCC_AHB6SECSR_FMCSECF 12
+#define RCC_AHB6SECSR_QSPISECF 14
+#define RCC_AHB6SECSR_SDMMC1SECF 16
+#define RCC_AHB6SECSR_SDMMC2SECF 17
+
+#define RCC_AHB6SECSR_ETH1SECF_MASK GENMASK(11, 7)
+#define RCC_AHB6SECSR_ETH2SECF_MASK GENMASK(31, 27)
+#define RCC_AHB6SECSR_ETH1SECF_SHIFT 7
+#define RCC_AHB6SECSR_ETH2SECF_SHIFT 27
+
+#define RCC_AHB6SECSR_ETH1CKSECF 7
+#define RCC_AHB6SECSR_ETH1TXSECF 8
+#define RCC_AHB6SECSR_ETH1RXSECF 9
+#define RCC_AHB6SECSR_ETH1MACSECF 10
+#define RCC_AHB6SECSR_ETH1STPSECF 11
+
+#define RCC_AHB6SECSR_ETH2CKSECF 27
+#define RCC_AHB6SECSR_ETH2TXSECF 28
+#define RCC_AHB6SECSR_ETH2RXSECF 29
+#define RCC_AHB6SECSR_ETH2MACSECF 30
+#define RCC_AHB6SECSR_ETH2STPSECF 31
+
+/* RCC_VERR register fields */
+#define RCC_VERR_MINREV_MASK GENMASK(3, 0)
+#define RCC_VERR_MAJREV_MASK GENMASK(7, 4)
+#define RCC_VERR_MINREV_SHIFT 0
+#define RCC_VERR_MAJREV_SHIFT 4
+
+/* RCC_IDR register fields */
+#define RCC_IDR_ID_MASK GENMASK(31, 0)
+#define RCC_IDR_ID_SHIFT 0
+
+/* RCC_SIDR register fields */
+#define RCC_SIDR_SID_MASK GENMASK(31, 0)
+#define RCC_SIDR_SID_SHIFT 0
+
+#endif /* STM32MP13_RCC_H */
+
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index 0d364f318d..32f57cc2e4 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -13,7 +13,7 @@
#include <linux/clk.h>
#include <linux/err.h>
-#include <mach/iomap.h>
+#include <mach/tegra/iomap.h>
#include "clk.h"
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index bdd822e296..6c4ec06b28 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -12,9 +12,9 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/lowlevel.h>
-#include <mach/tegra20-car.h>
-#include <mach/tegra30-car.h>
+#include <mach/tegra/lowlevel.h>
+#include <mach/tegra/tegra20-car.h>
+#include <mach/tegra/tegra30-car.h>
#include "clk.h"
@@ -322,7 +322,7 @@ static struct tegra_clk_init_table init_table[] = {
{TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0}, /* sentinel */
};
-static int tegra124_car_probe(struct device_d *dev)
+static int tegra124_car_probe(struct device *dev)
{
struct resource *iores;
iores = dev_request_mem_resource(dev, 0);
@@ -345,10 +345,10 @@ static int tegra124_car_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
- tegra_clk_init_rst_controller(car_base, dev->device_node, 6 * 32);
+ tegra_clk_init_rst_controller(car_base, dev->of_node, 6 * 32);
tegra_clk_reset_uarts();
return 0;
@@ -361,8 +361,9 @@ static __maybe_unused struct of_device_id tegra124_car_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra124_car_dt_ids);
-static struct driver_d tegra124_car_driver = {
+static struct driver tegra124_car_driver = {
.probe = tegra124_car_probe,
.name = "tegra124-car",
.of_compatible = DRV_OF_COMPAT(tegra124_car_dt_ids),
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index 6e5fa144e4..77ba62c132 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -12,8 +12,8 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/lowlevel.h>
-#include <mach/tegra20-car.h>
+#include <mach/tegra/lowlevel.h>
+#include <mach/tegra/tegra20-car.h>
#include "clk.h"
@@ -325,7 +325,7 @@ static struct tegra_clk_init_table init_table[] = {
{TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_CLK_MAX, 0, 0}, /* sentinel */
};
-static int tegra20_car_probe(struct device_d *dev)
+static int tegra20_car_probe(struct device *dev)
{
struct resource *iores;
iores = dev_request_mem_resource(dev, 0);
@@ -348,10 +348,10 @@ static int tegra20_car_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
- tegra_clk_init_rst_controller(car_base, dev->device_node, 3 * 32);
+ tegra_clk_init_rst_controller(car_base, dev->of_node, 3 * 32);
tegra_clk_reset_uarts();
return 0;
@@ -364,8 +364,9 @@ static __maybe_unused struct of_device_id tegra20_car_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra20_car_dt_ids);
-static struct driver_d tegra20_car_driver = {
+static struct driver tegra20_car_driver = {
.probe = tegra20_car_probe,
.name = "tegra20-car",
.of_compatible = DRV_OF_COMPAT(tegra20_car_dt_ids),
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index 505851f8f5..69cc118ff9 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -12,9 +12,9 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/lowlevel.h>
-#include <mach/tegra20-car.h>
-#include <mach/tegra30-car.h>
+#include <mach/tegra/lowlevel.h>
+#include <mach/tegra/tegra20-car.h>
+#include <mach/tegra/tegra30-car.h>
#include "clk.h"
@@ -353,7 +353,7 @@ static struct tegra_clk_init_table init_table[] = {
{TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0}, /* sentinel */
};
-static int tegra30_car_probe(struct device_d *dev)
+static int tegra30_car_probe(struct device *dev)
{
struct resource *iores;
iores = dev_request_mem_resource(dev, 0);
@@ -376,10 +376,10 @@ static int tegra30_car_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
- tegra_clk_init_rst_controller(car_base, dev->device_node, 6 * 32);
+ tegra_clk_init_rst_controller(car_base, dev->of_node, 6 * 32);
tegra_clk_reset_uarts();
return 0;
@@ -392,8 +392,9 @@ static __maybe_unused struct of_device_id tegra30_car_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra30_car_dt_ids);
-static struct driver_d tegra30_car_driver = {
+static struct driver tegra30_car_driver = {
.probe = tegra30_car_probe,
.name = "tegra30-car",
.of_compatible = DRV_OF_COMPAT(tegra30_car_dt_ids),
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index 26ff9f2580..ad384d8d4d 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -8,7 +8,7 @@
#include <common.h>
#include <linux/clk.h>
#include <linux/reset-controller.h>
-#include <mach/lowlevel.h>
+#include <mach/tegra/lowlevel.h>
#include "clk.h"
diff --git a/drivers/clk/ti-sci-clk.c b/drivers/clk/ti-sci-clk.c
new file mode 100644
index 0000000000..57e0406553
--- /dev/null
+++ b/drivers/clk/ti-sci-clk.c
@@ -0,0 +1,630 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SCI Clock driver for keystone based devices
+ *
+ * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
+ * Tero Kristo <t-kristo@ti.com>
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <of.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/bsearch.h>
+#include <soc/ti/ti_sci_protocol.h>
+#include <linux/list_sort.h>
+
+#define SCI_CLK_SSC_ENABLE BIT(0)
+#define SCI_CLK_ALLOW_FREQ_CHANGE BIT(1)
+#define SCI_CLK_INPUT_TERMINATION BIT(2)
+
+/**
+ * struct sci_clk_provider - TI SCI clock provider representation
+ * @sci: Handle to the System Control Interface protocol handler
+ * @ops: Pointer to the SCI ops to be used by the clocks
+ * @dev: Device pointer for the clock provider
+ * @clocks: Clocks array for this device
+ * @num_clocks: Total number of clocks for this provider
+ */
+struct sci_clk_provider {
+ const struct ti_sci_handle *sci;
+ const struct ti_sci_clk_ops *ops;
+ struct device *dev;
+ struct sci_clk **clocks;
+ int num_clocks;
+};
+
+/**
+ * struct sci_clk - TI SCI clock representation
+ * @hw: Hardware clock cookie for common clock framework
+ * @dev_id: Device index
+ * @clk_id: Clock index
+ * @num_parents: Number of parents for this clock
+ * @provider: Master clock provider
+ * @flags: Flags for the clock
+ * @node: Link for handling clocks probed via DT
+ * @cached_req: Cached requested freq for determine rate calls
+ * @cached_res: Cached result freq for determine rate calls
+ */
+struct sci_clk {
+ struct clk_hw hw;
+ u16 dev_id;
+ u32 clk_id;
+ u32 num_parents;
+ struct sci_clk_provider *provider;
+ u8 flags;
+ struct list_head node;
+ unsigned long cached_req;
+ unsigned long cached_res;
+};
+
+#define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw)
+
+/**
+ * sci_clk_enable - enable a TI SCI clock
+ * @hw: clock to enable
+ *
+ * Enables a clock to be actively used. Returns the SCI protocol status.
+ */
+static int sci_clk_enable(struct clk_hw *hw)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+ bool enable_ssc = clk->flags & SCI_CLK_SSC_ENABLE;
+ bool allow_freq_change = clk->flags & SCI_CLK_ALLOW_FREQ_CHANGE;
+ bool input_termination = clk->flags & SCI_CLK_INPUT_TERMINATION;
+
+ return clk->provider->ops->get_clock(clk->provider->sci, clk->dev_id,
+ clk->clk_id, enable_ssc,
+ allow_freq_change,
+ input_termination);
+}
+
+/**
+ * sci_clk_disable - disables a TI SCI clock
+ * @hw: clock to disable
+ *
+ * Disables a clock from active state.
+ */
+static void sci_clk_disable(struct clk_hw *hw)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+ int ret;
+
+ ret = clk->provider->ops->put_clock(clk->provider->sci, clk->dev_id,
+ clk->clk_id);
+ if (ret)
+ dev_err(clk->provider->dev,
+ "unprepare failed for dev=%d, clk=%d, ret=%d\n",
+ clk->dev_id, clk->clk_id, ret);
+}
+
+/**
+ * sci_clk_is_enabled - Check if a TI SCI clock is enabled or not
+ * @hw: clock to check status for
+ *
+ * Checks if a clock is enabled in hardware. Returns non-zero
+ * value if clock is enabled, zero otherwise.
+ */
+static int sci_clk_is_enabled(struct clk_hw *hw)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+ bool req_state, current_state;
+ int ret;
+
+ ret = clk->provider->ops->is_on(clk->provider->sci, clk->dev_id,
+ clk->clk_id, &req_state,
+ &current_state);
+ if (ret) {
+ dev_err(clk->provider->dev,
+ "is_prepared failed for dev=%d, clk=%d, ret=%d\n",
+ clk->dev_id, clk->clk_id, ret);
+ return 0;
+ }
+
+ return req_state;
+}
+
+/**
+ * sci_clk_recalc_rate - Get clock rate for a TI SCI clock
+ * @hw: clock to get rate for
+ * @parent_rate: parent rate provided by common clock framework, not used
+ *
+ * Gets the current clock rate of a TI SCI clock. Returns the current
+ * clock rate, or zero in failure.
+ */
+static unsigned long sci_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+ u64 freq;
+ int ret;
+
+ ret = clk->provider->ops->get_freq(clk->provider->sci, clk->dev_id,
+ clk->clk_id, &freq);
+ if (ret) {
+ dev_err(clk->provider->dev,
+ "recalc-rate failed for dev=%d, clk=%d, ret=%d\n",
+ clk->dev_id, clk->clk_id, ret);
+ return 0;
+ }
+
+ return freq;
+}
+
+/**
+ * sci_clk_set_rate - Set rate for a TI SCI clock
+ * @hw: clock to change rate for
+ * @rate: target rate for the clock
+ * @parent_rate: rate of the clock parent, not used for TI SCI clocks
+ *
+ * Sets a clock frequency for a TI SCI clock. Returns the TI SCI
+ * protocol status.
+ */
+static int sci_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+
+ return clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id,
+ clk->clk_id, rate / 10 * 9, rate,
+ rate / 10 * 11);
+}
+
+/**
+ * sci_clk_get_parent - Get the current parent of a TI SCI clock
+ * @hw: clock to get parent for
+ *
+ * Returns the index of the currently selected parent for a TI SCI clock.
+ */
+static int sci_clk_get_parent(struct clk_hw *hw)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+ u32 parent_id = 0;
+ int ret;
+
+ ret = clk->provider->ops->get_parent(clk->provider->sci, clk->dev_id,
+ clk->clk_id, (void *)&parent_id);
+ if (ret) {
+ dev_err(clk->provider->dev,
+ "get-parent failed for dev=%d, clk=%d, ret=%d\n",
+ clk->dev_id, clk->clk_id, ret);
+ return 0;
+ }
+
+ parent_id = parent_id - clk->clk_id - 1;
+
+ return parent_id;
+}
+
+/**
+ * sci_clk_set_parent - Set the parent of a TI SCI clock
+ * @hw: clock to set parent for
+ * @index: new parent index for the clock
+ *
+ * Sets the parent of a TI SCI clock. Return TI SCI protocol status.
+ */
+static int sci_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct sci_clk *clk = to_sci_clk(hw);
+
+ clk->cached_req = 0;
+
+ return clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id,
+ clk->clk_id,
+ index + 1 + clk->clk_id);
+}
+
+static const struct clk_ops sci_clk_ops = {
+ .enable = sci_clk_enable,
+ .disable = sci_clk_disable,
+ .is_enabled = sci_clk_is_enabled,
+ .recalc_rate = sci_clk_recalc_rate,
+ .set_rate = sci_clk_set_rate,
+ .get_parent = sci_clk_get_parent,
+ .set_parent = sci_clk_set_parent,
+};
+
+/**
+ * _sci_clk_get - Gets a handle for an SCI clock
+ * @provider: Handle to SCI clock provider
+ * @sci_clk: Handle to the SCI clock to populate
+ *
+ * Gets a handle to an existing TI SCI hw clock, or builds a new clock
+ * entry and registers it with the common clock framework. Called from
+ * the common clock framework, when a corresponding of_clk_get call is
+ * executed, or recursively from itself when parsing parent clocks.
+ * Returns 0 on success, negative error code on failure.
+ */
+static int _sci_clk_build(struct sci_clk_provider *provider,
+ struct sci_clk *sci_clk)
+{
+ struct clk_init_data init = { NULL };
+ char *name = NULL;
+ char **parent_names = NULL;
+ int i;
+ int ret = 0;
+
+ name = basprintf("clk:%d:%d", sci_clk->dev_id, sci_clk->clk_id);
+ if (!name)
+ return -ENOMEM;
+
+ init.name = name;
+
+ /*
+ * From kernel point of view, we only care about a clocks parents,
+ * if it has more than 1 possible parent. In this case, it is going
+ * to have mux functionality. Otherwise it is going to act as a root
+ * clock.
+ */
+ if (sci_clk->num_parents < 2)
+ sci_clk->num_parents = 0;
+
+ if (sci_clk->num_parents) {
+ parent_names = kcalloc(sci_clk->num_parents, sizeof(char *),
+ GFP_KERNEL);
+
+ if (!parent_names) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < sci_clk->num_parents; i++) {
+ char *parent_name;
+
+ parent_name = basprintf("clk:%d:%d",
+ sci_clk->dev_id,
+ sci_clk->clk_id + 1 + i);
+ if (!parent_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ parent_names[i] = parent_name;
+ }
+ init.parent_names = (void *)parent_names;
+ }
+
+ init.ops = &sci_clk_ops;
+ init.num_parents = sci_clk->num_parents;
+ sci_clk->hw.init = &init;
+
+ ret = clk_hw_register(provider->dev, &sci_clk->hw);
+ if (ret)
+ dev_err(provider->dev, "failed clk register with %d\n", ret);
+
+err:
+ if (parent_names) {
+ for (i = 0; i < sci_clk->num_parents; i++)
+ kfree(parent_names[i]);
+
+ kfree(parent_names);
+ }
+
+ kfree(name);
+
+ return ret;
+}
+
+static int _cmp_sci_clk(const void *a, const void *b)
+{
+ const struct sci_clk *ca = a;
+ const struct sci_clk *cb = *(struct sci_clk **)b;
+
+ if (ca->dev_id == cb->dev_id && ca->clk_id == cb->clk_id)
+ return 0;
+ if (ca->dev_id > cb->dev_id ||
+ (ca->dev_id == cb->dev_id && ca->clk_id > cb->clk_id))
+ return 1;
+ return -1;
+}
+
+/**
+ * sci_clk_get - Xlate function for getting clock handles
+ * @clkspec: device tree clock specifier
+ * @data: pointer to the clock provider
+ *
+ * Xlate function for retrieving clock TI SCI hw clock handles based on
+ * device tree clock specifier. Called from the common clock framework,
+ * when a corresponding of_clk_get call is executed. Returns a pointer
+ * to the TI SCI hw clock struct, or ERR_PTR value in failure.
+ */
+static struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct sci_clk_provider *provider = data;
+ struct sci_clk **clk;
+ struct sci_clk key;
+
+ if (clkspec->args_count != 2)
+ return ERR_PTR(-EINVAL);
+
+ key.dev_id = clkspec->args[0];
+ key.clk_id = clkspec->args[1];
+
+ clk = bsearch(&key, provider->clocks, provider->num_clocks,
+ sizeof(clk), _cmp_sci_clk);
+
+ if (!clk)
+ return ERR_PTR(-ENODEV);
+
+ return &(*clk)->hw;
+}
+
+static int ti_sci_init_clocks(struct sci_clk_provider *p)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < p->num_clocks; i++) {
+ ret = _sci_clk_build(p, p->clocks[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ti_sci_clk_of_match[] = {
+ { .compatible = "ti,k2g-sci-clk" },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ti_sci_clk_of_match);
+
+#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW
+static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider)
+{
+ int ret;
+ int num_clks = 0;
+ struct sci_clk **clks = NULL;
+ struct sci_clk **tmp_clks;
+ struct sci_clk *sci_clk;
+ int max_clks = 0;
+ int clk_id = 0;
+ int dev_id = 0;
+ u32 num_parents = 0;
+ int gap_size = 0;
+ struct device *dev = provider->dev;
+
+ while (1) {
+ ret = provider->ops->get_num_parents(provider->sci, dev_id,
+ clk_id,
+ (void *)&num_parents);
+ if (ret) {
+ gap_size++;
+ if (!clk_id) {
+ if (gap_size >= 5)
+ break;
+ dev_id++;
+ } else {
+ if (gap_size >= 2) {
+ dev_id++;
+ clk_id = 0;
+ gap_size = 0;
+ } else {
+ clk_id++;
+ }
+ }
+ continue;
+ }
+
+ gap_size = 0;
+
+ if (num_clks == max_clks) {
+ tmp_clks = devm_kmalloc_array(dev, max_clks + 64,
+ sizeof(sci_clk),
+ GFP_KERNEL);
+ memcpy(tmp_clks, clks, max_clks * sizeof(sci_clk));
+ if (max_clks)
+ devm_kfree(dev, clks);
+ max_clks += 64;
+ clks = tmp_clks;
+ }
+
+ sci_clk = devm_kzalloc(dev, sizeof(*sci_clk), GFP_KERNEL);
+ if (!sci_clk)
+ return -ENOMEM;
+ sci_clk->dev_id = dev_id;
+ sci_clk->clk_id = clk_id;
+ sci_clk->provider = provider;
+ sci_clk->num_parents = num_parents;
+
+ clks[num_clks] = sci_clk;
+
+ clk_id++;
+ num_clks++;
+ }
+
+ provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk),
+ GFP_KERNEL);
+ if (!provider->clocks)
+ return -ENOMEM;
+
+ memcpy(provider->clocks, clks, num_clks * sizeof(sci_clk));
+
+ provider->num_clocks = num_clks;
+
+ devm_kfree(dev, clks);
+
+ return 0;
+}
+
+#else
+
+static int _cmp_sci_clk_list(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ struct sci_clk *ca = container_of(a, struct sci_clk, node);
+ struct sci_clk *cb = container_of(b, struct sci_clk, node);
+
+ return _cmp_sci_clk(ca, &cb);
+}
+
+static int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider)
+{
+ struct device *dev = provider->dev;
+ struct device_node *np = NULL;
+ int ret;
+ int index;
+ struct of_phandle_args args;
+ struct list_head clks;
+ struct sci_clk *sci_clk, *prev;
+ int num_clks = 0;
+ int num_parents;
+ int clk_id;
+ const char * const clk_names[] = {
+ "clocks", "assigned-clocks", "assigned-clock-parents", NULL
+ };
+ const char * const *clk_name;
+
+ INIT_LIST_HEAD(&clks);
+
+ clk_name = clk_names;
+
+ while (*clk_name) {
+ np = of_find_node_with_property(np, *clk_name);
+ if (!np) {
+ clk_name++;
+ continue;
+ }
+
+ if (!of_device_is_available(np))
+ continue;
+
+ index = 0;
+
+ do {
+ ret = of_parse_phandle_with_args(np, *clk_name,
+ "#clock-cells", index,
+ &args);
+ if (ret)
+ break;
+
+ if (args.args_count == 2 && args.np == dev->of_node) {
+ sci_clk = xzalloc(sizeof(*sci_clk));
+
+ sci_clk->dev_id = args.args[0];
+ sci_clk->clk_id = args.args[1];
+ sci_clk->provider = provider;
+ provider->ops->get_num_parents(provider->sci,
+ sci_clk->dev_id,
+ sci_clk->clk_id,
+ (void *)&sci_clk->num_parents);
+ list_add_tail(&sci_clk->node, &clks);
+
+ num_clks++;
+
+ num_parents = sci_clk->num_parents;
+ if (num_parents == 1)
+ num_parents = 0;
+
+ /*
+ * Linux kernel has inherent limitation
+ * of 255 clock parents at the moment.
+ * Right now, it is not expected that
+ * any mux clock from sci-clk driver
+ * would exceed that limit either, but
+ * the ABI basically provides that
+ * possibility. Print out a warning if
+ * this happens for any clock.
+ */
+ if (num_parents >= 255) {
+ dev_warn(dev, "too many parents for dev=%d, clk=%d (%d), cropping to 255.\n",
+ sci_clk->dev_id,
+ sci_clk->clk_id, num_parents);
+ num_parents = 255;
+ }
+
+ clk_id = args.args[1] + 1;
+
+ while (num_parents--) {
+ sci_clk = xzalloc(sizeof(*sci_clk));
+ sci_clk->dev_id = args.args[0];
+ sci_clk->clk_id = clk_id++;
+ sci_clk->provider = provider;
+ list_add_tail(&sci_clk->node, &clks);
+
+ num_clks++;
+ }
+ }
+
+ index++;
+ } while (args.np);
+ }
+
+ list_sort(NULL, &clks, _cmp_sci_clk_list);
+
+ provider->clocks = xzalloc(num_clks * sizeof(sci_clk));
+
+ num_clks = 0;
+ prev = NULL;
+
+ list_for_each_entry(sci_clk, &clks, node) {
+ if (prev && prev->dev_id == sci_clk->dev_id &&
+ prev->clk_id == sci_clk->clk_id)
+ continue;
+
+ provider->clocks[num_clks++] = sci_clk;
+ prev = sci_clk;
+ }
+
+ provider->num_clocks = num_clks;
+
+ return 0;
+}
+#endif
+
+/**
+ * ti_sci_clk_probe - Probe function for the TI SCI clock driver
+ * @pdev: platform device pointer to be probed
+ *
+ * Probes the TI SCI clock device. Allocates a new clock provider
+ * and registers this to the common clock framework. Also applies
+ * any required flags to the identified clocks via clock lists
+ * supplied from DT. Returns 0 for success, negative error value
+ * for failure.
+ */
+static int ti_sci_clk_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct sci_clk_provider *provider;
+ const struct ti_sci_handle *handle;
+ int ret;
+
+ handle = ti_sci_get_handle(dev);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ provider = xzalloc(sizeof(*provider));
+
+ provider->sci = handle;
+ provider->ops = &handle->ops.clk_ops;
+ provider->dev = dev;
+
+ ret = ti_sci_scan_clocks_from_dt(provider);
+ if (ret) {
+ dev_err(dev, "scan clocks from DT failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = ti_sci_init_clocks(provider);
+ if (ret) {
+ pr_err("ti-sci-init-clocks failed.\n");
+ return ret;
+ }
+
+ return of_clk_add_hw_provider(np, sci_clk_get, provider);
+}
+
+static struct driver ti_sci_clk_driver = {
+ .probe = ti_sci_clk_probe,
+ .name = "ti-sci-clk",
+ .of_compatible = DRV_OF_COMPAT(ti_sci_clk_of_match),
+};
+
+core_platform_driver(ti_sci_clk_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI System Control Interface(SCI) Clock driver");
+MODULE_AUTHOR("Tero Kristo");
+MODULE_ALIAS("platform:ti-sci-clk");
diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c
index e4ce102d6e..d6de583e32 100644
--- a/drivers/clk/zynq/clkc.c
+++ b/drivers/clk/zynq/clkc.c
@@ -15,7 +15,7 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <mach/zynq7000-regs.h>
+#include <mach/zynq/zynq7000-regs.h>
#include <malloc.h>
enum zynq_clk {
@@ -364,11 +364,11 @@ static struct clk *zynq_cpu_subclk(const char *name,
return &subclk->hw.clk;
}
-static int zynq_clock_probe(struct device_d *dev)
+static int zynq_clock_probe(struct device *dev)
{
struct resource *iores;
void __iomem *clk_base;
- unsigned long ps_clk_rate = 33333330;
+ u32 ps_clk_rate = 33333330;
resource_size_t slcr_offset = 0;
iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
@@ -380,7 +380,7 @@ static int zynq_clock_probe(struct device_d *dev)
* in the SCLR region. So we can't directly map the address we get from
* the DT, but need to add the SCLR base offset.
*/
- if (dev->device_node) {
+ if (dev->of_node) {
struct resource *parent_res;
parent_res = dev_get_resource(dev->parent, IORESOURCE_MEM, 0);
@@ -390,6 +390,8 @@ static int zynq_clock_probe(struct device_d *dev)
slcr_offset = parent_res->start;
}
+ of_property_read_u32(dev->device_node, "ps-clk-frequency", &ps_clk_rate);
+
iores = request_iomem_region(dev_name(dev), iores->start + slcr_offset,
iores->end + slcr_offset);
if (IS_ERR(iores))
@@ -470,7 +472,7 @@ static int zynq_clock_probe(struct device_d *dev)
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&clk_data);
return 0;
@@ -483,8 +485,9 @@ static __maybe_unused struct of_device_id zynq_clock_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, zynq_clock_dt_ids);
-static struct driver_d zynq_clock_driver = {
+static struct driver zynq_clock_driver = {
.probe = zynq_clock_probe,
.name = "zynq-clock",
.of_compatible = DRV_OF_COMPAT(zynq_clock_dt_ids),
diff --git a/drivers/clk/zynqmp/clk-divider-zynqmp.c b/drivers/clk/zynqmp/clk-divider-zynqmp.c
index 38c7baa0c6..d78cda38b7 100644
--- a/drivers/clk/zynqmp/clk-divider-zynqmp.c
+++ b/drivers/clk/zynqmp/clk-divider-zynqmp.c
@@ -11,7 +11,7 @@
#include <common.h>
#include <linux/clk.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "clk-zynqmp.h"
diff --git a/drivers/clk/zynqmp/clk-gate-zynqmp.c b/drivers/clk/zynqmp/clk-gate-zynqmp.c
index 7b5a432aa8..daa17c34b8 100644
--- a/drivers/clk/zynqmp/clk-gate-zynqmp.c
+++ b/drivers/clk/zynqmp/clk-gate-zynqmp.c
@@ -11,7 +11,7 @@
#include <common.h>
#include <linux/clk.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "clk-zynqmp.h"
diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
index 29bd9e6ef0..fe31cff4b6 100644
--- a/drivers/clk/zynqmp/clk-mux-zynqmp.c
+++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
@@ -11,7 +11,7 @@
#include <common.h>
#include <linux/clk.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "clk-zynqmp.h"
diff --git a/drivers/clk/zynqmp/clk-pll-zynqmp.c b/drivers/clk/zynqmp/clk-pll-zynqmp.c
index 791f31a5a5..b386780f18 100644
--- a/drivers/clk/zynqmp/clk-pll-zynqmp.c
+++ b/drivers/clk/zynqmp/clk-pll-zynqmp.c
@@ -11,7 +11,7 @@
#include <common.h>
#include <linux/clk.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "clk-zynqmp.h"
diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c
index d7ac0bdb36..35eaf6f18e 100644
--- a/drivers/clk/zynqmp/clkc.c
+++ b/drivers/clk/zynqmp/clkc.c
@@ -14,7 +14,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "clk-zynqmp.h"
@@ -446,13 +446,13 @@ static struct clk *zynqmp_register_clk_topology(char *clk_name,
return clk;
}
-static int zynqmp_register_clocks(struct device_d *dev,
+static int zynqmp_register_clocks(struct device *dev,
struct clk **clks, size_t num_clocks)
{
unsigned int i;
const char *parent_names[MAX_PARENT];
char *name;
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
int num_parents;
for (i = 0; i < num_clocks; i++) {
@@ -518,7 +518,7 @@ static void zynqmp_fill_clock_info(struct zynqmp_clock_info *clock_info,
}
}
-static int zynqmp_clock_probe(struct device_d *dev)
+static int zynqmp_clock_probe(struct device *dev)
{
int err;
u32 api_version;
@@ -558,7 +558,7 @@ static int zynqmp_clock_probe(struct device_d *dev)
zynqmp_register_clocks(dev, clk_data->clks, num_clocks);
clk_data->clk_num = num_clocks;
- of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, clk_data);
+ of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, clk_data);
/*
* We can free clock_info now, as is only used to store clock info
@@ -573,8 +573,9 @@ static struct of_device_id zynqmp_clock_of_match[] = {
{.compatible = "xlnx,zynqmp-clk"},
{},
};
+MODULE_DEVICE_TABLE(of, zynqmp_clock_of_match);
-static struct driver_d zynqmp_clock_driver = {
+static struct driver zynqmp_clock_driver = {
.probe = zynqmp_clock_probe,
.name = "zynqmp_clock",
.of_compatible = DRV_OF_COMPAT(zynqmp_clock_of_match),
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index e1bff23320..5ee83d2b38 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -42,11 +42,11 @@ config CLOCKSOURCE_DUMMY_RATE
config CLOCKSOURCE_EFI
bool "Generic EFI Driver"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
config CLOCKSOURCE_EFI_X86
bool "EFI X86 HW driver"
- depends on EFI_BOOTUP && X86
+ depends on EFI_PAYLOAD && X86
config CLOCKSOURCE_KVX
bool "KVX core timer clocksource"
diff --git a/drivers/clocksource/arm_architected_timer.c b/drivers/clocksource/arm_architected_timer.c
index d5948fe948..9a1f2d2b79 100644
--- a/drivers/clocksource/arm_architected_timer.c
+++ b/drivers/clocksource/arm_architected_timer.c
@@ -22,7 +22,7 @@ static struct clocksource cs = {
.priority = 70,
};
-static int arm_arch_timer_probe(struct device_d *dev)
+static int arm_arch_timer_probe(struct device *dev)
{
cs.mult = clocksource_hz2mult(get_cntfrq(), cs.shift);
@@ -34,8 +34,9 @@ static struct of_device_id arm_arch_timer_dt_ids[] = {
{ .compatible = "arm,armv8-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, arm_arch_timer_dt_ids);
-static struct driver_d arm_arch_timer_driver = {
+static struct driver arm_arch_timer_driver = {
.name = "arm-architected-timer",
.probe = arm_arch_timer_probe,
.of_compatible = DRV_OF_COMPAT(arm_arch_timer_dt_ids),
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index 65bfca3558..969985f24e 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -63,7 +63,7 @@ static struct clocksource cs = {
.priority = 70,
};
-static int arm_global_timer_probe(struct device_d *dev)
+static int arm_global_timer_probe(struct device *dev)
{
struct resource *iores;
struct clk *clk;
@@ -103,8 +103,9 @@ static struct of_device_id arm_global_timer_dt_ids[] = {
{ .compatible = "arm,cortex-a9-global-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, arm_global_timer_dt_ids);
-static struct driver_d arm_global_timer_driver = {
+static struct driver arm_global_timer_driver = {
.name = "arm-global-timer",
.probe = arm_global_timer_probe,
.of_compatible = DRV_OF_COMPAT(arm_global_timer_dt_ids),
diff --git a/drivers/clocksource/arm_smp_twd.c b/drivers/clocksource/arm_smp_twd.c
index 1ad754bb2b..e677acf136 100644
--- a/drivers/clocksource/arm_smp_twd.c
+++ b/drivers/clocksource/arm_smp_twd.c
@@ -40,7 +40,7 @@ static struct clocksource smp_twd_clksrc = {
#define SMP_TWD_MAX_FREQ (25 *1000 * 1000)
-static int smp_twd_probe(struct device_d *dev)
+static int smp_twd_probe(struct device *dev)
{
struct resource *iores;
u32 tick_rate;
@@ -98,8 +98,9 @@ static __maybe_unused struct of_device_id smp_twd_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, smp_twd_compatible);
-static struct driver_d smp_twd_driver = {
+static struct driver smp_twd_driver = {
.name = "smp_twd",
.probe = smp_twd_probe,
.of_compatible = DRV_OF_COMPAT(smp_twd_compatible),
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
index 16d89c23bd..eee028d12a 100644
--- a/drivers/clocksource/armv7m_systick.c
+++ b/drivers/clocksource/armv7m_systick.c
@@ -37,7 +37,7 @@ static struct clocksource cs = {
.priority = 70,
};
-static int armv7m_systick_probe(struct device_d *dev)
+static int armv7m_systick_probe(struct device *dev)
{
struct clk *clk = NULL;
u32 rate, cal;
@@ -47,7 +47,7 @@ static int armv7m_systick_probe(struct device_d *dev)
if (!systick_base)
return -ENOENT;
- ret = of_property_read_u32(dev->device_node, "clock-frequency", &rate);
+ ret = of_property_read_u32(dev->of_node, "clock-frequency", &rate);
if (ret) {
clk = clk_get(dev, NULL);
if (IS_ERR(clk))
@@ -64,7 +64,7 @@ static int armv7m_systick_probe(struct device_d *dev)
writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, systick_base + SYST_RVR);
- cal = readl(&systick_base + SYST_CALIB);
+ cal = readl(systick_base + SYST_CALIB);
if (cal & SYSTICK_CAL_NOREF)
writel(SYSTICK_CTRL_EN | SYSTICK_CTRL_CPU_CLK, systick_base + SYST_CSR);
else
@@ -79,8 +79,9 @@ static struct of_device_id armv7m_systick_dt_ids[] = {
{ .compatible = "arm,armv7m-systick", },
{ }
};
+MODULE_DEVICE_TABLE(of, armv7m_systick_dt_ids);
-static struct driver_d armv7m_systick_driver = {
+static struct driver armv7m_systick_driver = {
.name = "armv7m-systick-timer",
.probe = armv7m_systick_probe,
.of_compatible = DRV_OF_COMPAT(armv7m_systick_dt_ids),
diff --git a/drivers/clocksource/bcm2835.c b/drivers/clocksource/bcm2835.c
index d5d3e3477d..73f4e8f411 100644
--- a/drivers/clocksource/bcm2835.c
+++ b/drivers/clocksource/bcm2835.c
@@ -32,7 +32,7 @@ static struct clocksource bcm2835_stc = {
.priority = IS_ENABLED(CONFIG_CPU_V8) ? 60 : 80,
};
-static int bcm2835_cs_probe(struct device_d *dev)
+static int bcm2835_cs_probe(struct device *dev)
{
struct resource *iores;
static struct clk *stc_clk;
@@ -41,7 +41,7 @@ static int bcm2835_cs_probe(struct device_d *dev)
/* try to read rate from DT property first */
if (IS_ENABLED(CONFIG_OFTREE))
- of_property_read_u32(dev->device_node, "clock-frequency",
+ of_property_read_u32(dev->of_node, "clock-frequency",
&rate);
/* if rate is still empty, try to get rate from clk */
@@ -80,8 +80,9 @@ static __maybe_unused struct of_device_id bcm2835_cs_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, bcm2835_cs_dt_ids);
-static struct driver_d bcm2835_cs_driver = {
+static struct driver bcm2835_cs_driver = {
.name = "bcm2835-cs",
.probe = bcm2835_cs_probe,
.of_compatible = DRV_OF_COMPAT(bcm2835_cs_dt_ids),
diff --git a/drivers/clocksource/clps711x.c b/drivers/clocksource/clps711x.c
index d6ab695afa..f8bdc06ea8 100644
--- a/drivers/clocksource/clps711x.c
+++ b/drivers/clocksource/clps711x.c
@@ -22,14 +22,14 @@ static struct clocksource clps711x_cs = {
.priority = 60,
};
-static int clps711x_cs_probe(struct device_d *dev)
+static int clps711x_cs_probe(struct device *dev)
{
struct resource *iores;
u32 rate;
struct clk *timer_clk;
int id;
- id = of_alias_get_id(dev->device_node, "timer");
+ id = of_alias_get_id(dev->of_node, "timer");
if (id != 1)
return 0;
@@ -55,8 +55,9 @@ static const struct of_device_id __maybe_unused clps711x_timer_dt_ids[] = {
{ .compatible = "cirrus,ep7209-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, clps711x_timer_dt_ids);
-static struct driver_d clps711x_cs_driver = {
+static struct driver clps711x_cs_driver = {
.name = "clps711x-cs",
.probe = clps711x_cs_probe,
.of_compatible = DRV_OF_COMPAT(clps711x_timer_dt_ids),
diff --git a/drivers/clocksource/digic.c b/drivers/clocksource/digic.c
index 234985aece..38c4bd7dd2 100644
--- a/drivers/clocksource/digic.c
+++ b/drivers/clocksource/digic.c
@@ -29,7 +29,7 @@ static struct clocksource digic_cs = {
.priority = 60,
};
-static int digic_timer_probe(struct device_d *dev)
+static int digic_timer_probe(struct device *dev)
{
struct resource *iores;
/* use only one timer */
@@ -73,8 +73,9 @@ static __maybe_unused struct of_device_id digic_timer_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, digic_timer_dt_ids);
-static struct driver_d digic_timer_driver = {
+static struct driver digic_timer_driver = {
.probe = digic_timer_probe,
.name = "digic-timer",
.of_compatible = DRV_OF_COMPAT(digic_timer_dt_ids),
diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c
index 251089cf7e..3826c7490d 100644
--- a/drivers/clocksource/dw_apb_timer.c
+++ b/drivers/clocksource/dw_apb_timer.c
@@ -97,9 +97,9 @@ static struct clocksource dw_apb_clksrc = {
.priority = 50,
};
-static int dw_apb_timer_probe(struct device_d *dev)
+static int dw_apb_timer_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct resource *iores;
struct clk *clk;
uint32_t clk_freq;
@@ -135,8 +135,9 @@ static struct of_device_id dw_apb_timer_dt_ids[] = {
{ .compatible = "snps,dw-apb-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, dw_apb_timer_dt_ids);
-static struct driver_d dw_apb_timer_driver = {
+static struct driver dw_apb_timer_driver = {
.name = "dw-apb-timer",
.probe = dw_apb_timer_probe,
.of_compatible = DRV_OF_COMPAT(dw_apb_timer_dt_ids),
diff --git a/drivers/clocksource/efi.c b/drivers/clocksource/efi.c
index 9df5226573..1ac587a715 100644
--- a/drivers/clocksource/efi.c
+++ b/drivers/clocksource/efi.c
@@ -12,14 +12,14 @@
#include <linux/err.h>
static uint64_t ticks = 1;
-static void *efi_cs_evt;
+static struct efi_event *efi_cs_evt;
static uint64_t efi_cs_read(void)
{
return ticks;
}
-static void efi_cs_inc(void *event, void *ctx)
+static void efi_cs_inc(struct efi_event *event, void *ctx)
{
ticks++;
}
@@ -96,12 +96,12 @@ static struct clocksource efi_cs = {
.priority = 80,
};
-static int efi_cs_probe(struct device_d *dev)
+static int efi_cs_probe(struct device *dev)
{
return init_clock(&efi_cs);
}
-static struct driver_d efi_cs_driver = {
+static struct driver efi_cs_driver = {
.name = "efi-cs",
.probe = efi_cs_probe,
};
diff --git a/drivers/clocksource/efi_x86.c b/drivers/clocksource/efi_x86.c
index c9b6c44a1e..f5a822eef7 100644
--- a/drivers/clocksource/efi_x86.c
+++ b/drivers/clocksource/efi_x86.c
@@ -62,12 +62,12 @@ static struct clocksource efi_x86_cs = {
.init = efi_x86_cs_init,
};
-static int efi_x86_cs_probe(struct device_d *dev)
+static int efi_x86_cs_probe(struct device *dev)
{
return init_clock(&efi_x86_cs);
}
-static struct driver_d efi_x86_cs_driver = {
+static struct driver efi_x86_cs_driver = {
.name = "efi-cs-x86",
.probe = efi_x86_cs_probe,
};
diff --git a/drivers/clocksource/kvx_timer.c b/drivers/clocksource/kvx_timer.c
index 259755eacd..eaa8f63b1e 100644
--- a/drivers/clocksource/kvx_timer.c
+++ b/drivers/clocksource/kvx_timer.c
@@ -24,7 +24,7 @@ static struct clocksource kvx_clksrc = {
.priority = 70,
};
-static int kvx_timer_probe(struct device_d *dev)
+static int kvx_timer_probe(struct device *dev)
{
struct clk *clk;
uint32_t clk_freq;
@@ -49,8 +49,9 @@ static struct of_device_id kvx_timer_dt_ids[] = {
{ .compatible = "kalray,kvx-core-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, kvx_timer_dt_ids);
-static struct driver_d kvx_timer_driver = {
+static struct driver kvx_timer_driver = {
.name = "kvx-timer",
.probe = kvx_timer_probe,
.of_compatible = DRV_OF_COMPAT(kvx_timer_dt_ids),
diff --git a/drivers/clocksource/mvebu.c b/drivers/clocksource/mvebu.c
index d3214955dc..6a1c3d58de 100644
--- a/drivers/clocksource/mvebu.c
+++ b/drivers/clocksource/mvebu.c
@@ -45,7 +45,7 @@ static struct clocksource cs = {
.priority = 70,
};
-static int mvebu_timer_probe(struct device_d *dev)
+static int mvebu_timer_probe(struct device *dev)
{
struct resource *iores;
struct clk *clk;
@@ -58,7 +58,7 @@ static int mvebu_timer_probe(struct device_d *dev)
val = __raw_readl(timer_base + TIMER_CTRL_OFF);
val &= ~(TIMER0_25MHZ | TIMER0_DIV_MASK);
- if (of_device_is_compatible(dev->device_node,
+ if (of_device_is_compatible(dev->of_node,
"marvell,armada-370-timer")) {
clk = clk_get(dev, NULL);
div = TIMER_DIVIDER;
@@ -89,8 +89,9 @@ static struct of_device_id mvebu_timer_dt_ids[] = {
{ .compatible = "marvell,armada-xp-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_timer_dt_ids);
-static struct driver_d mvebu_timer_driver = {
+static struct driver mvebu_timer_driver = {
.name = "mvebu-timer",
.probe = mvebu_timer_probe,
.of_compatible = DRV_OF_COMPAT(mvebu_timer_dt_ids),
diff --git a/drivers/clocksource/nomadik.c b/drivers/clocksource/nomadik.c
index 7cf10352ea..cffe9cdd03 100644
--- a/drivers/clocksource/nomadik.c
+++ b/drivers/clocksource/nomadik.c
@@ -90,7 +90,7 @@ static void nmdk_timer_reset(void)
writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
}
-static int nmdk_mtu_probe(struct device_d *dev)
+static int nmdk_mtu_probe(struct device *dev)
{
struct resource *iores;
static struct clk *mtu_clk;
@@ -135,7 +135,7 @@ static int nmdk_mtu_probe(struct device_d *dev)
return init_clock(&nmdk_clksrc);
}
-static struct driver_d nmdk_mtu_driver = {
+static struct driver nmdk_mtu_driver = {
.name = "nomadik_mtu",
.probe = nmdk_mtu_probe,
};
diff --git a/drivers/clocksource/orion.c b/drivers/clocksource/orion.c
index 4a56849800..caa314e884 100644
--- a/drivers/clocksource/orion.c
+++ b/drivers/clocksource/orion.c
@@ -34,7 +34,7 @@ static struct clocksource clksrc = {
.priority = 70,
};
-static int orion_timer_probe(struct device_d *dev)
+static int orion_timer_probe(struct device *dev)
{
struct resource *iores;
struct clk *tclk;
@@ -63,8 +63,9 @@ static struct of_device_id orion_timer_dt_ids[] = {
{ .compatible = "marvell,orion-timer", },
{ }
};
+MODULE_DEVICE_TABLE(of, orion_timer_dt_ids);
-static struct driver_d orion_timer_driver = {
+static struct driver orion_timer_driver = {
.name = "orion-timer",
.probe = orion_timer_probe,
.of_compatible = DRV_OF_COMPAT(orion_timer_dt_ids),
diff --git a/drivers/clocksource/rk_timer.c b/drivers/clocksource/rk_timer.c
index e941030771..eb6c886af0 100644
--- a/drivers/clocksource/rk_timer.c
+++ b/drivers/clocksource/rk_timer.c
@@ -10,12 +10,20 @@
#include <clock.h>
#include <init.h>
#include <io.h>
-#include <mach/timer.h>
#include <stdio.h>
-#include <mach/hardware.h>
-#include <mach/cru_rk3288.h>
+#include <mach/rockchip/hardware.h>
+#include <mach/rockchip/cru_rk3288.h>
#include <common.h>
+struct rk_timer {
+ unsigned int timer_load_count0;
+ unsigned int timer_load_count1;
+ unsigned int timer_curr_value0;
+ unsigned int timer_curr_value1;
+ unsigned int timer_ctrl_reg;
+ unsigned int timer_int_status;
+};
+
struct rk_timer *timer_ptr;
static uint64_t rockchip_get_ticks(void)
@@ -35,7 +43,7 @@ static struct clocksource rkcs = {
.priority = 80,
};
-static int rockchip_timer_probe(struct device_d *dev)
+static int rockchip_timer_probe(struct device *dev)
{
struct resource *iores;
@@ -60,8 +68,9 @@ static __maybe_unused struct of_device_id rktimer_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, rktimer_dt_ids);
-static struct driver_d rktimer_driver = {
+static struct driver rktimer_driver = {
.name = "rockchip-timer",
.probe = rockchip_timer_probe,
.of_compatible = DRV_OF_COMPAT(rktimer_dt_ids),
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index a133e384d7..9d2ff32c48 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -15,8 +15,8 @@
#include <common.h>
#include <init.h>
#include <clock.h>
-#include <mach/hardware.h>
-#include <mach/at91_pit.h>
+#include <mach/at91/hardware.h>
+#include <mach/at91/at91_pit.h>
#include <io.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -56,7 +56,7 @@ static void at91sam926x_pit_reset(void)
pit_write(AT91_PIT_MR, 0xfffff | AT91_PIT_PITEN);
}
-static int at91_pit_probe(struct device_d *dev)
+static int at91_pit_probe(struct device *dev)
{
struct clk *clk;
u32 pit_rate;
@@ -96,8 +96,9 @@ const static __maybe_unused struct of_device_id at91_pit_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, at91_pit_dt_ids);
-static struct driver_d at91_pit_driver = {
+static struct driver at91_pit_driver = {
.name = "at91-pit",
.probe = at91_pit_probe,
.of_compatible = DRV_OF_COMPAT(at91_pit_dt_ids),
diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c
index d48b5bcba0..f264eb4cee 100644
--- a/drivers/clocksource/timer-clint.c
+++ b/drivers/clocksource/timer-clint.c
@@ -60,7 +60,7 @@ static struct clocksource clint_clocksource = {
.priority = 200,
};
-static int clint_timer_init_dt(struct device_d* dev)
+static int clint_timer_init_dt(struct device * dev)
{
struct resource *iores;
@@ -85,8 +85,9 @@ static struct of_device_id timer_clint_dt_ids[] = {
{ .compatible = "sifive,clint0" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, timer_clint_dt_ids);
-static struct driver_d clint_timer_driver = {
+static struct driver clint_timer_driver = {
.name = "clint-timer",
.probe = clint_timer_init_dt,
.of_compatible = timer_clint_dt_ids,
diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index 7ca879f4f0..6cf60ed3fc 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -77,7 +77,7 @@ static struct notifier_block imx_clock_notifier = {
.notifier_call = imx_clocksource_clock_change,
};
-static int imx_gpt_probe(struct device_d *dev)
+static int imx_gpt_probe(struct device *dev)
{
struct resource *iores;
int i;
@@ -153,6 +153,7 @@ static __maybe_unused struct of_device_id imx_gpt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_gpt_dt_ids);
static struct platform_device_id imx_gpt_ids[] = {
{
@@ -166,7 +167,7 @@ static struct platform_device_id imx_gpt_ids[] = {
},
};
-static struct driver_d imx_gpt_driver = {
+static struct driver imx_gpt_driver = {
.name = "imx-gpt",
.probe = imx_gpt_probe,
.of_compatible = DRV_OF_COMPAT(imx_gpt_dt_ids),
diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c
index c7af54fc8f..93f88711dc 100644
--- a/drivers/clocksource/timer-riscv.c
+++ b/drivers/clocksource/timer-riscv.c
@@ -48,7 +48,7 @@ static struct clocksource riscv_clocksource = {
.priority = 100,
};
-static int riscv_timer_init(struct device_d* dev)
+static int riscv_timer_init(struct device * dev)
{
struct device_node *cpu;
@@ -67,7 +67,7 @@ static int riscv_timer_init(struct device_d* dev)
return init_clock(&riscv_clocksource);
}
-static struct driver_d riscv_timer_driver = {
+static struct driver riscv_timer_driver = {
.name = "riscv-timer",
.probe = riscv_timer_init,
};
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
index dec48fccf5..d653beb0eb 100644
--- a/drivers/clocksource/timer-stm32.c
+++ b/drivers/clocksource/timer-stm32.c
@@ -61,7 +61,7 @@ static struct clocksource cs = {
.priority = 100,
};
-static int stm32_timer_probe(struct device_d *dev)
+static int stm32_timer_probe(struct device *dev)
{
struct resource *iores;
struct clk *clk;
@@ -113,8 +113,9 @@ static struct of_device_id stm32_timer_dt_ids[] = {
{ .compatible = "st,stm32-timer" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_timer_dt_ids);
-static struct driver_d stm32_timer_driver = {
+static struct driver stm32_timer_driver = {
.name = "stm32-timer",
.probe = stm32_timer_probe,
.of_compatible = stm32_timer_dt_ids,
diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
index 21cb686369..1880082066 100644
--- a/drivers/clocksource/timer-ti-32k.c
+++ b/drivers/clocksource/timer-ti-32k.c
@@ -16,12 +16,12 @@
#include <clock.h>
#include <init.h>
#include <io.h>
-#include <mach/omap3-silicon.h>
-#include <mach/omap4-silicon.h>
-#include <mach/clocks.h>
-#include <mach/timers.h>
-#include <mach/sys_info.h>
-#include <mach/syslib.h>
+#include <mach/omap/omap3-silicon.h>
+#include <mach/omap/omap4-silicon.h>
+#include <mach/omap/clocks.h>
+#include <mach/omap/timers.h>
+#include <mach/omap/sys_info.h>
+#include <mach/omap/syslib.h>
/** Sync 32Khz Timer registers */
#define S32K_CR 0x10
@@ -59,7 +59,7 @@ static struct clocksource s32k_cs = {
*
* @return result of @ref init_clock
*/
-static int omap_32ktimer_probe(struct device_d *dev)
+static int omap_32ktimer_probe(struct device *dev)
{
struct resource *iores;
@@ -84,8 +84,9 @@ static __maybe_unused struct of_device_id omap_32ktimer_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_32ktimer_dt_ids);
-static struct driver_d omap_32ktimer_driver = {
+static struct driver omap_32ktimer_driver = {
.name = "omap-32ktimer",
.probe = omap_32ktimer_probe,
.of_compatible = DRV_OF_COMPAT(omap_32ktimer_dt_ids),
diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index cdd297f10c..8473cf733d 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -21,8 +21,8 @@
#include <clock.h>
#include <init.h>
#include <io.h>
-#include <mach/am33xx-silicon.h>
-#include <mach/am33xx-clock.h>
+#include <mach/omap/am33xx-silicon.h>
+#include <mach/omap/am33xx-clock.h>
#include <stdio.h>
@@ -65,7 +65,7 @@ static struct clocksource dmtimer_cs = {
.priority = 70,
};
-static int omap_dmtimer_probe(struct device_d *dev)
+static int omap_dmtimer_probe(struct device *dev)
{
struct resource *iores;
u64 clk_speed;
@@ -96,8 +96,9 @@ static __maybe_unused struct of_device_id omap_dmtimer_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_dmtimer_dt_ids);
-static struct driver_d omap_dmtimer_driver = {
+static struct driver omap_dmtimer_driver = {
.name = "omap-dmtimer",
.probe = omap_dmtimer_probe,
.of_compatible = DRV_OF_COMPAT(omap_dmtimer_dt_ids),
diff --git a/drivers/clocksource/uemd.c b/drivers/clocksource/uemd.c
index a763eadc0c..283fb6d4aa 100644
--- a/drivers/clocksource/uemd.c
+++ b/drivers/clocksource/uemd.c
@@ -55,7 +55,7 @@ static struct clocksource uemd_cs = {
.priority = 60,
};
-static int uemd_timer_probe(struct device_d *dev)
+static int uemd_timer_probe(struct device *dev)
{
struct resource *iores;
int mode;
@@ -108,8 +108,9 @@ static __maybe_unused struct of_device_id uemd_timer_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, uemd_timer_dt_ids);
-static struct driver_d uemd_timer_driver = {
+static struct driver uemd_timer_driver = {
.probe = uemd_timer_probe,
.name = "uemd-timer",
.of_compatible = DRV_OF_COMPAT(uemd_timer_dt_ids),
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 6b7966e5c2..594c791273 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -1,11 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
-menuconfig CRYPTO_HW
- bool "Hardware crypto devices"
-
-if CRYPTO_HW
+menu "Hardware crypto devices"
source "drivers/crypto/caam/Kconfig"
source "drivers/crypto/imx-scc/Kconfig"
-endif
+endmenu
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 475dcf2def..8b600b8d40 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/
+obj-y += caam/
obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += imx-scc/
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index 0d2554e586..e7f57708f3 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -35,3 +35,6 @@ config CRYPTO_DEV_FSL_CAAM_RNG
help
Selecting this will register the SEC4 hardware rng.
+config FSL_CAAM_RNG_PBL_INIT
+ bool "Setup CAAM in EL3"
+ depends on ARCH_IMX8M
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 2d5079b4a5..5ab7892d95 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += ctrl.o error.o jr.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG) += caamrng.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += rng_self_test.o
obj-$(CONFIG_BLOBGEN) += caam-blobgen.o
+pbl-$(CONFIG_FSL_CAAM_RNG_PBL_INIT) += pbl-init.o
diff --git a/drivers/crypto/caam/caam-blobgen.c b/drivers/crypto/caam/caam-blobgen.c
index be4696f58c..1ce636a716 100644
--- a/drivers/crypto/caam/caam-blobgen.c
+++ b/drivers/crypto/caam/caam-blobgen.c
@@ -88,7 +88,7 @@ static void jr_jobdesc_blob_encap(struct blob_priv *ctx, u8 modlen, u16 input_si
append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB);
}
-static void blob_job_done(struct device_d *dev, u32 *desc, u32 err, void *arg)
+static void blob_job_done(struct device *dev, u32 *desc, u32 err, void *arg)
{
struct blob_job_result *res = arg;
@@ -106,7 +106,7 @@ static int caam_blob_decrypt(struct blobgen *bg, const char *modifier,
int *plainsize)
{
struct blob_priv *ctx = to_blob_priv(bg);
- struct device_d *jrdev = bg->dev.parent;
+ struct device *jrdev = bg->dev.parent;
struct blob_job_result testres;
int modifier_len = strlen(modifier);
u32 *desc = ctx->desc;
@@ -129,14 +129,14 @@ static int caam_blob_decrypt(struct blobgen *bg, const char *modifier,
jr_jobdesc_blob_decap(ctx, modifier_len, blobsize);
- dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ dma_sync_single_for_device(jrdev, (unsigned long)desc, desc_bytes(desc),
DMA_TO_DEVICE);
- dma_sync_single_for_device((unsigned long)modifier, modifier_len,
+ dma_sync_single_for_device(jrdev, (unsigned long)modifier, modifier_len,
DMA_TO_DEVICE);
- dma_sync_single_for_device((unsigned long)*plain, *plainsize,
+ dma_sync_single_for_device(jrdev, (unsigned long)*plain, *plainsize,
DMA_FROM_DEVICE);
- dma_sync_single_for_device((unsigned long)blob, blobsize,
+ dma_sync_single_for_device(jrdev, (unsigned long)blob, blobsize,
DMA_TO_DEVICE);
testres.err = 0;
@@ -147,11 +147,11 @@ static int caam_blob_decrypt(struct blobgen *bg, const char *modifier,
ret = testres.err;
- dma_sync_single_for_cpu((unsigned long)modifier, modifier_len,
+ dma_sync_single_for_cpu(jrdev, (unsigned long)modifier, modifier_len,
DMA_TO_DEVICE);
- dma_sync_single_for_cpu((unsigned long)*plain, *plainsize,
+ dma_sync_single_for_cpu(jrdev, (unsigned long)*plain, *plainsize,
DMA_FROM_DEVICE);
- dma_sync_single_for_cpu((unsigned long)blob, blobsize,
+ dma_sync_single_for_cpu(jrdev, (unsigned long)blob, blobsize,
DMA_TO_DEVICE);
return ret;
@@ -162,7 +162,7 @@ static int caam_blob_encrypt(struct blobgen *bg, const char *modifier,
int *blobsize)
{
struct blob_priv *ctx = to_blob_priv(bg);
- struct device_d *jrdev = bg->dev.parent;
+ struct device *jrdev = bg->dev.parent;
struct blob_job_result testres;
int modifier_len = strlen(modifier);
u32 *desc = ctx->desc;
@@ -178,14 +178,14 @@ static int caam_blob_encrypt(struct blobgen *bg, const char *modifier,
jr_jobdesc_blob_encap(ctx, modifier_len, plainsize);
- dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ dma_sync_single_for_device(jrdev, (unsigned long)desc, desc_bytes(desc),
DMA_TO_DEVICE);
- dma_sync_single_for_device((unsigned long)modifier, modifier_len,
+ dma_sync_single_for_device(jrdev, (unsigned long)modifier, modifier_len,
DMA_TO_DEVICE);
- dma_sync_single_for_device((unsigned long)plain, plainsize,
+ dma_sync_single_for_device(jrdev, (unsigned long)plain, plainsize,
DMA_TO_DEVICE);
- dma_sync_single_for_device((unsigned long)blob, *blobsize,
+ dma_sync_single_for_device(jrdev, (unsigned long)blob, *blobsize,
DMA_FROM_DEVICE);
testres.err = 0;
@@ -196,17 +196,17 @@ static int caam_blob_encrypt(struct blobgen *bg, const char *modifier,
ret = testres.err;
- dma_sync_single_for_cpu((unsigned long)modifier, modifier_len,
+ dma_sync_single_for_cpu(jrdev, (unsigned long)modifier, modifier_len,
DMA_TO_DEVICE);
- dma_sync_single_for_cpu((unsigned long)plain, plainsize,
+ dma_sync_single_for_cpu(jrdev, (unsigned long)plain, plainsize,
DMA_TO_DEVICE);
- dma_sync_single_for_cpu((unsigned long)blob, *blobsize,
+ dma_sync_single_for_cpu(jrdev, (unsigned long)blob, *blobsize,
DMA_FROM_DEVICE);
return ret;
}
-int caam_blob_gen_probe(struct device_d *dev, struct device_d *jrdev)
+int caam_blob_gen_probe(struct device *dev, struct device *jrdev)
{
struct blob_priv *ctx;
struct blobgen *bg;
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index 3283e5f321..ea154913ca 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -67,7 +67,7 @@ struct buf_data {
/* rng per-device context */
struct caam_rng_ctx {
- struct device_d *jrdev;
+ struct device *jrdev;
dma_addr_t sh_desc_dma;
u32 sh_desc[DESC_RNG_LEN];
unsigned int cur_buf_idx;
@@ -78,7 +78,7 @@ struct caam_rng_ctx {
static struct caam_rng_ctx *rng_ctx;
-static void rng_done(struct device_d *jrdev, u32 *desc, u32 err, void *context)
+static void rng_done(struct device *jrdev, u32 *desc, u32 err, void *context)
{
struct buf_data *bd;
@@ -90,22 +90,19 @@ static void rng_done(struct device_d *jrdev, u32 *desc, u32 err, void *context)
bd->empty = BUF_NOT_EMPTY;
/* Buffer refilled, invalidate cache */
- dma_sync_single_for_cpu(bd->addr, RN_BUF_SIZE, DMA_FROM_DEVICE);
-
- print_hex_dump_debug("rng refreshed buf@: ", DUMP_PREFIX_OFFSET,
- 16, 4, bd->buf, RN_BUF_SIZE, 1);
+ dma_sync_single_for_cpu(jrdev, bd->addr, RN_BUF_SIZE, DMA_FROM_DEVICE);
}
static inline int submit_job(struct caam_rng_ctx *ctx, int to_current)
{
struct buf_data *bd = &ctx->bufs[!(to_current ^ ctx->current_buf)];
- struct device_d *jrdev = ctx->jrdev;
+ struct device *jrdev = ctx->jrdev;
u32 *desc = bd->hw_desc;
int err;
dev_dbg(jrdev, "submitting job %d\n", !(to_current ^ ctx->current_buf));
- dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ dma_sync_single_for_device(jrdev, (unsigned long)desc, desc_bytes(desc),
DMA_TO_DEVICE);
err = caam_jr_enqueue(jrdev, desc, rng_done, ctx);
@@ -183,7 +180,7 @@ static inline int rng_create_sh_desc(struct caam_rng_ctx *ctx)
ctx->sh_desc_dma = (dma_addr_t)desc;
- dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ dma_sync_single_for_device(ctx->jrdev, (unsigned long)desc, desc_bytes(desc),
DMA_TO_DEVICE);
print_hex_dump_debug("rng shdesc@: ", DUMP_PREFIX_OFFSET, 16, 4,
@@ -222,7 +219,7 @@ static int caam_init_buf(struct caam_rng_ctx *ctx, int buf_id)
return submit_job(ctx, buf_id == ctx->current_buf);
}
-static int caam_init_rng(struct caam_rng_ctx *ctx, struct device_d *jrdev)
+static int caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev)
{
int err;
@@ -242,7 +239,7 @@ static int caam_init_rng(struct caam_rng_ctx *ctx, struct device_d *jrdev)
return caam_init_buf(ctx, 1);
}
-int caam_rng_probe(struct device_d *dev, struct device_d *jrdev)
+int caam_rng_probe(struct device *dev, struct device *jrdev)
{
int err;
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index e271051575..24a01ca094 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -26,6 +26,12 @@
bool caam_little_end;
EXPORT_SYMBOL(caam_little_end);
+bool caam_imx = true;
+EXPORT_SYMBOL(caam_imx);
+
+size_t caam_ptr_sz = 4;
+EXPORT_SYMBOL(caam_ptr_sz);
+
/*
* Descriptor to instantiate RNG State Handle 0 in normal mode and
* load the JDKEK, TDKEK and TDSK registers
@@ -75,7 +81,7 @@ static void build_instantiation_desc(u32 *desc, int handle, int do_sk)
* - -ENODEV if the DECO couldn't be acquired
* - -EAGAIN if an error occurred while executing the descriptor
*/
-static inline int run_descriptor_deco0(struct device_d *ctrldev, u32 *desc,
+static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc,
u32 *status)
{
struct caam_drv_private *ctrlpriv = ctrldev->priv;
@@ -171,7 +177,7 @@ static inline int run_descriptor_deco0(struct device_d *ctrldev, u32 *desc,
* f.i. there was a RNG hardware error due to not "good enough"
* entropy being aquired.
*/
-static int instantiate_rng(struct device_d *ctrldev, int state_handle_mask,
+static int instantiate_rng(struct device *ctrldev, int state_handle_mask,
int gen_sk)
{
struct caam_drv_private *ctrlpriv = ctrldev->priv;
@@ -222,7 +228,7 @@ static int instantiate_rng(struct device_d *ctrldev, int state_handle_mask,
return ret;
}
-static void caam_remove(struct device_d *dev)
+static void caam_remove(struct device *dev)
{
struct caam_drv_private *ctrlpriv = dev->priv;
@@ -241,7 +247,7 @@ static void caam_remove(struct device_d *dev)
* @pdev - pointer to the platform device
* @ent_delay - Defines the length (in system clocks) of each entropy sample.
*/
-static void kick_trng(struct device_d *ctrldev, int ent_delay)
+static void kick_trng(struct device *ctrldev, int ent_delay)
{
struct caam_drv_private *ctrlpriv = ctrldev->priv;
struct caam_ctrl __iomem *ctrl;
@@ -350,7 +356,7 @@ static int caam_get_era(struct caam_ctrl __iomem *ctrl)
}
/* Probe routine for CAAM top (controller) level */
-static int caam_probe(struct device_d *dev)
+static int caam_probe(struct device *dev)
{
int ret, ring, rspec, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN;
u64 caam_id;
@@ -366,7 +372,7 @@ static int caam_probe(struct device_d *dev)
dev->priv = ctrlpriv;
ctrlpriv->pdev = dev;
- nprop = dev->device_node;
+ nprop = dev->of_node;
ctrlpriv->caam_ipg = clk_get(dev, "ipg");
if (IS_ERR(ctrlpriv->caam_ipg)) {
@@ -525,14 +531,14 @@ static int caam_probe(struct device_d *dev)
of_device_is_compatible(np, "fsl,sec4.0-job-ring"))
rspec++;
- ctrlpriv->jrpdev = xzalloc(sizeof(struct device_d *) * rspec);
+ ctrlpriv->jrpdev = xzalloc(sizeof(struct device *) * rspec);
ring = 0;
ctrlpriv->total_jobrs = 0;
for_each_available_child_of_node(nprop, np) {
if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") ||
of_device_is_compatible(np, "fsl,sec4.0-job-ring")) {
- struct device_d *jrdev;
+ struct device *jrdev;
jrdev = of_platform_device_create(np, dev);
if (!jrdev)
@@ -689,8 +695,9 @@ static __maybe_unused struct of_device_id caam_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, caam_match);
-static struct driver_d caam_driver = {
+static struct driver caam_driver = {
.name = "caam",
.probe = caam_probe,
.of_compatible = DRV_OF_COMPAT(caam_match),
diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h
index a7966a8781..1e68bc4f0b 100644
--- a/drivers/crypto/caam/desc.h
+++ b/drivers/crypto/caam/desc.h
@@ -36,26 +36,26 @@
#define CMD_SHIFT 27
#define CMD_MASK 0xf8000000
-#define CMD_KEY (0x00 << CMD_SHIFT)
-#define CMD_SEQ_KEY (0x01 << CMD_SHIFT)
-#define CMD_LOAD (0x02 << CMD_SHIFT)
-#define CMD_SEQ_LOAD (0x03 << CMD_SHIFT)
-#define CMD_FIFO_LOAD (0x04 << CMD_SHIFT)
-#define CMD_SEQ_FIFO_LOAD (0x05 << CMD_SHIFT)
-#define CMD_STORE (0x0a << CMD_SHIFT)
-#define CMD_SEQ_STORE (0x0b << CMD_SHIFT)
-#define CMD_FIFO_STORE (0x0c << CMD_SHIFT)
-#define CMD_SEQ_FIFO_STORE (0x0d << CMD_SHIFT)
-#define CMD_MOVE_LEN (0x0e << CMD_SHIFT)
-#define CMD_MOVE (0x0f << CMD_SHIFT)
-#define CMD_OPERATION (0x10 << CMD_SHIFT)
-#define CMD_SIGNATURE (0x12 << CMD_SHIFT)
-#define CMD_JUMP (0x14 << CMD_SHIFT)
-#define CMD_MATH (0x15 << CMD_SHIFT)
-#define CMD_DESC_HDR (0x16 << CMD_SHIFT)
-#define CMD_SHARED_DESC_HDR (0x17 << CMD_SHIFT)
-#define CMD_SEQ_IN_PTR (0x1e << CMD_SHIFT)
-#define CMD_SEQ_OUT_PTR (0x1f << CMD_SHIFT)
+#define CMD_KEY (0x00u << CMD_SHIFT)
+#define CMD_SEQ_KEY (0x01u << CMD_SHIFT)
+#define CMD_LOAD (0x02u << CMD_SHIFT)
+#define CMD_SEQ_LOAD (0x03u << CMD_SHIFT)
+#define CMD_FIFO_LOAD (0x04u << CMD_SHIFT)
+#define CMD_SEQ_FIFO_LOAD (0x05u << CMD_SHIFT)
+#define CMD_STORE (0x0au << CMD_SHIFT)
+#define CMD_SEQ_STORE (0x0bu << CMD_SHIFT)
+#define CMD_FIFO_STORE (0x0cu << CMD_SHIFT)
+#define CMD_SEQ_FIFO_STORE (0x0du << CMD_SHIFT)
+#define CMD_MOVE_LEN (0x0eu << CMD_SHIFT)
+#define CMD_MOVE (0x0fu << CMD_SHIFT)
+#define CMD_OPERATION (0x10u << CMD_SHIFT)
+#define CMD_SIGNATURE (0x12u << CMD_SHIFT)
+#define CMD_JUMP (0x14u << CMD_SHIFT)
+#define CMD_MATH (0x15u << CMD_SHIFT)
+#define CMD_DESC_HDR (0x16u << CMD_SHIFT)
+#define CMD_SHARED_DESC_HDR (0x17u << CMD_SHIFT)
+#define CMD_SEQ_IN_PTR (0x1eu << CMD_SHIFT)
+#define CMD_SEQ_OUT_PTR (0x1fu << CMD_SHIFT)
/* General-purpose class selector for all commands */
#define CLASS_SHIFT 25
@@ -1182,6 +1182,7 @@
/* RNG4 AAI set */
#define OP_ALG_AAI_RNG4_SH_0 (0x00 << OP_ALG_AAI_SHIFT)
#define OP_ALG_AAI_RNG4_SH_1 (0x01 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG4_SH_MASK (0x03 << OP_ALG_AAI_SHIFT)
#define OP_ALG_AAI_RNG4_PS (0x40 << OP_ALG_AAI_SHIFT)
#define OP_ALG_AAI_RNG4_AI (0x80 << OP_ALG_AAI_SHIFT)
#define OP_ALG_AAI_RNG4_SK (0x100 << OP_ALG_AAI_SHIFT)
@@ -1218,6 +1219,8 @@
#define OP_ALG_ICV_OFF (0 << OP_ALG_ICV_SHIFT)
#define OP_ALG_ICV_ON (1 << OP_ALG_ICV_SHIFT)
+#define OP_ALG_PR_ON BIT(1)
+
#define OP_ALG_DIR_SHIFT 0
#define OP_ALG_DIR_MASK 1
#define OP_ALG_DECRYPT 0
diff --git a/drivers/crypto/caam/detect.h b/drivers/crypto/caam/detect.h
new file mode 100644
index 0000000000..f621ce91e9
--- /dev/null
+++ b/drivers/crypto/caam/detect.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+#ifndef __CAAM_DETECT_H__
+#define __CAAM_DETECT_H__
+
+#include "regs.h"
+
+static inline int caam_is_64bit(struct caam_ctrl __iomem *ctrl)
+{
+ return (rd_reg32(&ctrl->perfmon.comp_parms_ms) & CTPR_MS_PS) &&
+ (rd_reg32(&ctrl->mcr) & MCFGR_LONG_PTR);
+}
+
+static inline bool caam_is_big_endian(struct caam_ctrl *ctrl)
+{
+ return rd_reg32(&ctrl->perfmon.status) & (CSTA_PLEND | CSTA_ALT_PLEND);
+}
+
+#endif
diff --git a/drivers/crypto/caam/error.c b/drivers/crypto/caam/error.c
index b737e5b0a9..de7a64b630 100644
--- a/drivers/crypto/caam/error.c
+++ b/drivers/crypto/caam/error.c
@@ -115,14 +115,14 @@ static const char * const rng_err_id_list[] = {
"Secure key generation",
};
-static void report_invalid_status(struct device_d *jrdev, const u32 status,
+static void report_invalid_status(struct device *jrdev, const u32 status,
const char *error)
{
dev_err(jrdev, "%08x: %s: %s() not implemented\n",
status, error, __func__);
}
-static void report_ccb_status(struct device_d *jrdev, const u32 status,
+static void report_ccb_status(struct device *jrdev, const u32 status,
const char *error)
{
u8 cha_id = (status & JRSTA_CCBERR_CHAID_MASK) >>
@@ -166,14 +166,14 @@ static void report_ccb_status(struct device_d *jrdev, const u32 status,
err_str, err_err_code);
}
-static void report_jump_status(struct device_d *jrdev, const u32 status,
+static void report_jump_status(struct device *jrdev, const u32 status,
const char *error)
{
dev_err(jrdev, "%08x: %s: %s() not implemented\n",
status, error, __func__);
}
-static void report_deco_status(struct device_d *jrdev, const u32 status,
+static void report_deco_status(struct device *jrdev, const u32 status,
const char *error)
{
u8 err_id = status & JRSTA_DECOERR_ERROR_MASK;
@@ -202,24 +202,24 @@ static void report_deco_status(struct device_d *jrdev, const u32 status,
status, error, idx_str, idx, err_str, err_err_code);
}
-static void report_jr_status(struct device_d *jrdev, const u32 status,
+static void report_jr_status(struct device *jrdev, const u32 status,
const char *error)
{
dev_err(jrdev, "%08x: %s: %s() not implemented\n",
status, error, __func__);
}
-static void report_cond_code_status(struct device_d *jrdev, const u32 status,
+static void report_cond_code_status(struct device *jrdev, const u32 status,
const char *error)
{
dev_err(jrdev, "%08x: %s: %s() not implemented\n",
status, error, __func__);
}
-void caam_jr_strstatus(struct device_d *jrdev, u32 status)
+void caam_jr_strstatus(struct device *jrdev, u32 status)
{
static const struct stat_src {
- void (*report_ssed)(struct device_d *jrdev, const u32 status,
+ void (*report_ssed)(struct device *jrdev, const u32 status,
const char *error);
const char *error;
} status_src[16] = {
diff --git a/drivers/crypto/caam/error.h b/drivers/crypto/caam/error.h
index faaf62aec8..9f164cb92c 100644
--- a/drivers/crypto/caam/error.h
+++ b/drivers/crypto/caam/error.h
@@ -8,5 +8,5 @@
#ifndef CAAM_ERROR_H
#define CAAM_ERROR_H
#define CAAM_ERROR_STR_MAX 302
-void caam_jr_strstatus(struct device_d *jrdev, u32 status);
+void caam_jr_strstatus(struct device *jrdev, u32 status);
#endif /* CAAM_ERROR_H */
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index 5064974ff3..3d13fa8f02 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -18,7 +18,7 @@
* Each entry on an output ring needs one of these
*/
struct caam_jrentry_info {
- void (*callbk)(struct device_d *dev, u32 *desc, u32 status, void *arg);
+ void (*callbk)(struct device *dev, u32 *desc, u32 status, void *arg);
void *cbkarg; /* Argument per ring entry */
u32 *desc_addr_virt; /* Stored virt addr for postprocessing */
dma_addr_t desc_addr_dma; /* Stored bus addr for done matching */
@@ -28,7 +28,7 @@ struct caam_jrentry_info {
/* Private sub-storage for a single JobR */
struct caam_drv_private_jr {
struct list_head list_node; /* Job Ring device list */
- struct device_d *dev;
+ struct device *dev;
int ridx;
struct caam_job_ring __iomem *rregs; /* JobR's register space */
int irq; /* One per queue */
@@ -54,8 +54,8 @@ struct caam_drv_private_jr {
*/
struct caam_drv_private {
- struct device_d **jrpdev; /* Alloc'ed array per sub-device */
- struct device_d *pdev;
+ struct device **jrpdev; /* Alloc'ed array per sub-device */
+ struct device *pdev;
/* Physical-presence section */
struct caam_ctrl __iomem *ctrl; /* controller region */
@@ -88,7 +88,7 @@ struct caam_drv_private {
struct clk *caam_emi_slow;
};
-int caam_rng_probe(struct device_d *dev, struct device_d *jrdev);
-int caam_blob_gen_probe(struct device_d *dev, struct device_d *jrdev);
-int caam_jr_probe(struct device_d *dev);
+int caam_rng_probe(struct device *dev, struct device *jrdev);
+int caam_blob_gen_probe(struct device *dev, struct device *jrdev);
+int caam_jr_probe(struct device *dev);
#endif /* INTERN_H */
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 6c48679147..b5d70b24b3 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -22,7 +22,7 @@
#include "desc.h"
#include "intern.h"
-static int caam_reset_hw_jr(struct device_d *dev)
+static int caam_reset_hw_jr(struct device *dev)
{
struct caam_drv_private_jr *jrp = dev->priv;
uint64_t start;
@@ -59,7 +59,7 @@ static int caam_reset_hw_jr(struct device_d *dev)
static int caam_jr_dequeue(struct caam_drv_private_jr *jrp)
{
int hw_idx, sw_idx, i, head, tail;
- void (*usercall)(struct device_d *dev, u32 *desc, u32 status, void *arg);
+ void (*usercall)(struct device *dev, u32 *desc, u32 status, void *arg);
u32 *userdesc, userstatus;
void *userarg;
int found;
@@ -183,8 +183,8 @@ static int caam_jr_interrupt(struct caam_drv_private_jr *jrp)
* @areq: optional pointer to a user argument for use at callback
* time.
**/
-int caam_jr_enqueue(struct device_d *dev, u32 *desc,
- void (*cbk)(struct device_d *dev, u32 *desc,
+int caam_jr_enqueue(struct device *dev, u32 *desc,
+ void (*cbk)(struct device *dev, u32 *desc,
u32 status, void *areq),
void *areq)
{
@@ -207,7 +207,7 @@ int caam_jr_enqueue(struct device_d *dev, u32 *desc,
}
head_entry = &jrp->entinfo[head];
- head_entry->desc_addr_virt = phys_to_virt((u32) desc);
+ head_entry->desc_addr_virt = desc;
head_entry->desc_size = desc_size;
head_entry->callbk = (void *)cbk;
head_entry->cbkarg = areq;
@@ -237,7 +237,7 @@ EXPORT_SYMBOL(caam_jr_enqueue);
/*
* Init JobR independent of platform property detection
*/
-static int caam_jr_init(struct device_d *dev)
+static int caam_jr_init(struct device *dev)
{
struct caam_drv_private_jr *jrp;
dma_addr_t dma_inpring;
@@ -287,7 +287,7 @@ static int caam_jr_init(struct device_d *dev)
/*
* Probe routine for each detected JobR subsystem.
*/
-int caam_jr_probe(struct device_d *dev)
+int caam_jr_probe(struct device *dev)
{
struct caam_job_ring __iomem *ctrl;
struct caam_drv_private_jr *jrpriv;
diff --git a/drivers/crypto/caam/jr.h b/drivers/crypto/caam/jr.h
index 684511affe..60f221f948 100644
--- a/drivers/crypto/caam/jr.h
+++ b/drivers/crypto/caam/jr.h
@@ -9,8 +9,8 @@
#define JR_H
/* Prototypes for backend-level services exposed to APIs */
-int caam_jr_enqueue(struct device_d *dev, u32 *desc,
- void (*cbk)(struct device_d *dev, u32 *desc, u32 status,
+int caam_jr_enqueue(struct device *dev, u32 *desc,
+ void (*cbk)(struct device *dev, u32 *desc, u32 status,
void *areq),
void *areq);
diff --git a/drivers/crypto/caam/pbl-init.c b/drivers/crypto/caam/pbl-init.c
new file mode 100644
index 0000000000..08fad4525a
--- /dev/null
+++ b/drivers/crypto/caam/pbl-init.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2012-2016, Freescale Semiconductor, Inc.
+//
+// Best practice is to load OP-TEE early within prebootloader and
+// run most of barebox in the normal world. OP-TEE, in at least
+// some versions, relies on barebox however to setup the CAAM RNG.
+// Similiarly, Linux, as of v6.1, can only initialize the CAAM
+// via DECO, but this memory region may be reserved by OP-TEE for
+// its own use. While the latter should be rather fixed by switching
+// Linux to SH use, the former is a strong reason to poke the
+// necessary bits here.
+
+#define pr_fmt(fmt) "caam-pbl-init: " fmt
+
+#include <io.h>
+#include <dma.h>
+#include <linux/printk.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <errno.h>
+#include <pbl.h>
+#include <string.h>
+#include <soc/fsl/caam.h>
+#include <asm/mmu.h>
+
+#include "detect.h"
+#include "regs.h"
+#include "jr.h"
+#include "desc.h"
+#include "desc_constr.h"
+
+#define rd_reg32_poll(addr, val, cond, tries) \
+({ \
+ int __tries = tries, __tmp; \
+ __tmp = read_poll_timeout(rd_reg32, val, (cond) || __tries--, \
+ 0, (addr)); \
+ __tries ? __tmp : -ETIMEDOUT; \
+})
+
+static struct caam_ctrl *caam;
+
+struct jr_data_st {
+ u8 inrings[16];
+ u8 outrings[16];
+ u32 desc[3 * MAX_CAAM_DESCSIZE / sizeof(u32)];
+} __aligned(8);
+
+static struct jr_data_st *g_jrdata;
+
+static void dump_error(void)
+{
+ struct rng4tst __iomem *r4tst = &caam->r4tst[0];
+ int i;
+
+ pr_debug("Dump CAAM Error\n");
+ pr_debug("MCFGR 0x%08x\n", rd_reg32(&caam->mcr));
+ pr_debug("FAR 0x%08x\n", rd_reg32(&caam->perfmon.faultaddr));
+ pr_debug("FAMR 0x%08x\n", rd_reg32(&caam->perfmon.faultliodn));
+ pr_debug("FADR 0x%08x\n", rd_reg32(&caam->perfmon.faultdetail));
+ pr_debug("CSTA 0x%08x\n", rd_reg32(&caam->perfmon.status));
+ pr_debug("RTMCTL 0x%08x\n", rd_reg32(&r4tst->rtmctl));
+ pr_debug("RTSTATUS 0x%08x\n", rd_reg32(&r4tst->rtstatus));
+ pr_debug("RDSTA 0x%08x\n", rd_reg32(&r4tst->rdsta));
+
+ for (i = 0; i < desc_len(g_jrdata->desc); i++)
+ pr_debug("desc[%2d] 0x%08x\n", i, g_jrdata->desc[i]);
+}
+
+#define CAAM_JUMP_OFFSET(x) ((x) & JUMP_OFFSET_MASK)
+
+/* Descriptors to instantiate SH0, SH1, load the keys */
+static const u32 rng_inst_sh0_desc[] = {
+ /* Header, don't setup the size */
+ CMD_DESC_HDR | IMMEDIATE,
+ /* Operation instantiation (sh0) */
+ CMD_OPERATION | OP_ALG_ALGSEL_RNG | OP_ALG_TYPE_CLASS1 | OP_ALG_AAI_RNG4_SH_0
+ | OP_ALG_AS_INIT | OP_ALG_PR_ON,
+};
+
+static const u32 rng_inst_sh1_desc[] = {
+ /* wait for done - Jump to next entry */
+ CMD_JUMP | CLASS_1 | JUMP_TEST_ALL | CAAM_JUMP_OFFSET(1),
+ /* Clear written register (write 1) */
+ CMD_LOAD | LDST_IMM | LDST_SRCDST_WORD_CLRW | sizeof(u32),
+ 0x00000001,
+ /* Operation instantiation (sh1) */
+ CMD_OPERATION | OP_ALG_ALGSEL_RNG | OP_ALG_TYPE_CLASS1 | OP_ALG_AAI_RNG4_SH_1
+ | OP_ALG_AS_INIT | OP_ALG_PR_ON,
+};
+
+static const u32 rng_inst_load_keys[] = {
+ /* wait for done - Jump to next entry */
+ CMD_JUMP | CLASS_1 | JUMP_TEST_ALL | CAAM_JUMP_OFFSET(1),
+ /* Clear written register (write 1) */
+ CMD_LOAD | LDST_IMM | LDST_SRCDST_WORD_CLRW | sizeof(u32),
+ 0x00000001,
+ /* Generate the Key */
+ CMD_OPERATION | OP_ALG_ALGSEL_RNG | OP_ALG_TYPE_CLASS1 | OP_ALG_AAI_RNG4_SK,
+};
+
+static int do_job(struct caam_job_ring __iomem *jr, u32 *desc, u32 *ecode)
+{
+ phys_addr_t p_desc = cpu_to_caam_dma((dma_addr_t)desc);
+ u32 status;
+ int ret = 0;
+
+ if (rd_reg32(&jr->inpring_avail) == 0)
+ return -EBUSY;
+
+ jr_inpentry_set(g_jrdata->inrings, 0, p_desc);
+
+ barrier();
+
+ /* Inform HW that a new JR is available */
+ wr_reg32(&jr->inpring_jobadd, 1);
+ while (rd_reg32(&jr->outring_used) == 0)
+ ;
+
+ if (p_desc == jr_outentry_desc(g_jrdata->outrings, 0)) {
+ status = caam32_to_cpu(jr_outentry_jrstatus(g_jrdata->outrings, 0));
+ if (ecode)
+ *ecode = status;
+ } else {
+ dump_error();
+ ret = -ENODATA;
+ }
+
+ /* Acknowledge interrupt */
+ setbits_le32(&jr->jrintstatus, JRINT_JR_INT);
+ /* Remove the JR from the output list even if no JR caller found */
+ wr_reg32(&jr->outring_rmvd, 1);
+
+ return ret;
+}
+
+static int do_cfg_jrqueue(struct caam_job_ring __iomem *jr)
+{
+ u32 value = 0;
+ phys_addr_t ip_base;
+ phys_addr_t op_base;
+
+ /* Configure the HW Job Rings */
+ ip_base = cpu_to_caam_dma((dma_addr_t)g_jrdata->inrings);
+ op_base = cpu_to_caam_dma((dma_addr_t)g_jrdata->outrings);
+
+ wr_reg64(&jr->inpring_base, ip_base);
+ wr_reg32(&jr->inpring_size, 1);
+
+ wr_reg64(&jr->outring_base, op_base);
+ wr_reg32(&jr->outring_size, 1);
+
+ setbits_le32(&jr->jrintstatus, JRINT_JR_INT);
+
+ /*
+ * Configure interrupts but disable it:
+ * Optimization to generate an interrupt either when there are
+ * half of the job done or when there is a job done and
+ * 10 clock cycles elapse without new job complete
+ */
+ value = 10 << JRCFG_ICTT_SHIFT;
+ value |= 1 << JRCFG_ICDCT_SHIFT;
+ value |= JRCFG_ICEN;
+ value |= JRCFG_IMSK;
+ wr_reg32(&jr->rconfig_lo, value);
+
+ /* Enable deco watchdog */
+ setbits_le32(&caam->mcr, MCFGR_WDENABLE);
+
+ return 0;
+}
+
+static void do_clear_rng_error(struct rng4tst __iomem *r4tst)
+{
+ if (rd_reg32(&r4tst->rtmctl) & (RTMCTL_ERR | RTMCTL_FCT_FAIL)) {
+ setbits_le32(&r4tst->rtmctl, RTMCTL_ERR);
+ (void)rd_reg32(&r4tst->rtmctl);
+ }
+}
+
+static void do_inst_desc(u32 *desc, u32 status)
+{
+ u32 *pdesc = desc;
+ u8 desc_len;
+ bool add_sh0 = false;
+ bool add_sh1 = false;
+ bool load_keys = false;
+
+ /*
+ * Modify the the descriptor to remove if necessary:
+ * - The key loading
+ * - One of the SH already instantiated
+ */
+ desc_len = sizeof(rng_inst_sh0_desc);
+ if ((status & RDSTA_IF0) != RDSTA_IF0)
+ add_sh0 = true;
+
+ if ((status & RDSTA_IF1) != RDSTA_IF1) {
+ add_sh1 = true;
+ if (add_sh0)
+ desc_len += sizeof(rng_inst_sh0_desc);
+ }
+
+ if ((status & RDSTA_SKVN) != RDSTA_SKVN) {
+ load_keys = true;
+ desc_len += sizeof(rng_inst_load_keys);
+ }
+
+ /* Copy the SH0 descriptor anyway */
+ memcpy(pdesc, rng_inst_sh0_desc, sizeof(rng_inst_sh0_desc));
+ pdesc += ARRAY_SIZE(rng_inst_sh0_desc);
+
+ if (load_keys) {
+ pr_debug("RNG - Load keys\n");
+ memcpy(pdesc, rng_inst_load_keys, sizeof(rng_inst_load_keys));
+ pdesc += ARRAY_SIZE(rng_inst_load_keys);
+ }
+
+ if (add_sh1) {
+ if (add_sh0) {
+ pr_debug("RNG - Instantiation of SH0 and SH1\n");
+ /* Add the sh1 descriptor */
+ memcpy(pdesc, rng_inst_sh1_desc,
+ sizeof(rng_inst_sh1_desc));
+ } else {
+ pr_debug("RNG - Instantiation of SH1 only\n");
+ /* Modify the SH0 descriptor to instantiate only SH1 */
+ desc[1] &= ~OP_ALG_AAI_RNG4_SH_MASK;
+ desc[1] |= OP_ALG_AAI_RNG4_SH_1;
+ }
+ }
+
+ /* Setup the descriptor size */
+ desc[0] &= ~HDR_DESCLEN_SHR_MASK;
+ desc[0] |= desc_len & HDR_DESCLEN_SHR_MASK;
+}
+
+static void kick_trng(struct rng4tst __iomem *r4tst, u32 ent_delay)
+{
+ u32 samples = 512; /* number of bits to generate and test */
+ u32 mono_min = 195;
+ u32 mono_max = 317;
+ u32 mono_range = mono_max - mono_min;
+ u32 poker_min = 1031;
+ u32 poker_max = 1600;
+ u32 poker_range = poker_max - poker_min + 1;
+ u32 retries = 2;
+ u32 lrun_max = 32;
+ s32 run_1_min = 27;
+ s32 run_1_max = 107;
+ s32 run_1_range = run_1_max - run_1_min;
+ s32 run_2_min = 7;
+ s32 run_2_max = 62;
+ s32 run_2_range = run_2_max - run_2_min;
+ s32 run_3_min = 0;
+ s32 run_3_max = 39;
+ s32 run_3_range = run_3_max - run_3_min;
+ s32 run_4_min = -1;
+ s32 run_4_max = 26;
+ s32 run_4_range = run_4_max - run_4_min;
+ s32 run_5_min = -1;
+ s32 run_5_max = 18;
+ s32 run_5_range = run_5_max - run_5_min;
+ s32 run_6_min = -1;
+ s32 run_6_max = 17;
+ s32 run_6_range = run_6_max - run_6_min;
+ u32 val;
+
+ /* Put RNG in program mode */
+ /* Setting both RTMCTL:PRGM and RTMCTL:TRNG_ACC causes TRNG to
+ * properly invalidate the entropy in the entropy register and
+ * force re-generation.
+ */
+ setbits_le32(&r4tst->rtmctl, RTMCTL_PRGM | RTMCTL_ACC);
+
+ /* Configure the RNG Entropy Delay
+ * Performance-wise, it does not make sense to
+ * set the delay to a value that is lower
+ * than the last one that worked (i.e. the state handles
+ * were instantiated properly. Thus, instead of wasting
+ * time trying to set the values controlling the sample
+ * frequency, the function simply returns.
+ */
+ val = rd_reg32(&r4tst->rtsdctl);
+ if (ent_delay < FIELD_GET(RTSDCTL_ENT_DLY_MASK, val)) {
+ /* Put RNG4 into run mode */
+ clrbits_le32(&r4tst->rtmctl, RTMCTL_PRGM | RTMCTL_ACC);
+ return;
+ }
+
+ val = (ent_delay << RTSDCTL_ENT_DLY_SHIFT) | samples;
+ wr_reg32(&r4tst->rtsdctl, val);
+
+ /* min. freq. count, equal to 1/2 of the entropy sample length */
+ wr_reg32(&r4tst->rtfrqmin, ent_delay >> 1);
+
+ /* max. freq. count, equal to 32 times the entropy sample length */
+ wr_reg32(&r4tst->rtfrqmax, ent_delay << 5);
+
+ wr_reg32(&r4tst->rtscmisc, (retries << 16) | lrun_max);
+ wr_reg32(&r4tst->rtpkrmax, poker_max);
+ wr_reg32(&r4tst->rtpkrrng, poker_range);
+ wr_reg32(&r4tst->rtscml, (mono_range << 16) | mono_max);
+ wr_reg32(&r4tst->rtscr1l, (run_1_range << 16) | run_1_max);
+ wr_reg32(&r4tst->rtscr2l, (run_2_range << 16) | run_2_max);
+ wr_reg32(&r4tst->rtscr3l, (run_3_range << 16) | run_3_max);
+ wr_reg32(&r4tst->rtscr4l, (run_4_range << 16) | run_4_max);
+ wr_reg32(&r4tst->rtscr5l, (run_5_range << 16) | run_5_max);
+ wr_reg32(&r4tst->rtscr6pl, (run_6_range << 16) | run_6_max);
+
+ /*
+ * select raw sampling in both entropy shifter
+ * and statistical checker; ; put RNG4 into run mode
+ */
+ clrsetbits_32(&r4tst->rtmctl, RTMCTL_PRGM | RTMCTL_ACC | RTMCTL_SAMP_MODE_MASK,
+ RTMCTL_SAMP_MODE_RAW_ES_SC);
+
+ /* Clear the ERR bit in RTMCTL if set. The TRNG error can occur when the
+ * RNG clock is not within 1/2x to 8x the system clock.
+ * This error is possible if ROM code does not initialize the system PLLs
+ * immediately after PoR.
+ */
+ /* setbits_le32(&r4tst->rtmctl, RTMCTL_ERR); */
+}
+
+static int do_instantiation(struct caam_job_ring __iomem *jr,
+ struct rng4tst __iomem *r4tst)
+{
+ struct caam_perfmon __iomem *perfmon = &caam->perfmon;
+ int ret;
+ u32 cha_vid_ls, rng_vid;
+ u32 ent_delay;
+ u32 status;
+
+ if (!g_jrdata) {
+ pr_err("descriptor allocation failed\n");
+ return -ENODEV;
+ }
+
+ cha_vid_ls = rd_reg32(&perfmon->cha_id_ls);
+
+ /*
+ * If SEC has RNG version >= 4 and RNG state handle has not been
+ * already instantiated, do RNG instantiation
+ */
+ rng_vid = FIELD_GET(CHAVID_LS_RNGVID_MASK, cha_vid_ls);
+ if (rng_vid < 4) {
+ pr_info("RNG (VID=%u) already instantiated.\n", rng_vid);
+ return 0;
+ }
+
+ ent_delay = RTSDCTL_ENT_DLY_MIN;
+
+ do {
+ /* Read the CAAM RNG status */
+ status = rd_reg32(&r4tst->rdsta);
+
+ if ((status & RDSTA_IF0) != RDSTA_IF0) {
+ /* Configure the RNG entropy delay */
+ kick_trng(r4tst, ent_delay);
+ ent_delay += 400;
+ }
+
+ do_clear_rng_error(r4tst);
+
+ if ((status & (RDSTA_IF0 | RDSTA_IF1)) != (RDSTA_IF0 | RDSTA_IF1)) {
+ do_inst_desc(g_jrdata->desc, status);
+
+ ret = do_job(jr, g_jrdata->desc, NULL);
+ if (ret < 0) {
+ pr_err("RNG Instantiation failed\n");
+ goto end_instantation;
+ }
+ } else {
+ ret = 0;
+ pr_debug("RNG instantiation done (%d)\n", ent_delay);
+ goto end_instantation;
+ }
+ } while (ent_delay < RTSDCTL_ENT_DLY_MAX);
+
+ pr_err("RNG Instantation Failure - Entropy delay (%d)\n", ent_delay);
+ ret = -ETIMEDOUT;
+
+end_instantation:
+ return ret;
+}
+
+static int jr_reset(struct caam_job_ring __iomem *jr)
+{
+ int ret;
+ u32 val;
+
+ /* Mask interrupts to poll for reset completion status */
+ setbits_le32(&jr->rconfig_lo, JRCFG_IMSK);
+
+ /* Initiate flush of all pending jobs (required prior to reset) */
+ wr_reg32(&jr->jrcommand, JRCR_RESET);
+
+ ret = rd_reg32_poll(&jr->jrintstatus, val,
+ val != JRINT_ERR_HALT_INPROGRESS, 10000);
+
+ if (ret || val != JRINT_ERR_HALT_COMPLETE) {
+ pr_err("failed to flush job ring\n");
+ return ret ?: -EIO;
+ }
+
+ /* Initiate reset by setting reset bit a second time */
+ wr_reg32(&jr->jrcommand, JRCR_RESET);
+
+ ret = rd_reg32_poll(&jr->jrcommand, val, !(val & JRCR_RESET), 100);
+ if (ret) {
+ pr_err("failed to reset job ring\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int rng_init(struct caam_job_ring __iomem *jr,
+ struct rng4tst __iomem *r4tst)
+{
+ int ret;
+
+ ret = jr_reset(jr);
+ if (ret)
+ return ret;
+
+ ret = do_instantiation(jr, r4tst);
+ if (ret)
+ return ret;
+
+ jr_reset(jr);
+ return 0;
+}
+
+bool caam_little_end;
+bool caam_imx;
+size_t caam_ptr_sz;
+
+int early_caam_init(struct caam_ctrl __iomem *_caam, bool is_imx)
+{
+ static struct jr_data_st pbl_jrdata;
+ struct caam_job_ring __iomem *jr;
+ struct rng4tst __iomem *r4tst;
+ u32 temp_reg;
+ int ret;
+
+ caam = _caam;
+ caam_imx = is_imx;
+ caam_little_end = !caam_is_big_endian(caam);
+ caam_ptr_sz = caam_is_64bit(caam) ? sizeof(u64) : sizeof(u32);
+
+ /*
+ * PBL will only enable MMU right before unpacking, so all memory
+ * is uncached and thus coherent here
+ */
+ if (IN_PBL)
+ g_jrdata = &pbl_jrdata;
+ else
+ g_jrdata = dma_alloc_coherent(sizeof(*g_jrdata), NULL);
+
+ jr = IOMEM(caam) + 0x1000;
+ r4tst = &caam->r4tst[0];
+
+ pr_debug("Detected %zu-bit %s-endian %sCAAM\n", caam_ptr_sz * 8,
+ caam_little_end ? "little" : "big", caam_imx ? "i.MX " : "");
+
+ /* reset the CAAM */
+ temp_reg = rd_reg32(&caam->mcr) | MCFGR_DMA_RESET | MCFGR_SWRESET;
+ wr_reg32(&caam->mcr, temp_reg);
+
+ while (rd_reg32(&caam->mcr) & MCFGR_DMA_RESET)
+ ;
+
+ jr_reset(jr);
+
+ ret = do_cfg_jrqueue(jr);
+ if (ret) {
+ pr_err("job ring init failed\n");
+ return ret;
+ }
+
+ /* Check if the RNG is already instantiated */
+ temp_reg = rd_reg32(&r4tst->rdsta);
+ if (temp_reg == (RDSTA_IF0 | RDSTA_IF1 | RDSTA_SKVN)) {
+ pr_notice("RNG already instantiated 0x%x\n", temp_reg);
+ return 0;
+ }
+
+ return rng_init(jr, r4tst);
+}
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index f80ece94fd..c2eea8d1a5 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -2,33 +2,93 @@
/*
* CAAM hardware register-level view
*
- * Copyright 2008-2015 Freescale Semiconductor, Inc.
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
*/
#ifndef REGS_H
#define REGS_H
#include <linux/types.h>
+#include <linux/bitops.h>
#include <io.h>
+#include <io-64-nonatomic-hi-lo.h>
-extern bool caam_little_end;
+/*
+ * Architecture-specific register access methods
+ *
+ * CAAM's bus-addressable registers are 64 bits internally.
+ * They have been wired to be safely accessible on 32-bit
+ * architectures, however. Registers were organized such
+ * that (a) they can be contained in 32 bits, (b) if not, then they
+ * can be treated as two 32-bit entities, or finally (c) if they
+ * must be treated as a single 64-bit value, then this can safely
+ * be done with two 32-bit cycles.
+ *
+ * For 32-bit operations on 64-bit values, CAAM follows the same
+ * 64-bit register access conventions as it's predecessors, in that
+ * writes are "triggered" by a write to the register at the numerically
+ * higher address, thus, a full 64-bit write cycle requires a write
+ * to the lower address, followed by a write to the higher address,
+ * which will latch/execute the write cycle.
+ *
+ * For example, let's assume a SW reset of CAAM through the master
+ * configuration register.
+ * - SWRST is in bit 31 of MCFG.
+ * - MCFG begins at base+0x0000.
+ * - Bits 63-32 are a 32-bit word at base+0x0000 (numerically-lower)
+ * - Bits 31-0 are a 32-bit word at base+0x0004 (numerically-higher)
+ *
+ * (and on Power, the convention is 0-31, 32-63, I know...)
+ *
+ * Assuming a 64-bit write to this MCFG to perform a software reset
+ * would then require a write of 0 to base+0x0000, followed by a
+ * write of 0x80000000 to base+0x0004, which would "execute" the
+ * reset.
+ *
+ * Of course, since MCFG 63-32 is all zero, we could cheat and simply
+ * write 0x8000000 to base+0x0004, and the reset would work fine.
+ * However, since CAAM does contain some write-and-read-intended
+ * 64-bit registers, this code defines 64-bit access methods for
+ * the sake of internal consistency and simplicity, and so that a
+ * clean transition to 64-bit is possible when it becomes necessary.
+ *
+ * There are limitations to this that the developer must recognize.
+ * 32-bit architectures cannot enforce an atomic-64 operation,
+ * Therefore:
+ *
+ * - On writes, since the HW is assumed to latch the cycle on the
+ * write of the higher-numeric-address word, then ordered
+ * writes work OK.
+ *
+ * - For reads, where a register contains a relevant value of more
+ * that 32 bits, the hardware employs logic to latch the other
+ * "half" of the data until read, ensuring an accurate value.
+ * This is of particular relevance when dealing with CAAM's
+ * performance counters.
+ *
+ */
-#define caam_to_cpu(len) \
-static inline u##len caam##len ## _to_cpu(u##len val) \
-{ \
- if (caam_little_end) \
- return le##len ## _to_cpu(val); \
- else \
- return be##len ## _to_cpu(val); \
+extern bool caam_little_end;
+extern bool caam_imx;
+extern size_t caam_ptr_sz;
+
+#define caam_to_cpu(len) \
+static inline u##len caam##len ## _to_cpu(u##len val) \
+{ \
+ if (caam_little_end) \
+ return le##len ## _to_cpu((__force __le##len)val); \
+ else \
+ return be##len ## _to_cpu((__force __be##len)val); \
}
-#define cpu_to_caam(len) \
-static inline u##len cpu_to_caam##len(u##len val) \
-{ \
- if (caam_little_end) \
- return cpu_to_le##len(val); \
- else \
- return cpu_to_be##len(val); \
+#define cpu_to_caam(len) \
+static inline u##len cpu_to_caam##len(u##len val) \
+{ \
+ if (caam_little_end) \
+ return (__force u##len)cpu_to_le##len(val); \
+ else \
+ return (__force u##len)cpu_to_be##len(val); \
}
caam_to_cpu(16)
@@ -63,67 +123,95 @@ static inline void clrsetbits_32(void __iomem *reg, u32 clear, u32 set)
}
/*
- * The DMA address registers in the JR are a pair of 32-bit registers.
- * The layout is:
+ * The only users of these wr/rd_reg64 functions is the Job Ring (JR).
+ * The DMA address registers in the JR are handled differently depending on
+ * platform:
+ *
+ * 1. All BE CAAM platforms and i.MX platforms (LE CAAM):
*
* base + 0x0000 : most-significant 32 bits
* base + 0x0004 : least-significant 32 bits
*
* The 32-bit version of this core therefore has to write to base + 0x0004
- * to set the 32-bit wide DMA address. This seems to be independent of the
- * endianness of the written/read data.
+ * to set the 32-bit wide DMA address.
+ *
+ * 2. All other LE CAAM platforms (LS1021A etc.)
+ * base + 0x0000 : least-significant 32 bits
+ * base + 0x0004 : most-significant 32 bits
*/
-
-#ifdef CONFIG_64BIT
static inline void wr_reg64(void __iomem *reg, u64 data)
{
- if (caam_little_end)
- iowrite64(data, reg);
- else
+ if (caam_little_end) {
+ if (caam_imx) {
+ iowrite32(data >> 32, (u32 __iomem *)(reg));
+ iowrite32(data, (u32 __iomem *)(reg) + 1);
+ } else {
+ iowrite64(data, reg);
+ }
+ } else {
iowrite64be(data, reg);
+ }
}
-static inline void rd_reg64(void __iomem *reg)
+static inline u64 rd_reg64(void __iomem *reg)
{
- if (caam_little_end)
- ioread64(reg);
- else
- ioread64be(reg);
+ if (caam_little_end) {
+ if (caam_imx) {
+ u32 low, high;
+
+ high = ioread32(reg);
+ low = ioread32(reg + sizeof(u32));
+
+ return low + ((u64)high << 32);
+ } else {
+ return ioread64(reg);
+ }
+ } else {
+ return ioread64be(reg);
+ }
}
-#else /* CONFIG_64BIT */
-static inline void wr_reg64(void __iomem *reg, u64 data)
+
+static inline u64 cpu_to_caam_dma64(dma_addr_t value)
{
- wr_reg32((u32 __iomem *)(reg), data >> 32);
- wr_reg32((u32 __iomem *)(reg) + 1, data);
+ if (caam_imx) {
+ u64 ret_val = (u64)cpu_to_caam32(lower_32_bits(value)) << 32;
+
+ if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT))
+ ret_val |= (u64)cpu_to_caam32(upper_32_bits(value));
+
+ return ret_val;
+ }
+
+ return cpu_to_caam64(value);
}
-static inline u64 rd_reg64(void __iomem *reg)
+static inline u64 caam_dma64_to_cpu(u64 value)
{
- return ((u64)rd_reg32((u32 __iomem *)(reg)) << 32 |
- (u64)rd_reg32((u32 __iomem *)(reg) + 1));
+ if (caam_imx)
+ return (((u64)caam32_to_cpu(lower_32_bits(value)) << 32) |
+ (u64)caam32_to_cpu(upper_32_bits(value)));
+
+ return caam64_to_cpu(value);
}
-#endif /* CONFIG_64BIT */
-static inline u64 cpu_to_caam_dma64(dma_addr_t value)
+static inline u64 cpu_to_caam_dma(u64 value)
{
- return (((u64)cpu_to_caam32(lower_32_bits(value)) << 32) |
- (u64)cpu_to_caam32(upper_32_bits(value)));
+ if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+ caam_ptr_sz == sizeof(u64))
+ return cpu_to_caam_dma64(value);
+ else
+ return cpu_to_caam32(value);
}
-static inline u64 caam_dma64_to_cpu(u64 value)
+static inline u64 caam_dma_to_cpu(u64 value)
{
- return (((u64)caam32_to_cpu(lower_32_bits(value)) << 32) |
- (u64)caam32_to_cpu(upper_32_bits(value)));
+ if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+ caam_ptr_sz == sizeof(u64))
+ return caam_dma64_to_cpu(value);
+ else
+ return caam32_to_cpu(value);
}
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
-#define cpu_to_caam_dma(value) cpu_to_caam_dma64(value)
-#define caam_dma_to_cpu(value) caam_dma64_to_cpu(value)
-#else
-#define cpu_to_caam_dma(value) cpu_to_caam32(value)
-#define caam_dma_to_cpu(value) caam32_to_cpu(value)
-#endif /* CONFIG_ARCH_DMA_ADDR_T_64BIT */
-
/*
* jr_outentry
* Represents each entry in a JobR output ring
@@ -133,6 +221,66 @@ struct jr_outentry {
u32 jrstatus; /* Status for completed descriptor */
} __packed;
+static inline void jr_outentry_get(void *outring, int hw_idx, dma_addr_t *desc,
+ u32 *jrstatus)
+{
+
+ if (caam_ptr_sz == sizeof(u32)) {
+ struct {
+ u32 desc;
+ u32 jrstatus;
+ } __packed *outentry = outring;
+
+ *desc = outentry[hw_idx].desc;
+ *jrstatus = outentry[hw_idx].jrstatus;
+ } else {
+ struct {
+ dma_addr_t desc;/* Pointer to completed descriptor */
+ u32 jrstatus; /* Status for completed descriptor */
+ } __packed *outentry = outring;
+
+ *desc = outentry[hw_idx].desc;
+ *jrstatus = outentry[hw_idx].jrstatus;
+ }
+}
+
+#define SIZEOF_JR_OUTENTRY (caam_ptr_sz + sizeof(u32))
+
+static inline dma_addr_t jr_outentry_desc(void *outring, int hw_idx)
+{
+ dma_addr_t desc;
+ u32 unused;
+
+ jr_outentry_get(outring, hw_idx, &desc, &unused);
+
+ return desc;
+}
+
+static inline u32 jr_outentry_jrstatus(void *outring, int hw_idx)
+{
+ dma_addr_t unused;
+ u32 jrstatus;
+
+ jr_outentry_get(outring, hw_idx, &unused, &jrstatus);
+
+ return jrstatus;
+}
+
+static inline void jr_inpentry_set(void *inpring, int hw_idx, dma_addr_t val)
+{
+ if (caam_ptr_sz == sizeof(u32)) {
+ u32 *inpentry = inpring;
+
+ inpentry[hw_idx] = val;
+ } else {
+ dma_addr_t *inpentry = inpring;
+
+ inpentry[hw_idx] = val;
+ }
+}
+
+#define SIZEOF_JR_INPENTRY caam_ptr_sz
+
/*
* CHA version ID / instantiation bitfields
* Defined for use within cha_id in perfmon
@@ -283,6 +431,7 @@ struct caam_perfmon {
#define CRNR_LS_RNGRN_SHIFT 16
#define CRNR_LS_RNGRN_MASK (0xfull << CRNR_LS_RNGRN_SHIFT)
u32 cha_rev_ls; /* CRNR - CHA Rev No. Least significant half*/
+#define CTPR_MS_PS BIT(17)
#define CTPR_MS_QI_SHIFT 25
#define CTPR_MS_QI_MASK (0x1ull << CTPR_MS_QI_SHIFT)
#define CTPR_MS_VIRT_EN_INCL 0x00000001
@@ -434,7 +583,10 @@ struct rngtst {
/* RNG4 TRNG test registers */
struct rng4tst {
-#define RTMCTL_PRGM 0x00010000 /* 1 -> program mode, 0 -> run mode */
+#define RTMCTL_ACC BIT(5) /* TRNG access mode */
+#define RTMCTL_FCT_FAIL BIT(8)
+#define RTMCTL_ERR BIT(12)
+#define RTMCTL_PRGM BIT(16) /* 1 -> program mode, 0 -> run mode */
#define RTMCTL_SAMP_MODE_VON_NEUMANN_ES_SC 0 /* use von Neumann data in
both entropy shifter and
statistical checker */
@@ -445,6 +597,7 @@ struct rng4tst {
entropy shifter, raw data
in statistical checker */
#define RTMCTL_SAMP_MODE_INVALID 3 /* invalid combination */
+#define RTMCTL_SAMP_MODE_MASK 3
u32 rtmctl; /* misc. control register */
u32 rtscmisc; /* statistical check misc. register */
u32 rtpkrrng; /* poker range register */
@@ -467,12 +620,23 @@ struct rng4tst {
u32 rtfrqmax; /* PRGM=1: freq. count max. limit register */
u32 rtfrqcnt; /* PRGM=0: freq. count register */
};
- u32 rsvd1[40];
+ u32 rtscml;
+ u32 rtscr1l;
+ u32 rtscr2l;
+ u32 rtscr3l;
+ u32 rtscr4l;
+ u32 rtscr5l;
+ u32 rtscr6pl;
+ u32 rtstatus;
+ u32 rsvd1[32];
#define RDSTA_SKVT 0x80000000
#define RDSTA_SKVN 0x40000000
+#define RDSTA_PR0 BIT(4)
+#define RDSTA_PR1 BIT(5)
#define RDSTA_IF0 0x00000001
#define RDSTA_IF1 0x00000002
#define RDSTA_IFMASK (RDSTA_IF1 | RDSTA_IF0)
+#define RDSTA_MASK (RDSTA_PR1 | RDSTA_PR0 | RDSTA_IF1 | RDSTA_IF0)
u32 rdsta;
u32 rsvd2[15];
};
diff --git a/drivers/crypto/caam/rng_self_test.c b/drivers/crypto/caam/rng_self_test.c
index ed3017d828..b6fcc3bc09 100644
--- a/drivers/crypto/caam/rng_self_test.c
+++ b/drivers/crypto/caam/rng_self_test.c
@@ -116,11 +116,12 @@ static void construct_rng_self_test_jobdesc(u32 *desc, const u32 *rng_st_dsc, u8
}
/* Replace destination address in the descriptor */
- desc[result_addr_idx] = (u32)res_addr;
+ desc[result_addr_idx] = virt_to_phys(res_addr);
}
/* rng_self_test_done() - callback for caam_jr_enqueue */
-static void rng_self_test_done(struct device_d *dev, u32 *desc, u32 err, void *arg)
+static void rng_self_test_done(struct device *dev, u32 *desc, u32 err,
+ void *arg)
{
int * job_err = arg;
*job_err = err;
@@ -145,7 +146,8 @@ static void rng_self_test_done(struct device_d *dev, u32 *desc, u32 err, void *a
* * i.MX67SD silicon revision 1.3
*
*/
-int caam_rng_self_test(struct device_d *dev, const u8 caam_era, const u8 rngvid, const u8 rngrev)
+int caam_rng_self_test(struct device *dev, const u8 caam_era, const u8 rngvid,
+ const u8 rngrev)
{
int ret, desc_size = 0, result_size = 0, job_err = 0;
const u32 *rng_st_dsc;
@@ -184,9 +186,9 @@ int caam_rng_self_test(struct device_d *dev, const u8 caam_era, const u8 rngvid,
construct_rng_self_test_jobdesc(desc, rng_st_dsc, result, desc_size);
- dma_sync_single_for_device((unsigned long)desc,
+ dma_sync_single_for_device(dev, (unsigned long)desc,
desc_size * sizeof(*desc), DMA_TO_DEVICE);
- dma_sync_single_for_device((unsigned long)result,
+ dma_sync_single_for_device(dev, (unsigned long)result,
result_size * sizeof(*result), DMA_FROM_DEVICE);
/* wait for job completion */
@@ -203,7 +205,7 @@ int caam_rng_self_test(struct device_d *dev, const u8 caam_era, const u8 rngvid,
goto err;
}
- dma_sync_single_for_cpu((unsigned long)result, result_size * sizeof(*result),
+ dma_sync_single_for_cpu(dev, (unsigned long)result, result_size * sizeof(*result),
DMA_FROM_DEVICE);
if (memcmp(result, exp_result, sizeof(*result) * result_size) != 0) {
diff --git a/drivers/crypto/caam/rng_self_test.h b/drivers/crypto/caam/rng_self_test.h
index ba688f7e39..1c1011466f 100644
--- a/drivers/crypto/caam/rng_self_test.h
+++ b/drivers/crypto/caam/rng_self_test.h
@@ -9,6 +9,7 @@
#ifndef RNG_SELF_TEST_H
#define RNG_SELF_TEST_H
-int caam_rng_self_test(struct device_d *dev, const u8 caam_era, const u8 rngvid, const u8 rngrev);
+int caam_rng_self_test(struct device *dev, const u8 caam_era, const u8 rngvid,
+ const u8 rngrev);
#endif /* RNG_SELF_TEST_H */
diff --git a/drivers/crypto/imx-scc/Kconfig b/drivers/crypto/imx-scc/Kconfig
index c5b0ac2e83..75038fd2a5 100644
--- a/drivers/crypto/imx-scc/Kconfig
+++ b/drivers/crypto/imx-scc/Kconfig
@@ -2,8 +2,6 @@
config CRYPTO_DEV_MXC_SCC
tristate "Support for Freescale Security Controller (SCC)"
depends on (ARCH_IMX25 || COMPILE_TEST) && OFTREE
- select CRYPTO_BLKCIPHER
- select CRYPTO_DES
help
This option enables support for the Security Controller (SCC)
found in Freescale i.MX25 chips.
diff --git a/drivers/crypto/imx-scc/scc-blobgen.c b/drivers/crypto/imx-scc/scc-blobgen.c
index 7554cbce37..530d0840f8 100644
--- a/drivers/crypto/imx-scc/scc-blobgen.c
+++ b/drivers/crypto/imx-scc/scc-blobgen.c
@@ -137,7 +137,7 @@ out:
return ret;
}
-int imx_scc_blob_gen_probe(struct device_d *dev)
+int imx_scc_blob_gen_probe(struct device *dev)
{
struct blobgen *bg;
int ret;
diff --git a/drivers/crypto/imx-scc/scc.c b/drivers/crypto/imx-scc/scc.c
index f2c004f7fd..fcff7e9e6e 100644
--- a/drivers/crypto/imx-scc/scc.c
+++ b/drivers/crypto/imx-scc/scc.c
@@ -115,7 +115,7 @@
static char scc_block_padding[8] = { 0x80, 0, 0, 0, 0, 0, 0, 0 };
struct imx_scc {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct clk *clk;
struct ablkcipher_request *req;
@@ -228,9 +228,9 @@ static int imx_scc_ablkcipher_next(struct imx_scc_ctx *ctx,
if (err)
return err;
- dev_dbg(scc->dev, "Start encryption (0x%p/0x%p)\n",
- (void *)readl(scc->base + SCC_SCM_RED_START),
- (void *)readl(scc->base + SCC_SCM_BLACK_START));
+ dev_dbg(scc->dev, "Start encryption (0x%x/0x%x)\n",
+ readl(scc->base + SCC_SCM_RED_START),
+ readl(scc->base + SCC_SCM_BLACK_START));
/* clear interrupt control registers */
writel(SCC_SCM_INTR_CTRL_CLR_INTR,
@@ -417,7 +417,7 @@ static int imx_scc_get_state(struct imx_scc *scc)
return ret;
}
-static int imx_scc_probe(struct device_d *dev)
+static int imx_scc_probe(struct device *dev)
{
struct imx_scc *scc;
int ret;
@@ -486,8 +486,9 @@ static __maybe_unused struct of_device_id imx_scc_dt_ids[] = {
{ .compatible = "fsl,imx25-scc", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_scc_dt_ids);
-static struct driver_d imx_scc_driver = {
+static struct driver imx_scc_driver = {
.name = "mxc-scc",
.probe = imx_scc_probe,
.of_compatible = imx_scc_dt_ids,
diff --git a/drivers/crypto/imx-scc/scc.h b/drivers/crypto/imx-scc/scc.h
index 77161d25a4..77333f67c5 100644
--- a/drivers/crypto/imx-scc/scc.h
+++ b/drivers/crypto/imx-scc/scc.h
@@ -7,4 +7,4 @@ struct ablkcipher_request;
int imx_scc_cbc_des_encrypt(struct ablkcipher_request *req);
int imx_scc_cbc_des_decrypt(struct ablkcipher_request *req);
-int imx_scc_blob_gen_probe(struct device_d *dev);
+int imx_scc_blob_gen_probe(struct device *dev);
diff --git a/drivers/ddr/Kconfig b/drivers/ddr/Kconfig
index 17d01ab658..0b0d7a8893 100644
--- a/drivers/ddr/Kconfig
+++ b/drivers/ddr/Kconfig
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/ddr/fsl/Kconfig"
-source "drivers/ddr/imx8m/Kconfig"
+source "drivers/ddr/imx/Kconfig"
diff --git a/drivers/ddr/Makefile b/drivers/ddr/Makefile
index 0b5ac949a4..e5d7bd14db 100644
--- a/drivers/ddr/Makefile
+++ b/drivers/ddr/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DDR_FSL) += fsl/
-obj-$(CONFIG_IMX8M_DRAM) += imx8m/
+obj-$(CONFIG_IMX_DRAM) += imx/
diff --git a/drivers/ddr/fsl/Makefile b/drivers/ddr/fsl/Makefile
index 394ae55383..787b4453f3 100644
--- a/drivers/ddr/fsl/Makefile
+++ b/drivers/ddr/fsl/Makefile
@@ -4,9 +4,5 @@
pbl-y += main.o util.o ctrl_regs.o options.o lc_common_dimm_params.o
-pbl-y += ddr1_dimm_params.o
-pbl-y += ddr2_dimm_params.o
-pbl-y += ddr3_dimm_params.o
-pbl-y += ddr4_dimm_params.o
obj-y += arm_ddr_gen3.o
pbl-y += fsl_ddr_gen4.o
diff --git a/drivers/ddr/fsl/arm_ddr_gen3.c b/drivers/ddr/fsl/arm_ddr_gen3.c
index a8b96f1261..1cbdb1446f 100644
--- a/drivers/ddr/fsl/arm_ddr_gen3.c
+++ b/drivers/ddr/fsl/arm_ddr_gen3.c
@@ -21,7 +21,7 @@
* Dividing the initialization to two steps to deassert DDR reset signal
* to comply with JEDEC specs for RDIMMs.
*/
-void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step)
+void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step, bool little_endian)
{
struct ccsr_ddr __iomem *ddr = c->base;
const fsl_ddr_cfg_regs_t *regs = &c->fsl_ddr_config_reg;
@@ -30,6 +30,11 @@ void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step)
u32 total_gb_size_per_controller;
int timeout;
+ if (little_endian)
+ ddr_endianess = DDR_ENDIANESS_LE;
+ else
+ ddr_endianess = DDR_ENDIANESS_BE;
+
if (step == 2)
goto step2;
diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c
index b0d98a929c..7c882946b9 100644
--- a/drivers/ddr/fsl/ctrl_regs.c
+++ b/drivers/ddr/fsl/ctrl_regs.c
@@ -284,7 +284,7 @@ static void set_timing_cfg_0(struct fsl_ddr_controller *c)
/*
* for single quad-rank DIMM and two-slot DIMMs
* to avoid ODT overlap
- */
+ */
switch (avoid_odt_overlap(c, dimm_params)) {
case 2:
twrt_mclk = 2;
diff --git a/drivers/ddr/fsl/ddr1_dimm_params.c b/drivers/ddr/fsl/ddr1_dimm_params.c
deleted file mode 100644
index f5f9067073..0000000000
--- a/drivers/ddr/fsl/ddr1_dimm_params.c
+++ /dev/null
@@ -1,319 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2008 Freescale Semiconductor, Inc.
- */
-#include <common.h>
-#include <soc/fsl/fsl_ddr_sdram.h>
-#include <linux/log2.h>
-#include "fsl_ddr.h"
-
-/*
- * Calculate the Density of each Physical Rank.
- * Returned size is in bytes.
- *
- * Study these table from Byte 31 of JEDEC SPD Spec.
- *
- * DDR I DDR II
- * Bit Size Size
- * --- ----- ------
- * 7 high 512MB 512MB
- * 6 256MB 256MB
- * 5 128MB 128MB
- * 4 64MB 16GB
- * 3 32MB 8GB
- * 2 16MB 4GB
- * 1 2GB 2GB
- * 0 low 1GB 1GB
- *
- * Reorder Table to be linear by stripping the bottom
- * 2 or 5 bits off and shifting them up to the top.
- */
-
-static unsigned long long
-compute_ranksize(unsigned int mem_type, unsigned char row_dens)
-{
- unsigned long long bsize;
-
- /* Bottom 2 bits up to the top. */
- bsize = ((row_dens >> 2) | ((row_dens & 3) << 6));
- bsize <<= 24ULL;
- debug("DDR: DDR I rank density = 0x%16llx\n", bsize);
-
- return bsize;
-}
-
-/*
- * Convert a two-nibble BCD value into a cycle time.
- * While the spec calls for nano-seconds, picos are returned.
- *
- * This implements the tables for bytes 9, 23 and 25 for both
- * DDR I and II. No allowance for distinguishing the invalid
- * fields absent for DDR I yet present in DDR II is made.
- * (That is, cycle times of .25, .33, .66 and .75 ns are
- * allowed for both DDR II and I.)
- */
-static unsigned int
-convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val)
-{
- /* Table look up the lower nibble, allow DDR I & II. */
- unsigned int tenths_ps[16] = {
- 0,
- 100,
- 200,
- 300,
- 400,
- 500,
- 600,
- 700,
- 800,
- 900,
- 250, /* This and the next 3 entries valid ... */
- 330, /* ... only for tCK calculations. */
- 660,
- 750,
- 0, /* undefined */
- 0 /* undefined */
- };
-
- unsigned int whole_ns = (spd_val & 0xF0) >> 4;
- unsigned int tenth_ns = spd_val & 0x0F;
- unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns];
-
- return ps;
-}
-
-static unsigned int
-convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val)
-{
- unsigned int tenth_ns = (spd_val & 0xF0) >> 4;
- unsigned int hundredth_ns = spd_val & 0x0F;
- unsigned int ps = tenth_ns * 100 + hundredth_ns * 10;
-
- return ps;
-}
-
-static unsigned int byte40_table_ps[8] = {
- 0,
- 250,
- 330,
- 500,
- 660,
- 750,
- 0, /* supposed to be RFC, but not sure what that means */
- 0 /* Undefined */
-};
-
-static unsigned int
-compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc)
-{
- return ((trctrfc_ext & 0x1) * 256 + trfc) * 1000
- + byte40_table_ps[(trctrfc_ext >> 1) & 0x7];
-}
-
-static unsigned int
-compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc)
-{
- return trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7];
-}
-
-/*
- * tCKmax from DDR I SPD Byte 43
- *
- * Bits 7:2 == whole ns
- * Bits 1:0 == quarter ns
- * 00 == 0.00 ns
- * 01 == 0.25 ns
- * 10 == 0.50 ns
- * 11 == 0.75 ns
- *
- * Returns picoseconds.
- */
-static unsigned int
-compute_tckmax_from_spd_ps(unsigned int byte43)
-{
- return (byte43 >> 2) * 1000 + (byte43 & 0x3) * 250;
-}
-
-/*
- * Determine Refresh Rate. Ignore self refresh bit on DDR I.
- * Table from SPD Spec, Byte 12, converted to picoseconds and
- * filled in with "default" normal values.
- */
-static unsigned int
-determine_refresh_rate_ps(const unsigned int spd_refresh)
-{
- unsigned int refresh_time_ps[8] = {
- 15625000, /* 0 Normal 1.00x */
- 3900000, /* 1 Reduced .25x */
- 7800000, /* 2 Extended .50x */
- 31300000, /* 3 Extended 2.00x */
- 62500000, /* 4 Extended 4.00x */
- 125000000, /* 5 Extended 8.00x */
- 15625000, /* 6 Normal 1.00x filler */
- 15625000, /* 7 Normal 1.00x filler */
- };
-
- return refresh_time_ps[spd_refresh & 0x7];
-}
-
-/*
- * The purpose of this function is to compute a suitable
- * CAS latency given the DRAM clock period. The SPD only
- * defines at most 3 CAS latencies. Typically the slower in
- * frequency the DIMM runs at, the shorter its CAS latency can be.
- * If the DIMM is operating at a sufficiently low frequency,
- * it may be able to run at a CAS latency shorter than the
- * shortest SPD-defined CAS latency.
- *
- * If a CAS latency is not found, 0 is returned.
- *
- * Do this by finding in the standard speed bin table the longest
- * tCKmin that doesn't exceed the value of mclk_ps (tCK).
- *
- * An assumption made is that the SDRAM device allows the
- * CL to be programmed for a value that is lower than those
- * advertised by the SPD. This is not always the case,
- * as those modes not defined in the SPD are optional.
- *
- * CAS latency de-rating based upon values JEDEC Standard No. 79-E
- * Table 11.
- *
- * ordinal 2, ddr1_speed_bins[1] contains tCK for CL=2
- */
- /* CL2.0 CL2.5 CL3.0 */
-unsigned short ddr1_speed_bins[] = {0, 7500, 6000, 5000 };
-
-static unsigned int
-compute_derated_DDR1_CAS_latency(unsigned int mclk_ps)
-{
- const unsigned int num_speed_bins = ARRAY_SIZE(ddr1_speed_bins);
- unsigned int lowest_tCKmin_found = 0;
- unsigned int lowest_tCKmin_CL = 0;
- unsigned int i;
-
- debug("mclk_ps = %u\n", mclk_ps);
-
- for (i = 0; i < num_speed_bins; i++) {
- unsigned int x = ddr1_speed_bins[i];
- debug("i=%u, x = %u, lowest_tCKmin_found = %u\n",
- i, x, lowest_tCKmin_found);
- if (x && lowest_tCKmin_found <= x && x <= mclk_ps) {
- lowest_tCKmin_found = x;
- lowest_tCKmin_CL = i + 1;
- }
- }
-
- debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL);
-
- return lowest_tCKmin_CL;
-}
-
-/*
- * ddr1_compute_dimm_parameters for DDR1 SPD
- *
- * Compute DIMM parameters based upon the SPD information in spd.
- * Writes the results to the struct dimm_params structure pointed by pdimm.
- *
- * FIXME: use #define for the retvals
- */
-unsigned int ddr1_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr1_spd_eeprom *spd,
- struct dimm_params *pdimm)
-{
- int ret;
-
- ret = ddr1_spd_check(spd);
- if (ret) {
- printf("DIMM: failed checksum\n");
- return 2;
- }
-
- /*
- * The part name in ASCII in the SPD EEPROM is not null terminated.
- * Guarantee null termination here by presetting all bytes to 0
- * and copying the part name in ASCII from the SPD onto it
- */
- memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
- memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
-
- /* DIMM organization parameters */
- pdimm->n_ranks = spd->nrows;
- pdimm->rank_density = compute_ranksize(spd->mem_type, spd->bank_dens);
- pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
- pdimm->data_width = spd->dataw_lsb;
- pdimm->primary_sdram_width = spd->primw;
- pdimm->ec_sdram_width = spd->ecw;
-
- /*
- * FIXME: Need to determine registered_dimm status.
- * 1 == register buffered
- * 0 == unbuffered
- */
- pdimm->registered_dimm = 0; /* unbuffered */
-
- /* SDRAM device parameters */
- pdimm->n_row_addr = spd->nrow_addr;
- pdimm->n_col_addr = spd->ncol_addr;
- pdimm->n_banks_per_sdram_device = spd->nbanks;
- pdimm->edc_config = spd->config;
- pdimm->burst_lengths_bitmask = spd->burstl;
-
- /*
- * Calculate the Maximum Data Rate based on the Minimum Cycle time.
- * The SPD clk_cycle field (tCKmin) is measured in tenths of
- * nanoseconds and represented as BCD.
- */
- pdimm->tckmin_x_ps
- = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle);
- pdimm->tckmin_x_minus_1_ps
- = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2);
- pdimm->tckmin_x_minus_2_ps
- = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3);
-
- pdimm->tckmax_ps = compute_tckmax_from_spd_ps(spd->tckmax);
-
- /*
- * Compute CAS latencies defined by SPD
- * The SPD caslat_x should have at least 1 and at most 3 bits set.
- *
- * If cas_lat after masking is 0, the __ilog2 function returns
- * 255 into the variable. This behavior is abused once.
- */
- pdimm->caslat_x = ilog2(spd->cas_lat);
- pdimm->caslat_x_minus_1 = ilog2(spd->cas_lat
- & ~(1 << pdimm->caslat_x));
- pdimm->caslat_x_minus_2 = ilog2(spd->cas_lat
- & ~(1 << pdimm->caslat_x)
- & ~(1 << pdimm->caslat_x_minus_1));
-
- /* Compute CAS latencies below that defined by SPD */
- pdimm->caslat_lowest_derated = compute_derated_DDR1_CAS_latency(
- get_memory_clk_period_ps(c));
-
- /* Compute timing parameters */
- pdimm->trcd_ps = spd->trcd * 250;
- pdimm->trp_ps = spd->trp * 250;
- pdimm->tras_ps = spd->tras * 1000;
-
- pdimm->twr_ps = mclk_to_picos(c, 3);
- pdimm->twtr_ps = mclk_to_picos(c, 1);
- pdimm->trfc_ps = compute_trfc_ps_from_spd(0, spd->trfc);
-
- pdimm->trrd_ps = spd->trrd * 250;
- pdimm->trc_ps = compute_trc_ps_from_spd(0, spd->trc);
-
- pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh);
-
- pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
- pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
- pdimm->tds_ps
- = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
- pdimm->tdh_ps
- = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
-
- pdimm->trtp_ps = mclk_to_picos(c, 2); /* By the book. */
- pdimm->tdqsq_max_ps = spd->tdqsq * 10;
- pdimm->tqhs_ps = spd->tqhs * 10;
-
- return 0;
-}
diff --git a/drivers/ddr/fsl/ddr2_dimm_params.c b/drivers/ddr/fsl/ddr2_dimm_params.c
deleted file mode 100644
index e33a8ded48..0000000000
--- a/drivers/ddr/fsl/ddr2_dimm_params.c
+++ /dev/null
@@ -1,320 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2008 Freescale Semiconductor, Inc.
- */
-
-#include <common.h>
-#include <soc/fsl/fsl_ddr_sdram.h>
-#include <linux/log2.h>
-#include "fsl_ddr.h"
-
-/*
- * Calculate the Density of each Physical Rank.
- * Returned size is in bytes.
- *
- * Study these table from Byte 31 of JEDEC SPD Spec.
- *
- * DDR I DDR II
- * Bit Size Size
- * --- ----- ------
- * 7 high 512MB 512MB
- * 6 256MB 256MB
- * 5 128MB 128MB
- * 4 64MB 16GB
- * 3 32MB 8GB
- * 2 16MB 4GB
- * 1 2GB 2GB
- * 0 low 1GB 1GB
- *
- * Reorder Table to be linear by stripping the bottom
- * 2 or 5 bits off and shifting them up to the top.
- *
- */
-static unsigned long long
-compute_ranksize(unsigned int mem_type, unsigned char row_dens)
-{
- unsigned long long bsize;
-
- /* Bottom 5 bits up to the top. */
- bsize = ((row_dens >> 5) | ((row_dens & 31) << 3));
- bsize <<= 27ULL;
- debug("DDR: DDR II rank density = 0x%16llx\n", bsize);
-
- return bsize;
-}
-
-/*
- * Convert a two-nibble BCD value into a cycle time.
- * While the spec calls for nano-seconds, picos are returned.
- *
- * This implements the tables for bytes 9, 23 and 25 for both
- * DDR I and II. No allowance for distinguishing the invalid
- * fields absent for DDR I yet present in DDR II is made.
- * (That is, cycle times of .25, .33, .66 and .75 ns are
- * allowed for both DDR II and I.)
- */
-static unsigned int
-convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val)
-{
- /* Table look up the lower nibble, allow DDR I & II. */
- unsigned int tenths_ps[16] = {
- 0,
- 100,
- 200,
- 300,
- 400,
- 500,
- 600,
- 700,
- 800,
- 900,
- 250, /* This and the next 3 entries valid ... */
- 330, /* ... only for tCK calculations. */
- 660,
- 750,
- 0, /* undefined */
- 0 /* undefined */
- };
-
- unsigned int whole_ns = (spd_val & 0xF0) >> 4;
- unsigned int tenth_ns = spd_val & 0x0F;
- unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns];
-
- return ps;
-}
-
-static unsigned int
-convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val)
-{
- unsigned int tenth_ns = (spd_val & 0xF0) >> 4;
- unsigned int hundredth_ns = spd_val & 0x0F;
- unsigned int ps = tenth_ns * 100 + hundredth_ns * 10;
-
- return ps;
-}
-
-static unsigned int byte40_table_ps[8] = {
- 0,
- 250,
- 330,
- 500,
- 660,
- 750,
- 0, /* supposed to be RFC, but not sure what that means */
- 0 /* Undefined */
-};
-
-static unsigned int
-compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc)
-{
- return (((trctrfc_ext & 0x1) * 256) + trfc) * 1000
- + byte40_table_ps[(trctrfc_ext >> 1) & 0x7];
-}
-
-static unsigned int
-compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc)
-{
- return trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7];
-}
-
-/*
- * Determine Refresh Rate. Ignore self refresh bit on DDR I.
- * Table from SPD Spec, Byte 12, converted to picoseconds and
- * filled in with "default" normal values.
- */
-static unsigned int
-determine_refresh_rate_ps(const unsigned int spd_refresh)
-{
- unsigned int refresh_time_ps[8] = {
- 15625000, /* 0 Normal 1.00x */
- 3900000, /* 1 Reduced .25x */
- 7800000, /* 2 Extended .50x */
- 31300000, /* 3 Extended 2.00x */
- 62500000, /* 4 Extended 4.00x */
- 125000000, /* 5 Extended 8.00x */
- 15625000, /* 6 Normal 1.00x filler */
- 15625000, /* 7 Normal 1.00x filler */
- };
-
- return refresh_time_ps[spd_refresh & 0x7];
-}
-
-/*
- * The purpose of this function is to compute a suitable
- * CAS latency given the DRAM clock period. The SPD only
- * defines at most 3 CAS latencies. Typically the slower in
- * frequency the DIMM runs at, the shorter its CAS latency can.
- * be. If the DIMM is operating at a sufficiently low frequency,
- * it may be able to run at a CAS latency shorter than the
- * shortest SPD-defined CAS latency.
- *
- * If a CAS latency is not found, 0 is returned.
- *
- * Do this by finding in the standard speed bin table the longest
- * tCKmin that doesn't exceed the value of mclk_ps (tCK).
- *
- * An assumption made is that the SDRAM device allows the
- * CL to be programmed for a value that is lower than those
- * advertised by the SPD. This is not always the case,
- * as those modes not defined in the SPD are optional.
- *
- * CAS latency de-rating based upon values JEDEC Standard No. 79-2C
- * Table 40, "DDR2 SDRAM stanadard speed bins and tCK, tRCD, tRP, tRAS,
- * and tRC for corresponding bin"
- *
- * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3
- * Not certain if any good value exists for CL=2
- */
- /* CL2 CL3 CL4 CL5 CL6 CL7*/
-unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 };
-
-static unsigned int
-compute_derated_DDR2_CAS_latency(unsigned int mclk_ps)
-{
- const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins);
- unsigned int lowest_tCKmin_found = 0;
- unsigned int lowest_tCKmin_CL = 0;
- unsigned int i;
-
- debug("mclk_ps = %u\n", mclk_ps);
-
- for (i = 0; i < num_speed_bins; i++) {
- unsigned int x = ddr2_speed_bins[i];
- debug("i=%u, x = %u, lowest_tCKmin_found = %u\n",
- i, x, lowest_tCKmin_found);
- if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) {
- lowest_tCKmin_found = x;
- lowest_tCKmin_CL = i + 2;
- }
- }
-
- debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL);
-
- return lowest_tCKmin_CL;
-}
-
-/*
- * ddr2_compute_dimm_parameters for DDR2 SPD
- *
- * Compute DIMM parameters based upon the SPD information in spd.
- * Writes the results to the struct dimm_params structure pointed by pdimm.
- *
- * FIXME: use #define for the retvals
- */
-unsigned int ddr2_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr2_spd_eeprom *spd,
- struct dimm_params *pdimm)
-{
- int ret;
-
- ret = ddr2_spd_check(spd);
- if (ret) {
- printf("DIMM: failed checksum\n");
- return 2;
- }
-
- /*
- * The part name in ASCII in the SPD EEPROM is not null terminated.
- * Guarantee null termination here by presetting all bytes to 0
- * and copying the part name in ASCII from the SPD onto it
- */
- memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
- memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
-
- /* DIMM organization parameters */
- pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1;
- pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens);
- pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
- pdimm->data_width = spd->dataw;
- pdimm->primary_sdram_width = spd->primw;
- pdimm->ec_sdram_width = spd->ecw;
-
- /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */
- switch (spd->dimm_type) {
- case DDR2_SPD_DIMMTYPE_RDIMM:
- case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM:
- case DDR2_SPD_DIMMTYPE_MINI_RDIMM:
- /* Registered/buffered DIMMs */
- pdimm->registered_dimm = 1;
- break;
-
- case DDR2_SPD_DIMMTYPE_UDIMM:
- case DDR2_SPD_DIMMTYPE_SO_DIMM:
- case DDR2_SPD_DIMMTYPE_MICRO_DIMM:
- case DDR2_SPD_DIMMTYPE_MINI_UDIMM:
- /* Unbuffered DIMMs */
- pdimm->registered_dimm = 0;
- break;
-
- case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM:
- default:
- printf("unknown dimm_type 0x%02X\n", spd->dimm_type);
- return 1;
- }
-
- /* SDRAM device parameters */
- pdimm->n_row_addr = spd->nrow_addr;
- pdimm->n_col_addr = spd->ncol_addr;
- pdimm->n_banks_per_sdram_device = spd->nbanks;
- pdimm->edc_config = spd->config;
- pdimm->burst_lengths_bitmask = spd->burstl;
-
- /*
- * Calculate the Maximum Data Rate based on the Minimum Cycle time.
- * The SPD clk_cycle field (tCKmin) is measured in tenths of
- * nanoseconds and represented as BCD.
- */
- pdimm->tckmin_x_ps
- = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle);
- pdimm->tckmin_x_minus_1_ps
- = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2);
- pdimm->tckmin_x_minus_2_ps
- = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3);
-
- pdimm->tckmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax);
-
- /*
- * Compute CAS latencies defined by SPD
- * The SPD caslat_x should have at least 1 and at most 3 bits set.
- *
- * If cas_lat after masking is 0, the __ilog2 function returns
- * 255 into the variable. This behavior is abused once.
- */
- pdimm->caslat_x = ilog2(spd->cas_lat);
- pdimm->caslat_x_minus_1 = ilog2(spd->cas_lat
- & ~(1 << pdimm->caslat_x));
- pdimm->caslat_x_minus_2 = ilog2(spd->cas_lat
- & ~(1 << pdimm->caslat_x)
- & ~(1 << pdimm->caslat_x_minus_1));
-
- /* Compute CAS latencies below that defined by SPD */
- pdimm->caslat_lowest_derated = compute_derated_DDR2_CAS_latency(
- get_memory_clk_period_ps(c));
-
- /* Compute timing parameters */
- pdimm->trcd_ps = spd->trcd * 250;
- pdimm->trp_ps = spd->trp * 250;
- pdimm->tras_ps = spd->tras * 1000;
-
- pdimm->twr_ps = spd->twr * 250;
- pdimm->twtr_ps = spd->twtr * 250;
- pdimm->trfc_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc);
-
- pdimm->trrd_ps = spd->trrd * 250;
- pdimm->trc_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc);
-
- pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh);
-
- pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
- pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
- pdimm->tds_ps
- = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
- pdimm->tdh_ps
- = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
-
- pdimm->trtp_ps = spd->trtp * 250;
- pdimm->tdqsq_max_ps = spd->tdqsq * 10;
- pdimm->tqhs_ps = spd->tqhs * 10;
-
- return 0;
-}
diff --git a/drivers/ddr/fsl/ddr3_dimm_params.c b/drivers/ddr/fsl/ddr3_dimm_params.c
deleted file mode 100644
index 92012a5af9..0000000000
--- a/drivers/ddr/fsl/ddr3_dimm_params.c
+++ /dev/null
@@ -1,325 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2008-2012 Freescale Semiconductor, Inc.
- * Dave Liu <daveliu@freescale.com>
- *
- * calculate the organization and timing parameter
- * from ddr3 spd, please refer to the spec
- * JEDEC standard No.21-C 4_01_02_11R18.pdf
- */
-
-#include <common.h>
-#include <soc/fsl/fsl_ddr_sdram.h>
-#include "fsl_ddr.h"
-
-/*
- * Calculate the Density of each Physical Rank.
- * Returned size is in bytes.
- *
- * each rank size =
- * sdram capacity(bit) / 8 * primary bus width / sdram width
- *
- * where: sdram capacity = spd byte4[3:0]
- * primary bus width = spd byte8[2:0]
- * sdram width = spd byte7[2:0]
- *
- * SPD byte4 - sdram density and banks
- * bit[3:0] size(bit) size(byte)
- * 0000 256Mb 32MB
- * 0001 512Mb 64MB
- * 0010 1Gb 128MB
- * 0011 2Gb 256MB
- * 0100 4Gb 512MB
- * 0101 8Gb 1GB
- * 0110 16Gb 2GB
- *
- * SPD byte8 - module memory bus width
- * bit[2:0] primary bus width
- * 000 8bits
- * 001 16bits
- * 010 32bits
- * 011 64bits
- *
- * SPD byte7 - module organiztion
- * bit[2:0] sdram device width
- * 000 4bits
- * 001 8bits
- * 010 16bits
- * 011 32bits
- *
- */
-static unsigned long long
-compute_ranksize(const struct ddr3_spd_eeprom *spd)
-{
- unsigned long long bsize;
-
- int nbit_sdram_cap_bsize = 0;
- int nbit_primary_bus_width = 0;
- int nbit_sdram_width = 0;
-
- if ((spd->density_banks & 0xf) < 7)
- nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28;
- if ((spd->bus_width & 0x7) < 4)
- nbit_primary_bus_width = (spd->bus_width & 0x7) + 3;
- if ((spd->organization & 0x7) < 4)
- nbit_sdram_width = (spd->organization & 0x7) + 2;
-
- bsize = 1ULL << (nbit_sdram_cap_bsize - 3
- + nbit_primary_bus_width - nbit_sdram_width);
-
- debug("DDR: DDR III rank density = 0x%16llx\n", bsize);
-
- return bsize;
-}
-
-/*
- * ddr3_compute_dimm_parameters for DDR3 SPD
- *
- * Compute DIMM parameters based upon the SPD information in spd.
- * Writes the results to the struct dimm_params structure pointed by pdimm.
- *
- */
-unsigned int ddr3_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr3_spd_eeprom *spd,
- struct dimm_params *pdimm)
-{
- int ret;
- unsigned int mtb_ps;
- int ftb_10th_ps;
- int i;
-
- ret = ddr3_spd_check(spd);
- if (ret) {
- printf("DIMM: failed checksum\n");
- return 2;
- }
-
- /*
- * The part name in ASCII in the SPD EEPROM is not null terminated.
- * Guarantee null termination here by presetting all bytes to 0
- * and copying the part name in ASCII from the SPD onto it
- */
- memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
- if ((spd->info_size_crc & 0xF) > 1)
- memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
-
- /* DIMM organization parameters */
- pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1;
- pdimm->rank_density = compute_ranksize(spd);
- pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
- pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7));
- if ((spd->bus_width >> 3) & 0x3)
- pdimm->ec_sdram_width = 8;
- else
- pdimm->ec_sdram_width = 0;
- pdimm->data_width = pdimm->primary_sdram_width
- + pdimm->ec_sdram_width;
- pdimm->device_width = 1 << ((spd->organization & 0x7) + 2);
-
- /* These are the types defined by the JEDEC DDR3 SPD spec */
- pdimm->mirrored_dimm = 0;
- pdimm->registered_dimm = 0;
- switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) {
- case DDR3_SPD_MODULETYPE_RDIMM:
- case DDR3_SPD_MODULETYPE_MINI_RDIMM:
- case DDR3_SPD_MODULETYPE_72B_SO_RDIMM:
- /* Registered/buffered DIMMs */
- pdimm->registered_dimm = 1;
- for (i = 0; i < 16; i += 2) {
- u8 rcw = spd->mod_section.registered.rcw[i/2];
- pdimm->rcw[i] = (rcw >> 0) & 0x0F;
- pdimm->rcw[i+1] = (rcw >> 4) & 0x0F;
- }
- break;
-
- case DDR3_SPD_MODULETYPE_UDIMM:
- case DDR3_SPD_MODULETYPE_SO_DIMM:
- case DDR3_SPD_MODULETYPE_MICRO_DIMM:
- case DDR3_SPD_MODULETYPE_MINI_UDIMM:
- case DDR3_SPD_MODULETYPE_MINI_CDIMM:
- case DDR3_SPD_MODULETYPE_72B_SO_UDIMM:
- case DDR3_SPD_MODULETYPE_72B_SO_CDIMM:
- case DDR3_SPD_MODULETYPE_LRDIMM:
- case DDR3_SPD_MODULETYPE_16B_SO_DIMM:
- case DDR3_SPD_MODULETYPE_32B_SO_DIMM:
- /* Unbuffered DIMMs */
- if (spd->mod_section.unbuffered.addr_mapping & 0x1)
- pdimm->mirrored_dimm = 1;
- break;
-
- default:
- printf("unknown module_type 0x%02X\n", spd->module_type);
- return 1;
- }
-
- /* SDRAM device parameters */
- pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12;
- pdimm->n_col_addr = (spd->addressing & 0x7) + 9;
- pdimm->n_banks_per_sdram_device = 8 << ((spd->density_banks >> 4) & 0x7);
-
- /*
- * The SPD spec has not the ECC bit,
- * We consider the DIMM as ECC capability
- * when the extension bus exist
- */
- if (pdimm->ec_sdram_width)
- pdimm->edc_config = 0x02;
- else
- pdimm->edc_config = 0x00;
-
- /*
- * The SPD spec has not the burst length byte
- * but DDR3 spec has nature BL8 and BC4,
- * BL8 -bit3, BC4 -bit2
- */
- pdimm->burst_lengths_bitmask = 0x0c;
-
- /* MTB - medium timebase
- * The unit in the SPD spec is ns,
- * We convert it to ps.
- * eg: MTB = 0.125ns (125ps)
- */
- mtb_ps = (spd->mtb_dividend * 1000) /spd->mtb_divisor;
- pdimm->mtb_ps = mtb_ps;
-
- /*
- * FTB - fine timebase
- * use 1/10th of ps as our unit to avoid floating point
- * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps
- */
- ftb_10th_ps =
- ((spd->ftb_div & 0xf0) >> 4) * 10 / (spd->ftb_div & 0x0f);
- pdimm->ftb_10th_ps = ftb_10th_ps;
- /*
- * sdram minimum cycle time
- * we assume the MTB is 0.125ns
- * eg:
- * tck_min=15 MTB (1.875ns) ->DDR3-1066
- * =12 MTB (1.5ns) ->DDR3-1333
- * =10 MTB (1.25ns) ->DDR3-1600
- */
- pdimm->tckmin_x_ps = spd->tck_min * mtb_ps +
- (spd->fine_tck_min * ftb_10th_ps) / 10;
-
- /*
- * CAS latency supported
- * bit4 - CL4
- * bit5 - CL5
- * bit18 - CL18
- */
- pdimm->caslat_x = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4;
-
- /*
- * min CAS latency time
- * eg: taa_min =
- * DDR3-800D 100 MTB (12.5ns)
- * DDR3-1066F 105 MTB (13.125ns)
- * DDR3-1333H 108 MTB (13.5ns)
- * DDR3-1600H 90 MTB (11.25ns)
- */
- pdimm->taa_ps = spd->taa_min * mtb_ps +
- (spd->fine_taa_min * ftb_10th_ps) / 10;
-
- /*
- * min write recovery time
- * eg:
- * twr_min = 120 MTB (15ns) -> all speed grades.
- */
- pdimm->twr_ps = spd->twr_min * mtb_ps;
-
- /*
- * min RAS to CAS delay time
- * eg: trcd_min =
- * DDR3-800 100 MTB (12.5ns)
- * DDR3-1066F 105 MTB (13.125ns)
- * DDR3-1333H 108 MTB (13.5ns)
- * DDR3-1600H 90 MTB (11.25)
- */
- pdimm->trcd_ps = spd->trcd_min * mtb_ps +
- (spd->fine_trcd_min * ftb_10th_ps) / 10;
-
- /*
- * min row active to row active delay time
- * eg: trrd_min =
- * DDR3-800(1KB page) 80 MTB (10ns)
- * DDR3-1333(1KB page) 48 MTB (6ns)
- */
- pdimm->trrd_ps = spd->trrd_min * mtb_ps;
-
- /*
- * min row precharge delay time
- * eg: trp_min =
- * DDR3-800D 100 MTB (12.5ns)
- * DDR3-1066F 105 MTB (13.125ns)
- * DDR3-1333H 108 MTB (13.5ns)
- * DDR3-1600H 90 MTB (11.25ns)
- */
- pdimm->trp_ps = spd->trp_min * mtb_ps +
- (spd->fine_trp_min * ftb_10th_ps) / 10;
-
- /* min active to precharge delay time
- * eg: tRAS_min =
- * DDR3-800D 300 MTB (37.5ns)
- * DDR3-1066F 300 MTB (37.5ns)
- * DDR3-1333H 288 MTB (36ns)
- * DDR3-1600H 280 MTB (35ns)
- */
- pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) | spd->tras_min_lsb)
- * mtb_ps;
- /*
- * min active to actice/refresh delay time
- * eg: tRC_min =
- * DDR3-800D 400 MTB (50ns)
- * DDR3-1066F 405 MTB (50.625ns)
- * DDR3-1333H 396 MTB (49.5ns)
- * DDR3-1600H 370 MTB (46.25ns)
- */
- pdimm->trc_ps = (((spd->tras_trc_ext & 0xf0) << 4) | spd->trc_min_lsb)
- * mtb_ps + (spd->fine_trc_min * ftb_10th_ps) / 10;
- /*
- * min refresh recovery delay time
- * eg: tRFC_min =
- * 512Mb 720 MTB (90ns)
- * 1Gb 880 MTB (110ns)
- * 2Gb 1280 MTB (160ns)
- */
- pdimm->trfc_ps = ((spd->trfc_min_msb << 8) | spd->trfc_min_lsb)
- * mtb_ps;
- /*
- * min internal write to read command delay time
- * eg: twtr_min = 40 MTB (7.5ns) - all speed bins.
- * tWRT is at least 4 mclk independent of operating freq.
- */
- pdimm->twtr_ps = spd->twtr_min * mtb_ps;
-
- /*
- * min internal read to precharge command delay time
- * eg: trtp_min = 40 MTB (7.5ns) - all speed bins.
- * tRTP is at least 4 mclk independent of operating freq.
- */
- pdimm->trtp_ps = spd->trtp_min * mtb_ps;
-
- /*
- * Average periodic refresh interval
- * tREFI = 7.8 us at normal temperature range
- * = 3.9 us at ext temperature range
- */
- pdimm->refresh_rate_ps = 7800000;
- if ((spd->therm_ref_opt & 0x1) && !(spd->therm_ref_opt & 0x2)) {
- pdimm->refresh_rate_ps = 3900000;
- pdimm->extended_op_srt = 1;
- }
-
- /*
- * min four active window delay time
- * eg: tfaw_min =
- * DDR3-800(1KB page) 320 MTB (40ns)
- * DDR3-1066(1KB page) 300 MTB (37.5ns)
- * DDR3-1333(1KB page) 240 MTB (30ns)
- * DDR3-1600(1KB page) 240 MTB (30ns)
- */
- pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min)
- * mtb_ps;
-
- return 0;
-}
diff --git a/drivers/ddr/fsl/ddr4_dimm_params.c b/drivers/ddr/fsl/ddr4_dimm_params.c
deleted file mode 100644
index 0be2de8de6..0000000000
--- a/drivers/ddr/fsl/ddr4_dimm_params.c
+++ /dev/null
@@ -1,352 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2014-2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018 NXP Semiconductor
- *
- * calculate the organization and timing parameter
- * from ddr3 spd, please refer to the spec
- * JEDEC standard No.21-C 4_01_02_12R23A.pdf
- *
- *
- */
-
-#include <common.h>
-#include <soc/fsl/fsl_ddr_sdram.h>
-#include "fsl_ddr.h"
-
-/*
- * Calculate the Density of each Physical Rank.
- * Returned size is in bytes.
- *
- * Total DIMM size =
- * sdram capacity(bit) / 8 * primary bus width / sdram width
- * * Logical Ranks per DIMM
- *
- * where: sdram capacity = spd byte4[3:0]
- * primary bus width = spd byte13[2:0]
- * sdram width = spd byte12[2:0]
- * Logical Ranks per DIMM = spd byte12[5:3] for SDP, DDP, QDP
- * spd byte12{5:3] * spd byte6[6:4] for 3DS
- *
- * To simplify each rank size = total DIMM size / Number of Package Ranks
- * where Number of Package Ranks = spd byte12[5:3]
- *
- * SPD byte4 - sdram density and banks
- * bit[3:0] size(bit) size(byte)
- * 0000 256Mb 32MB
- * 0001 512Mb 64MB
- * 0010 1Gb 128MB
- * 0011 2Gb 256MB
- * 0100 4Gb 512MB
- * 0101 8Gb 1GB
- * 0110 16Gb 2GB
- * 0111 32Gb 4GB
- *
- * SPD byte13 - module memory bus width
- * bit[2:0] primary bus width
- * 000 8bits
- * 001 16bits
- * 010 32bits
- * 011 64bits
- *
- * SPD byte12 - module organization
- * bit[2:0] sdram device width
- * 000 4bits
- * 001 8bits
- * 010 16bits
- * 011 32bits
- *
- * SPD byte12 - module organization
- * bit[5:3] number of package ranks per DIMM
- * 000 1
- * 001 2
- * 010 3
- * 011 4
- *
- * SPD byte6 - SDRAM package type
- * bit[6:4] Die count
- * 000 1
- * 001 2
- * 010 3
- * 011 4
- * 100 5
- * 101 6
- * 110 7
- * 111 8
- *
- * SPD byte6 - SRAM package type
- * bit[1:0] Signal loading
- * 00 Not specified
- * 01 Multi load stack
- * 10 Sigle load stack (3DS)
- * 11 Reserved
- */
-static unsigned long long
-compute_ranksize(const struct ddr4_spd_eeprom *spd)
-{
- unsigned long long bsize;
-
- int nbit_sdram_cap_bsize = 0;
- int nbit_primary_bus_width = 0;
- int nbit_sdram_width = 0;
- int die_count = 0;
- bool package_3ds;
-
- if ((spd->density_banks & 0xf) <= 7)
- nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28;
- if ((spd->bus_width & 0x7) < 4)
- nbit_primary_bus_width = (spd->bus_width & 0x7) + 3;
- if ((spd->organization & 0x7) < 4)
- nbit_sdram_width = (spd->organization & 0x7) + 2;
- package_3ds = (spd->package_type & 0x3) == 0x2;
- if ((spd->package_type & 0x80) && !package_3ds) { /* other than 3DS */
- printf("Warning: not supported SDRAM package type\n");
- return 0;
- }
- if (package_3ds)
- die_count = (spd->package_type >> 4) & 0x7;
-
- bsize = 1ULL << (nbit_sdram_cap_bsize - 3 +
- nbit_primary_bus_width - nbit_sdram_width +
- die_count);
-
- debug("DDR: DDR rank density = 0x%16llx\n", bsize);
-
- return bsize;
-}
-
-#define spd_to_ps(mtb, ftb) \
- (mtb * pdimm->mtb_ps + (ftb * pdimm->ftb_10th_ps) / 10)
-/*
- * ddr4_compute_dimm_parameters for DDR4 SPD
- *
- * Compute DIMM parameters based upon the SPD information in spd.
- * Writes the results to the struct dimm_params structure pointed by pdimm.
- *
- */
-unsigned int ddr4_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr4_spd_eeprom *spd,
- struct dimm_params *pdimm)
-{
- int ret;
- int i;
- const u8 udimm_rc_e_dq[18] = {
- 0x0c, 0x2c, 0x15, 0x35, 0x15, 0x35, 0x0b, 0x2c, 0x15,
- 0x35, 0x0b, 0x35, 0x0b, 0x2c, 0x0b, 0x35, 0x15, 0x36
- };
- int spd_error = 0;
- u8 *ptr;
- u8 val;
-
- ret = ddr4_spd_check(spd);
- if (ret) {
- printf("DIMM: failed checksum\n");
- return 2;
- }
-
- /*
- * The part name in ASCII in the SPD EEPROM is not null terminated.
- * Guarantee null termination here by presetting all bytes to 0
- * and copying the part name in ASCII from the SPD onto it
- */
- memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
- if ((spd->info_size_crc & 0xF) > 2)
- memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
-
- /* DIMM organization parameters */
- pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1;
- pdimm->rank_density = compute_ranksize(spd);
- pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
- pdimm->die_density = spd->density_banks & 0xf;
- pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7));
- if ((spd->bus_width >> 3) & 0x3)
- pdimm->ec_sdram_width = 8;
- else
- pdimm->ec_sdram_width = 0;
- pdimm->data_width = pdimm->primary_sdram_width
- + pdimm->ec_sdram_width;
- pdimm->device_width = 1 << ((spd->organization & 0x7) + 2);
- pdimm->package_3ds = (spd->package_type & 0x3) == 0x2 ?
- (spd->package_type >> 4) & 0x7 : 0;
-
- /* These are the types defined by the JEDEC SPD spec */
- pdimm->mirrored_dimm = 0;
- pdimm->registered_dimm = 0;
- switch (spd->module_type & DDR4_SPD_MODULETYPE_MASK) {
- case DDR4_SPD_MODULETYPE_RDIMM:
- /* Registered/buffered DIMMs */
- pdimm->registered_dimm = 1;
- if (spd->mod_section.registered.reg_map & 0x1)
- pdimm->mirrored_dimm = 1;
- val = spd->mod_section.registered.ca_stren;
- pdimm->rcw[3] = val >> 4;
- pdimm->rcw[4] = ((val & 0x3) << 2) | ((val & 0xc) >> 2);
- val = spd->mod_section.registered.clk_stren;
- pdimm->rcw[5] = ((val & 0x3) << 2) | ((val & 0xc) >> 2);
- /* Not all in SPD. For convience only. Boards may overwrite. */
- pdimm->rcw[6] = 0xf;
- /*
- * A17 only used for 16Gb and above devices.
- * C[2:0] only used for 3DS.
- */
- pdimm->rcw[8] = pdimm->die_density >= 0x6 ? 0x0 : 0x8 |
- (pdimm->package_3ds > 0x3 ? 0x0 :
- (pdimm->package_3ds > 0x1 ? 0x1 :
- (pdimm->package_3ds > 0 ? 0x2 : 0x3)));
- if (pdimm->package_3ds || pdimm->n_ranks != 4)
- pdimm->rcw[13] = 0xc;
- else
- pdimm->rcw[13] = 0xd; /* Fix encoded by board */
-
- break;
-
- case DDR4_SPD_MODULETYPE_UDIMM:
- case DDR4_SPD_MODULETYPE_SO_DIMM:
- /* Unbuffered DIMMs */
- if (spd->mod_section.unbuffered.addr_mapping & 0x1)
- pdimm->mirrored_dimm = 1;
- if ((spd->mod_section.unbuffered.mod_height & 0xe0) == 0 &&
- (spd->mod_section.unbuffered.ref_raw_card == 0x04)) {
- /* Fix SPD error found on DIMMs with raw card E0 */
- for (i = 0; i < 18; i++) {
- if (spd->mapping[i] == udimm_rc_e_dq[i])
- continue;
- spd_error = 1;
- debug("SPD byte %d: 0x%x, should be 0x%x\n",
- 60 + i, spd->mapping[i],
- udimm_rc_e_dq[i]);
- ptr = (u8 *)&spd->mapping[i];
- *ptr = udimm_rc_e_dq[i];
- }
- if (spd_error)
- printf("SPD DQ mapping error fixed\n");
- }
- break;
-
- default:
- printf("unknown module_type 0x%02X\n", spd->module_type);
- return 1;
- }
-
- /* SDRAM device parameters */
- pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12;
- pdimm->n_col_addr = (spd->addressing & 0x7) + 9;
- pdimm->bank_addr_bits = (spd->density_banks >> 4) & 0x3;
- pdimm->bank_group_bits = (spd->density_banks >> 6) & 0x3;
-
- /*
- * The SPD spec has not the ECC bit,
- * We consider the DIMM as ECC capability
- * when the extension bus exist
- */
- if (pdimm->ec_sdram_width)
- pdimm->edc_config = 0x02;
- else
- pdimm->edc_config = 0x00;
-
- /*
- * The SPD spec has not the burst length byte
- * but DDR4 spec has nature BL8 and BC4,
- * BL8 -bit3, BC4 -bit2
- */
- pdimm->burst_lengths_bitmask = 0x0c;
-
- /* MTB - medium timebase
- * The MTB in the SPD spec is 125ps,
- *
- * FTB - fine timebase
- * use 1/10th of ps as our unit to avoid floating point
- * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps
- */
- if ((spd->timebases & 0xf) == 0x0) {
- pdimm->mtb_ps = 125;
- pdimm->ftb_10th_ps = 10;
-
- } else {
- printf("Unknown Timebases\n");
- }
-
- /* sdram minimum cycle time */
- pdimm->tckmin_x_ps = spd_to_ps(spd->tck_min, spd->fine_tck_min);
-
- /* sdram max cycle time */
- pdimm->tckmax_ps = spd_to_ps(spd->tck_max, spd->fine_tck_max);
-
- /*
- * CAS latency supported
- * bit0 - CL7
- * bit4 - CL11
- * bit8 - CL15
- * bit12- CL19
- * bit16- CL23
- */
- pdimm->caslat_x = (spd->caslat_b1 << 7) |
- (spd->caslat_b2 << 15) |
- (spd->caslat_b3 << 23);
-
- BUG_ON(spd->caslat_b4 != 0);
-
- /*
- * min CAS latency time
- */
- pdimm->taa_ps = spd_to_ps(spd->taa_min, spd->fine_taa_min);
-
- /*
- * min RAS to CAS delay time
- */
- pdimm->trcd_ps = spd_to_ps(spd->trcd_min, spd->fine_trcd_min);
-
- /*
- * Min Row Precharge Delay Time
- */
- pdimm->trp_ps = spd_to_ps(spd->trp_min, spd->fine_trp_min);
-
- /* min active to precharge delay time */
- pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) +
- spd->tras_min_lsb) * pdimm->mtb_ps;
-
- /* min active to actice/refresh delay time */
- pdimm->trc_ps = spd_to_ps((((spd->tras_trc_ext & 0xf0) << 4) +
- spd->trc_min_lsb), spd->fine_trc_min);
- /* Min Refresh Recovery Delay Time */
- pdimm->trfc1_ps = ((spd->trfc1_min_msb << 8) | (spd->trfc1_min_lsb)) *
- pdimm->mtb_ps;
- pdimm->trfc2_ps = ((spd->trfc2_min_msb << 8) | (spd->trfc2_min_lsb)) *
- pdimm->mtb_ps;
- pdimm->trfc4_ps = ((spd->trfc4_min_msb << 8) | (spd->trfc4_min_lsb)) *
- pdimm->mtb_ps;
- /* min four active window delay time */
- pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) *
- pdimm->mtb_ps;
-
- /* min row active to row active delay time, different bank group */
- pdimm->trrds_ps = spd_to_ps(spd->trrds_min, spd->fine_trrds_min);
- /* min row active to row active delay time, same bank group */
- pdimm->trrdl_ps = spd_to_ps(spd->trrdl_min, spd->fine_trrdl_min);
- /* min CAS to CAS Delay Time (tCCD_Lmin), same bank group */
- pdimm->tccdl_ps = spd_to_ps(spd->tccdl_min, spd->fine_tccdl_min);
-
- if (pdimm->package_3ds) {
- if (pdimm->die_density <= 0x4) {
- pdimm->trfc_slr_ps = 260000;
- } else if (pdimm->die_density <= 0x5) {
- pdimm->trfc_slr_ps = 350000;
- } else {
- printf("WARN: Unsupported logical rank density 0x%x\n",
- pdimm->die_density);
- }
- }
-
- /*
- * Average periodic refresh interval
- * tREFI = 7.8 us at normal temperature range
- */
- pdimm->refresh_rate_ps = 7800000;
-
- for (i = 0; i < 18; i++)
- pdimm->dq_mapping[i] = spd->mapping[i];
-
- pdimm->dq_mapping_ors = ((spd->mapping[0] >> 6) & 0x3) == 0 ? 1 : 0;
-
- return 0;
-}
diff --git a/drivers/ddr/fsl/fsl_ddr.h b/drivers/ddr/fsl/fsl_ddr.h
index 459a7ee8e8..0c1a30a236 100644
--- a/drivers/ddr/fsl/fsl_ddr.h
+++ b/drivers/ddr/fsl/fsl_ddr.h
@@ -189,7 +189,6 @@ static inline int is_ddr3_4(const memctl_options_t *popts)
struct fsl_ddr_info;
-phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo);
u32 fsl_ddr_get_intl3r(void);
void board_mem_sleep_setup(void);
@@ -204,18 +203,6 @@ struct fsl_ddr_controller;
u32 fsl_ddr_get_version(struct fsl_ddr_controller *c);
-unsigned int ddr1_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr1_spd_eeprom *spd,
- struct dimm_params *pdimm);
-unsigned int ddr2_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr2_spd_eeprom *spd,
- struct dimm_params *pdimm);
-unsigned int ddr3_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr3_spd_eeprom *spd,
- struct dimm_params *pdimm);
-unsigned int ddr4_compute_dimm_parameters(struct fsl_ddr_controller *c,
- const struct ddr4_spd_eeprom *spd,
- struct dimm_params *pdimm);
void fsl_ddr_set_intl3r(const unsigned int granule_size);
unsigned int compute_fsl_memctl_config_regs(struct fsl_ddr_controller *c);
diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c
index 147ff9916d..19aa4f22a9 100644
--- a/drivers/ddr/fsl/fsl_ddr_gen4.c
+++ b/drivers/ddr/fsl/fsl_ddr_gen4.c
@@ -36,7 +36,7 @@ static void set_wait_for_bits_clear(void *ptr, u32 value, u32 bits)
* Dividing the initialization to two steps to deassert DDR reset signal
* to comply with JEDEC specs for RDIMMs.
*/
-void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step)
+void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step, bool little_endian)
{
struct ccsr_ddr __iomem *ddr = c->base;
const fsl_ddr_cfg_regs_t *regs = &c->fsl_ddr_config_reg;
@@ -53,6 +53,11 @@ void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step)
u32 cs0_bnds, cs1_bnds, cs2_bnds, cs3_bnds, cs0_config;
mod_bnds = regs->cs[0].config & CTLR_INTLV_MASK;
+ if (little_endian)
+ ddr_endianess = DDR_ENDIANESS_LE;
+ else
+ ddr_endianess = DDR_ENDIANESS_BE;
+
if (step == 2)
goto step2;
diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c
index aa2f2e1aa1..c8217a86dd 100644
--- a/drivers/ddr/fsl/main.c
+++ b/drivers/ddr/fsl/main.c
@@ -13,6 +13,8 @@
#include <linux/log2.h>
#include "fsl_ddr.h"
+enum ddr_endianess ddr_endianess;
+
/*
* ASSUMPTIONS:
* - Same number of CONFIG_DIMM_SLOTS_PER_CTLR on each controller
@@ -95,7 +97,7 @@ static unsigned long long step_assign_addresses_linear(struct fsl_ddr_info *pinf
static unsigned long long step_assign_addresses_interleaved(struct fsl_ddr_info *pinfo,
unsigned long long current_mem_base)
{
- unsigned long long total_mem, total_ctlr_mem;
+ unsigned long long total_mem = 0, total_ctlr_mem;
unsigned long long rank_density, ctlr_density = 0;
int i;
@@ -238,19 +240,20 @@ static int compute_dimm_parameters(struct fsl_ddr_controller *c,
struct spd_eeprom *spd,
struct dimm_params *pdimm)
{
+ unsigned int mclk_ps = get_memory_clk_period_ps(c);
const memctl_options_t *popts = &c->memctl_opts;
int ret = -EINVAL;
memset(pdimm, 0, sizeof(*pdimm));
if (is_ddr1(popts))
- ret = ddr1_compute_dimm_parameters(c, (void *)spd, pdimm);
+ ret = ddr1_compute_dimm_parameters(mclk_ps, (void *)spd, pdimm);
else if (is_ddr2(popts))
- ret = ddr2_compute_dimm_parameters(c, (void *)spd, pdimm);
+ ret = ddr2_compute_dimm_parameters(mclk_ps, (void *)spd, pdimm);
else if (is_ddr3(popts))
- ret = ddr3_compute_dimm_parameters(c, (void *)spd, pdimm);
+ ret = ddr3_compute_dimm_parameters((void *)spd, pdimm);
else if (is_ddr4(popts))
- ret = ddr4_compute_dimm_parameters(c, (void *)spd, pdimm);
+ ret = ddr4_compute_dimm_parameters((void *)spd, pdimm);
return ret;
}
@@ -377,12 +380,17 @@ static unsigned long long fsl_ddr_compute(struct fsl_ddr_info *pinfo)
return total_mem;
}
-phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo)
+phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo, bool little_endian)
{
unsigned int i;
unsigned long long total_memory;
int deassert_reset = 0;
+ if (little_endian)
+ ddr_endianess = DDR_ENDIANESS_LE;
+ else
+ ddr_endianess = DDR_ENDIANESS_BE;
+
total_memory = fsl_ddr_compute(pinfo);
/* setup 3-way interleaving before enabling DDRC */
@@ -427,14 +435,14 @@ phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo)
* The following call with step = 1 returns before enabling
* the controller. It has to finish with step = 2 later.
*/
- fsl_ddr_set_memctl_regs(c, deassert_reset ? 1 : 0);
+ fsl_ddr_set_memctl_regs(c, deassert_reset ? 1 : 0, little_endian);
}
if (deassert_reset) {
for (i = 0; i < pinfo->num_ctrls; i++) {
struct fsl_ddr_controller *c = &pinfo->c[i];
/* Call with step = 2 to continue initialization */
- fsl_ddr_set_memctl_regs(c, 2);
+ fsl_ddr_set_memctl_regs(c, 2, little_endian);
}
}
diff --git a/drivers/ddr/imx/Kconfig b/drivers/ddr/imx/Kconfig
new file mode 100644
index 0000000000..ec6c097ec9
--- /dev/null
+++ b/drivers/ddr/imx/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "i.MX DDR controllers"
+ depends on ARCH_IMX8MQ || ARCH_IMX8MM || ARCH_IMX8MN || ARCH_IMX8MP || ARCH_IMX93
+
+config IMX_DRAM
+ bool
+
+config IMX8M_DRAM
+ select IMX_DRAM
+ bool "imx8m dram controller support" if COMPILE_TEST
+
+config IMX9_DRAM
+ select IMX_DRAM
+ bool "imx9 dram controller support" if COMPILE_TEST
+
+endmenu
diff --git a/drivers/ddr/imx/Makefile b/drivers/ddr/imx/Makefile
new file mode 100644
index 0000000000..1d24522bbb
--- /dev/null
+++ b/drivers/ddr/imx/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright 2018 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+pbl-$(CONFIG_IMX_DRAM) += helper.o ddrphy_utils.o ddrphy_train.o ddrphy_csr.o
+pbl-$(CONFIG_IMX8M_DRAM) += imx8m_ddr_init.o
+pbl-$(CONFIG_IMX9_DRAM) += imx9_ddr_init.o
diff --git a/drivers/ddr/imx8m/ddrphy_csr.c b/drivers/ddr/imx/ddrphy_csr.c
index d1cbf8f880..744e140879 100644
--- a/drivers/ddr/imx8m/ddrphy_csr.c
+++ b/drivers/ddr/imx/ddrphy_csr.c
@@ -3,7 +3,7 @@
* Copyright 2018 NXP
*/
-#define pr_fmt(fmt) "imx8m-ddr: " fmt
+#define pr_fmt(fmt) "imx-ddr: " fmt
#include <linux/kernel.h>
#include <soc/imx8m/ddr.h>
diff --git a/drivers/ddr/imx8m/ddrphy_train.c b/drivers/ddr/imx/ddrphy_train.c
index e9d35afdfb..d599445e6f 100644
--- a/drivers/ddr/imx8m/ddrphy_train.c
+++ b/drivers/ddr/imx/ddrphy_train.c
@@ -3,13 +3,12 @@
* Copyright 2018 NXP
*/
-#define pr_fmt(fmt) "imx8m-ddr: " fmt
+#define pr_fmt(fmt) "imx-ddr: " fmt
#include <common.h>
#include <linux/kernel.h>
#include <soc/imx8m/ddr.h>
#include <firmware.h>
-#include <mach/imx8m-regs.h>
static const u16 *lpddr4_imem_1d;
static size_t lpddr4_imem_1d_size;
@@ -53,7 +52,8 @@ void ddr_get_firmware_ddr(void)
&ddr4_dmem_2d_size);
}
-void ddr_load_train_code(enum dram_type dram_type, enum fw_type fw_type)
+void ddr_load_train_code(struct dram_controller *dram, enum dram_type dram_type,
+ enum fw_type fw_type)
{
const u16 *imem, *dmem;
size_t isize, dsize;
@@ -86,17 +86,13 @@ void ddr_load_train_code(enum dram_type dram_type, enum fw_type fw_type)
panic("No matching DDR PHY firmware found");
}
- ddrc_phy_load_firmware(IOMEM(MX8M_DDRC_PHY_BASE_ADDR),
- DDRC_PHY_IMEM, imem, isize);
+ ddrc_phy_load_firmware(dram, DDRC_PHY_IMEM, imem, isize);
- ddrc_phy_load_firmware(IOMEM(MX8M_DDRC_PHY_BASE_ADDR),
- DDRC_PHY_DMEM, dmem, dsize);
+ ddrc_phy_load_firmware(dram, DDRC_PHY_DMEM, dmem, dsize);
}
-int ddr_cfg_phy(struct dram_timing_info *dram_timing, unsigned type)
+int ddr_cfg_phy(struct dram_controller *dram, struct dram_timing_info *dram_timing)
{
- enum ddrc_type ddrc_type = get_ddrc_type(type);
- enum dram_type dram_type = get_dram_type(type);
struct dram_cfg_param *dram_cfg;
struct dram_fsp_msg *fsp_msg;
unsigned int num;
@@ -109,7 +105,7 @@ int ddr_cfg_phy(struct dram_timing_info *dram_timing, unsigned type)
num = dram_timing->ddrphy_cfg_num;
for (i = 0; i < num; i++) {
/* config phy reg */
- dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ dwc_ddrphy_apb_wr(dram, dram_cfg->reg, dram_cfg->val);
dram_cfg++;
}
@@ -118,17 +114,17 @@ int ddr_cfg_phy(struct dram_timing_info *dram_timing, unsigned type)
for (i = 0; i < dram_timing->fsp_msg_num; i++) {
pr_debug("DRAM PHY training for %dMTS\n", fsp_msg->drate);
/* set dram PHY input clocks to desired frequency */
- ddrphy_init_set_dfi_clk(fsp_msg->drate, ddrc_type);
+ dram->set_dfi_clk(dram, fsp_msg->drate);
/* load the dram training firmware image */
- dwc_ddrphy_apb_wr(0xd0000, 0x0);
- ddr_load_train_code(dram_type, fsp_msg->fw_type);
+ dwc_ddrphy_apb_wr(dram, 0xd0000, 0x0);
+ ddr_load_train_code(dram, dram->dram_type, fsp_msg->fw_type);
/* load the frequency set point message block parameter */
dram_cfg = fsp_msg->fsp_cfg;
num = fsp_msg->fsp_cfg_num;
for (j = 0; j < num; j++) {
- dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ dwc_ddrphy_apb_wr(dram, dram_cfg->reg, dram_cfg->val);
dram_cfg++;
}
@@ -142,28 +138,26 @@ int ddr_cfg_phy(struct dram_timing_info *dram_timing, unsigned type)
* 4. read the message block result.
* -------------------------------------------------------------
*/
- dwc_ddrphy_apb_wr(0xd0000, 0x1);
- dwc_ddrphy_apb_wr(0xd0099, 0x9);
- dwc_ddrphy_apb_wr(0xd0099, 0x1);
- dwc_ddrphy_apb_wr(0xd0099, 0x0);
+ dwc_ddrphy_apb_wr(dram, 0xd0000, 0x1);
+ dwc_ddrphy_apb_wr(dram, 0xd0099, 0x9);
+ dwc_ddrphy_apb_wr(dram, 0xd0099, 0x1);
+ dwc_ddrphy_apb_wr(dram, 0xd0099, 0x0);
/* Wait for the training firmware to complete */
- ret = wait_ddrphy_training_complete();
+ ret = wait_ddrphy_training_complete(dram);
if (ret)
return ret;
/* Halt the microcontroller. */
- dwc_ddrphy_apb_wr(0xd0099, 0x1);
+ dwc_ddrphy_apb_wr(dram, 0xd0099, 0x1);
/* Read the Message Block results */
- dwc_ddrphy_apb_wr(0xd0000, 0x0);
-
- ddrphy_init_read_msg_block(fsp_msg->fw_type);
+ dwc_ddrphy_apb_wr(dram, 0xd0000, 0x0);
if (fsp_msg->fw_type != FW_2D_IMAGE)
- get_trained_CDD(i);
+ dram->get_trained_CDD(dram, i);
- dwc_ddrphy_apb_wr(0xd0000, 0x1);
+ dwc_ddrphy_apb_wr(dram, 0xd0000, 0x1);
fsp_msg++;
}
@@ -172,12 +166,12 @@ int ddr_cfg_phy(struct dram_timing_info *dram_timing, unsigned type)
dram_cfg = dram_timing->ddrphy_pie;
num = dram_timing->ddrphy_pie_num;
for (i = 0; i < num; i++) {
- dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ dwc_ddrphy_apb_wr(dram, dram_cfg->reg, dram_cfg->val);
dram_cfg++;
}
/* save the ddr PHY trained CSR in memory for low power use */
- ddrphy_trained_csr_save(ddrphy_trained_csr, ddrphy_trained_csr_num);
+ ddrphy_trained_csr_save(dram, ddrphy_trained_csr, ddrphy_trained_csr_num);
return 0;
}
diff --git a/drivers/ddr/imx/ddrphy_utils.c b/drivers/ddr/imx/ddrphy_utils.c
new file mode 100644
index 0000000000..4925fc39d4
--- /dev/null
+++ b/drivers/ddr/imx/ddrphy_utils.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+* Copyright 2018 NXP
+*/
+
+#define pr_fmt(fmt) "imx-ddr: " fmt
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <linux/iopoll.h>
+#include <soc/imx8m/ddr.h>
+
+void ddrc_phy_load_firmware(struct dram_controller *dram,
+ enum ddrc_phy_firmware_offset offset,
+ const u16 *blob, size_t size)
+{
+ while (size) {
+ writew(*blob++, dwc_ddrphy_apb_addr(dram, offset));
+ offset++;
+ size -= sizeof(*blob);
+ }
+}
+
+enum pmc_constants {
+ PMC_MESSAGE_ID,
+ PMC_MESSAGE_STREAM,
+
+ PMC_TRAIN_SUCCESS = 0x07,
+ PMC_TRAIN_STREAM_START = 0x08,
+ PMC_TRAIN_FAIL = 0xff,
+};
+
+static u32 ddrc_phy_get_message(struct dram_controller *dram, int type)
+{
+ u32 message;
+
+ /*
+ * When BIT0 set to 0, the PMU has a message for the user
+ * Wait for it indefinitely.
+ */
+ while (dwc_ddrphy_apb_rd(dram, 0xd0004) & BIT(0));
+
+ switch (type) {
+ case PMC_MESSAGE_ID:
+ /*
+ * Get the major message ID
+ */
+ message = dwc_ddrphy_apb_rd(dram, 0xd0032);
+ break;
+ case PMC_MESSAGE_STREAM:
+ message = dwc_ddrphy_apb_rd(dram, 0xd0034);
+ message <<= 16;
+ message |= dwc_ddrphy_apb_rd(dram, 0xd0032);
+ break;
+ }
+
+ /*
+ * By setting this register to 0, the user acknowledges the
+ * receipt of the message.
+ */
+ dwc_ddrphy_apb_wr(dram, 0xd0031, 0x00000000);
+ /*
+ * When BIT0 set to 0, the PMU has a message for the user
+ */
+ while (!(dwc_ddrphy_apb_rd(dram, 0xd0004) & BIT(0)));
+
+ dwc_ddrphy_apb_wr(dram, 0xd0031, 0x00000001);
+
+ return message;
+}
+
+static void ddrc_phy_fetch_streaming_message(struct dram_controller *dram)
+{
+ const u16 index = ddrc_phy_get_message(dram, PMC_MESSAGE_STREAM);
+ u16 i;
+
+ for (i = 0; i < index; i++)
+ ddrc_phy_get_message(dram, PMC_MESSAGE_STREAM);
+}
+
+int wait_ddrphy_training_complete(struct dram_controller *dram)
+{
+ for (;;) {
+ const u32 m = ddrc_phy_get_message(dram, PMC_MESSAGE_ID);
+
+ switch (m) {
+ case PMC_TRAIN_STREAM_START:
+ ddrc_phy_fetch_streaming_message(dram);
+ break;
+ case PMC_TRAIN_SUCCESS:
+ return 0;
+ case PMC_TRAIN_FAIL:
+ hang();
+ }
+ }
+}
diff --git a/drivers/ddr/imx8m/helper.c b/drivers/ddr/imx/helper.c
index 98e4084958..f9c25f7180 100644
--- a/drivers/ddr/imx8m/helper.c
+++ b/drivers/ddr/imx/helper.c
@@ -3,39 +3,38 @@
* Copyright 2018 NXP
*/
-#define pr_fmt(fmt) "imx8m-ddr: " fmt
+#define pr_fmt(fmt) "imx-ddr: " fmt
#include <common.h>
#include <io.h>
#include <errno.h>
#include <soc/imx8m/ddr.h>
-#define IMEM_LEN 32768 /* byte */
-#define DMEM_LEN 16384 /* byte */
-#define IMEM_2D_OFFSET 49152
-
-#define IMEM_OFFSET_ADDR 0x00050000
-#define DMEM_OFFSET_ADDR 0x00054000
-#define DDR_TRAIN_CODE_BASE_ADDR IP2APB_DDRPHY_IPS_BASE_ADDR(0)
+/*
+ * We deprecate ddrphy_trained_csr(_num) for board code, so we can set it
+ * ourselves here
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-void ddrphy_trained_csr_save(struct dram_cfg_param *ddrphy_csr,
+void ddrphy_trained_csr_save(struct dram_controller *dram, struct dram_cfg_param *ddrphy_csr,
unsigned int num)
{
int i = 0;
/* enable the ddrphy apb */
- dwc_ddrphy_apb_wr(0xd0000, 0x0);
- dwc_ddrphy_apb_wr(0xc0080, 0x3);
+ dwc_ddrphy_apb_wr(dram, 0xd0000, 0x0);
+ dwc_ddrphy_apb_wr(dram, 0xc0080, 0x3);
for (i = 0; i < num; i++) {
- ddrphy_csr->val = dwc_ddrphy_apb_rd(ddrphy_csr->reg);
+ ddrphy_csr->val = dwc_ddrphy_apb_rd(dram, ddrphy_csr->reg);
ddrphy_csr++;
}
/* disable the ddrphy apb */
- dwc_ddrphy_apb_wr(0xc0080, 0x2);
- dwc_ddrphy_apb_wr(0xd0000, 0x1);
+ dwc_ddrphy_apb_wr(dram, 0xc0080, 0x2);
+ dwc_ddrphy_apb_wr(dram, 0xd0000, 0x1);
}
-void dram_config_save(struct dram_timing_info *timing_info,
+void *dram_config_save(struct dram_controller *dram, struct dram_timing_info *timing_info,
unsigned long saved_timing_base)
{
int i = 0;
@@ -62,7 +61,7 @@ void dram_config_save(struct dram_timing_info *timing_info,
cfg++;
}
- if (imx8m_ddr_old_spreadsheet) {
+ if (dram->imx8m_ddr_old_spreadsheet) {
cfg->reg = DDRC_ADDRMAP7(0);
cfg->val = 0xf0f;
cfg++;
@@ -91,4 +90,6 @@ void dram_config_save(struct dram_timing_info *timing_info,
cfg->val = timing_info->ddrphy_pie[i].val;
cfg++;
}
+
+ return cfg;
}
diff --git a/drivers/ddr/imx8m/ddrphy_utils.c b/drivers/ddr/imx/imx8m_ddr_init.c
index c5d94e3050..d9a5d589f2 100644
--- a/drivers/ddr/imx8m/ddrphy_utils.c
+++ b/drivers/ddr/imx/imx8m_ddr_init.c
@@ -1,27 +1,227 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
-* Copyright 2018 NXP
-*/
+ * Copyright 2018-2019 NXP
+ */
#define pr_fmt(fmt) "imx8m-ddr: " fmt
#include <common.h>
#include <errno.h>
#include <io.h>
-#include <linux/iopoll.h>
#include <soc/imx8m/ddr.h>
-#include <mach/imx8m-regs.h>
-#include <mach/imx8m-ccm-regs.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx8m-regs.h>
+#include <mach/imx/imx8m-ccm-regs.h>
+
+struct dram_controller imx8m_dram_controller = {
+ .phy_base = IOMEM(IP2APB_DDRPHY_IPS_BASE_ADDR(0)),
+};
+
+static void ddr_cfg_umctl2(struct dram_controller *dram, struct dram_cfg_param *ddrc_cfg, int num)
+{
+ int i = 0;
+
+ dram->imx8m_ddr_old_spreadsheet = true;
+
+ for (i = 0; i < num; i++) {
+ if (ddrc_cfg->reg == DDRC_ADDRMAP7(0))
+ dram->imx8m_ddr_old_spreadsheet = false;
+ reg32_write((unsigned long)ddrc_cfg->reg, ddrc_cfg->val);
+ ddrc_cfg++;
+ }
+
+ /*
+ * Older NXP DDR configuration spreadsheets don't initialize ADDRMAP7,
+ * which falsifies the memory size read back from the controller
+ * in barebox proper.
+ */
+ if (dram->imx8m_ddr_old_spreadsheet) {
+ pr_warn("Working around old spreadsheet. Please regenerate\n");
+ /*
+ * Alternatively, stick { DDRC_ADDRMAP7(0), 0xf0f } into
+ * struct dram_timing_info::ddrc_cfg of your old timing file
+ */
+ reg32_write(DDRC_ADDRMAP7(0), 0xf0f);
+ }
+}
+
+static unsigned int g_cdd_rr_max[4];
+static unsigned int g_cdd_rw_max[4];
+static unsigned int g_cdd_wr_max[4];
+static unsigned int g_cdd_ww_max[4];
+
+static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
+ unsigned int addr_end)
+{
+ unsigned int i, imax = 0;
+
+ for (i = addr_start; i <= addr_end; i++) {
+ if (((data[i] >> 7) == 0) && (data[i] > imax))
+ imax = data[i];
+ }
+
+ return imax;
+}
+
+static void get_trained_CDD(struct dram_controller *dram, u32 fsp)
+{
+ unsigned int i, ddr_type, tmp;
+ unsigned int cdd_cha[12], cdd_chb[12];
+ unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
+ unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
+
+ ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
+ if (ddr_type == 0x20) {
+ for (i = 0; i < 6; i++) {
+ tmp = dwc_ddrphy_apb_rd(dram, 0x54013UL + i);
+ cdd_cha[i * 2] = tmp & 0xff;
+ cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ for (i = 0; i < 7; i++) {
+ tmp = dwc_ddrphy_apb_rd(dram, 0x5402cUL + i);
+ if (i == 0) {
+ cdd_cha[0] = (tmp >> 8) & 0xff;
+ } else if (i == 6) {
+ cdd_cha[11] = tmp & 0xff;
+ } else {
+ cdd_chb[ i * 2 - 1] = tmp & 0xff;
+ cdd_chb[i * 2] = (tmp >> 8) & 0xff;
+ }
+ }
+
+ cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
+ cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
+ cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
+ cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
+ cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
+ cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
+ cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
+ cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
+ g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
+ g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
+ g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
+ g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
+ } else {
+ unsigned int ddr4_cdd[64];
+
+ for( i = 0; i < 29; i++) {
+ tmp = dwc_ddrphy_apb_rd(dram, 0x54012UL + i);
+ ddr4_cdd[i * 2] = tmp & 0xff;
+ ddr4_cdd[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ g_cdd_rr_max[fsp] = look_for_max(ddr4_cdd, 1, 12);
+ g_cdd_ww_max[fsp] = look_for_max(ddr4_cdd, 13, 24);
+ g_cdd_rw_max[fsp] = look_for_max(ddr4_cdd, 25, 40);
+ g_cdd_wr_max[fsp] = look_for_max(ddr4_cdd, 41, 56);
+ }
+}
+
+static void update_umctl2_rank_space_setting(unsigned int pstat_num,
+ enum ddrc_type type)
+{
+ unsigned int i,ddr_type;
+ unsigned int rdata, tmp, tmp_t;
+ unsigned int ddrc_w2r,ddrc_r2w,ddrc_wr_gap,ddrc_rd_gap;
+ unsigned long addr_slot;
+
+ ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
+ for (i = 0; i < pstat_num; i++) {
+ addr_slot = i ? (i + 1) * 0x1000 : 0;
+ if (ddr_type == 0x20) {
+ /* update r2w:[13:8], w2r:[5:0] */
+ rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
+ ddrc_w2r = rdata & 0x3f;
+ if (type == DDRC_TYPE_MP)
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
+ else
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
+ ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+
+ ddrc_r2w = (rdata >> 8) & 0x3f;
+ if (type == DDRC_TYPE_MP)
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
+ else
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
+ ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+
+ tmp_t = (rdata & 0xffffc0c0) | (ddrc_r2w << 8) | ddrc_w2r;
+ reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
+ } else {
+ /* update w2r:[5:0] */
+ rdata = reg32_read(DDRC_DRAMTMG9(0) + addr_slot);
+ ddrc_w2r = rdata & 0x3f;
+ if (type == DDRC_TYPE_MP)
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
+ else
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
+ ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+ tmp_t = (rdata & 0xffffffc0) | ddrc_w2r;
+ reg32_write((DDRC_DRAMTMG9(0) + addr_slot), tmp_t);
+
+ /* update r2w:[13:8] */
+ rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
+ ddrc_r2w = (rdata >> 8) & 0x3f;
+ if (type == DDRC_TYPE_MP)
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
+ else
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
+ ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+
+ tmp_t = (rdata & 0xffffc0ff) | (ddrc_r2w << 8);
+ reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
+ }
+
+ if (type != DDRC_TYPE_MQ) {
+ /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
+ rdata = reg32_read(DDRC_RANKCTL(0) + addr_slot);
+ ddrc_wr_gap = (rdata >> 8) & 0xf;
+ if (type == DDRC_TYPE_MP)
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1);
+ else
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1) + 1;
+ ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ ddrc_rd_gap = (rdata >> 4) & 0xf;
+ if (type == DDRC_TYPE_MP)
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1);
+ else
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1) + 1;
+ ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
+ reg32_write((DDRC_RANKCTL(0) + addr_slot), tmp_t);
+ }
+ }
+
+ if (type == DDRC_TYPE_MQ) {
+ /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
+ rdata = reg32_read(DDRC_RANKCTL(0));
+ ddrc_wr_gap = (rdata >> 8) & 0xf;
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[0] >> 1) + 1;
+ ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ ddrc_rd_gap = (rdata >> 4) & 0xf;
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[0] >> 1) + 1;
+ ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
+ reg32_write(DDRC_RANKCTL(0), tmp_t);
+ }
+}
+
/* DDR Transfer rate, bus clock is transfer rate / 2, and the DDRC runs at bus
* clock / 2, which is therefor transfer rate / 4. */
enum ddr_rate {
DDR_4000,
+ DDR_3720,
DDR_3200,
DDR_3000,
- DDR_2600, /* Unused */
+ DDR_2600,
DDR_2400,
- DDR_2376, /* Unused */
+ DDR_2376,
DDR_1600,
DDR_1000, /* Unused */
DDR_1066,
@@ -52,6 +252,7 @@ static const struct imx8mm_fracpll_config {
bool valid;
} imx8mm_fracpll_table[DDR_NUM_RATES] = {
[DDR_4000] = { .valid = true, .r1 = MDIV(250) | PDIV(3) | SDIV(1), .r2 = 0 },
+ [DDR_3720] = { .valid = true, .r1 = MDIV(310) | PDIV(2) | SDIV(2), .r2 = 0 },
[DDR_3200] = { .valid = true, .r1 = MDIV(300) | PDIV(9) | SDIV(0), .r2 = 0 },
[DDR_3000] = { .valid = true, .r1 = MDIV(250) | PDIV(8) | SDIV(0), .r2 = 0 },
[DDR_2600] = { .valid = true, .r1 = MDIV(325) | PDIV(3) | SDIV(2), .r2 = 0 },
@@ -116,101 +317,12 @@ static const struct imx8m_bypass_config {
[DDR_100] = { .valid = true, .alt_clk = CCM_ROOT_CFG(2, 1), .apb_clk = CCM_ROOT_CFG(2, 2) },
};
-void ddrc_phy_load_firmware(void __iomem *phy,
- enum ddrc_phy_firmware_offset offset,
- const u16 *blob, size_t size)
-{
- while (size) {
- writew(*blob++, phy + DDRC_PHY_REG(offset));
- offset++;
- size -= sizeof(*blob);
- }
-}
-
-enum pmc_constants {
- PMC_MESSAGE_ID,
- PMC_MESSAGE_STREAM,
-
- PMC_TRAIN_SUCCESS = 0x07,
- PMC_TRAIN_STREAM_START = 0x08,
- PMC_TRAIN_FAIL = 0xff,
-};
-
-static u32 ddrc_phy_get_message(void __iomem *phy, int type)
-{
- u32 r, message;
-
- /*
- * When BIT0 set to 0, the PMU has a message for the user
- * Wait for it indefinitely.
- */
- readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004),
- r, !(r & BIT(0)), 0);
-
- switch (type) {
- case PMC_MESSAGE_ID:
- /*
- * Get the major message ID
- */
- message = readl(phy + DDRC_PHY_REG(0xd0032));
- break;
- case PMC_MESSAGE_STREAM:
- message = readl(phy + DDRC_PHY_REG(0xd0034));
- message <<= 16;
- message |= readl(phy + DDRC_PHY_REG(0xd0032));
- break;
- }
-
- /*
- * By setting this register to 0, the user acknowledges the
- * receipt of the message.
- */
- writel(0x00000000, phy + DDRC_PHY_REG(0xd0031));
- /*
- * When BIT0 set to 0, the PMU has a message for the user
- */
- readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004),
- r, r & BIT(0), 0);
-
- writel(0x00000001, phy + DDRC_PHY_REG(0xd0031));
-
- return message;
-}
-
-static void ddrc_phy_fetch_streaming_message(void __iomem *phy)
-{
- const u16 index = ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM);
- u16 i;
-
- for (i = 0; i < index; i++)
- ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM);
-}
-
-int wait_ddrphy_training_complete(void)
-{
- void __iomem *phy = IOMEM(MX8M_DDRC_PHY_BASE_ADDR);
-
- for (;;) {
- const u32 m = ddrc_phy_get_message(phy, PMC_MESSAGE_ID);
-
- switch (m) {
- case PMC_TRAIN_STREAM_START:
- ddrc_phy_fetch_streaming_message(phy);
- break;
- case PMC_TRAIN_SUCCESS:
- return 0;
- case PMC_TRAIN_FAIL:
- hang();
- }
- }
-}
-
static void dram_enable_bypass(enum ddr_rate drate)
{
const struct imx8m_bypass_config *config = &imx8m_bypass_table[drate];
if (!config->valid) {
- printf("No matched freq table entry %u\n", drate);
+ pr_warn("No matched freq table entry %u\n", drate);
return;
}
@@ -239,7 +351,7 @@ static int dram_frac_pll_init(enum ddr_rate drate)
const struct imx8mm_fracpll_config *config = &imx8mm_fracpll_table[drate];
if (!config->valid) {
- printf("No matched freq table entry %u\n", drate);
+ pr_warn("No matched freq table entry %u\n", drate);
return -EINVAL;
}
@@ -284,7 +396,7 @@ static int dram_sscg_pll_init(enum ddr_rate drate)
const struct imx8mq_ssgcpll_config *config = &imx8mq_ssgcpll_table[drate];
if (!config->valid) {
- printf("No matched freq table entry %u\n", drate);
+ pr_warn("No matched freq table entry %u\n", drate);
return -EINVAL;
}
@@ -329,201 +441,208 @@ static int dram_pll_init(enum ddr_rate drate, enum ddrc_type type)
}
}
-void ddrphy_init_set_dfi_clk(unsigned int drate_mhz, enum ddrc_type type)
+static void ddrphy_init_set_dfi_clk(struct dram_controller *dram, unsigned int drate_mhz)
{
enum ddr_rate drate;
switch (drate_mhz) {
case 4000: drate = DDR_4000; break;
+ case 3720: drate = DDR_3720; break;
case 3200: drate = DDR_3200; break;
case 3000: drate = DDR_3000; break;
+ case 2600: drate = DDR_2600; break;
case 2400: drate = DDR_2400; break;
+ case 2376: drate = DDR_2376; break;
case 1600: drate = DDR_1600; break;
case 1066: drate = DDR_1066; break;
case 667: drate = DDR_667; break;
case 400: drate = DDR_400; break;
case 100: drate = DDR_100; break;
default:
+ pr_warn("Unsupported frequency %u\n", drate_mhz);
return;
}
if (drate_mhz > 400) {
- dram_pll_init(drate, type);
+ dram_pll_init(drate, dram->ddrc_type);
dram_disable_bypass();
} else {
dram_enable_bypass(drate);
}
}
-void ddrphy_init_read_msg_block(enum fw_type type)
+/*
+ * We store the timing parameters here. the TF-A will pick these up.
+ * Note that the timing used we leave the driver with is a PLL bypass 25MHz
+ * mode. So if your board runs horribly slow you'll likely have to provide a
+ * TF-A binary.
+ */
+#define IMX8M_SAVED_DRAM_TIMING_BASE 0x180000
+
+int imx8m_ddr_init(struct dram_controller *dram, struct dram_timing_info *dram_timing)
{
-}
+ unsigned long src_ddrc_rcr = MX8M_SRC_DDRC_RCR_ADDR;
+ unsigned int tmp, initial_drate, target_freq;
+ int ret;
-static unsigned int g_cdd_rr_max[4];
-static unsigned int g_cdd_rw_max[4];
-static unsigned int g_cdd_wr_max[4];
-static unsigned int g_cdd_ww_max[4];
+ pr_debug("start DRAM init\n");
-static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
- unsigned int addr_end)
-{
- unsigned int i, imax = 0;
+ dram->get_trained_CDD = get_trained_CDD;
+ dram->set_dfi_clk = ddrphy_init_set_dfi_clk;
- for (i = addr_start; i <= addr_end; i++) {
- if (((data[i] >> 7) == 0) && (data[i] > imax))
- imax = data[i];
+ /* Step1: Follow the power up procedure */
+ switch (dram->ddrc_type) {
+ case DDRC_TYPE_MQ:
+ reg32_write(src_ddrc_rcr + 0x04, 0x8f00000f);
+ reg32_write(src_ddrc_rcr, 0x8f00000f);
+ reg32_write(src_ddrc_rcr + 0x04, 0x8f000000);
+ break;
+ case DDRC_TYPE_MM:
+ case DDRC_TYPE_MN:
+ case DDRC_TYPE_MP:
+ reg32_write(src_ddrc_rcr, 0x8f00001f);
+ reg32_write(src_ddrc_rcr, 0x8f00000f);
+ break;
}
- return imax;
-}
+ pr_debug("cfg clk\n");
-void get_trained_CDD(u32 fsp)
-{
- unsigned int i, ddr_type, tmp;
- unsigned int cdd_cha[12], cdd_chb[12];
- unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
- unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
+ /* disable iso */
+ reg32_write(0x303A00EC, 0x0000ffff); /* PGC_CPU_MAPPING */
+ reg32setbit(0x303A00F8, 5); /* PU_PGC_SW_PUP_REQ */
- ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
- if (ddr_type == 0x20) {
- for (i = 0; i < 6; i++) {
- tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) +
- (0x54013UL + i) * 4);
- cdd_cha[i * 2] = tmp & 0xff;
- cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
- }
+ initial_drate = dram_timing->fsp_msg[0].drate;
+ /* default to the frequency point 0 clock */
+ dram->set_dfi_clk(dram, initial_drate);
- for (i = 0; i < 7; i++) {
- tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) +
- (0x5402cUL + i) * 4);
- if (i == 0) {
- cdd_cha[0] = (tmp >> 8) & 0xff;
- } else if (i == 6) {
- cdd_cha[11] = tmp & 0xff;
- } else {
- cdd_chb[ i * 2 - 1] = tmp & 0xff;
- cdd_chb[i * 2] = (tmp >> 8) & 0xff;
- }
- }
+ /* D-aasert the presetn */
+ reg32_write(src_ddrc_rcr, 0x8F000006);
- cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
- cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
- cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
- cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
- cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
- cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
- cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
- cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
- g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
- g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
- g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
- g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
- } else {
- unsigned int ddr4_cdd[64];
+ /* Step2: Program the dwc_ddr_umctl2 registers */
+ pr_debug("ddrc config start\n");
+ ddr_cfg_umctl2(dram, dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num);
+ pr_debug("ddrc config done\n");
- for( i = 0; i < 29; i++) {
- tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) +
- (0x54012UL + i) * 4);
- ddr4_cdd[i * 2] = tmp & 0xff;
- ddr4_cdd[i * 2 + 1] = (tmp >> 8) & 0xff;
- }
+ /* Step3: De-assert reset signal(core_ddrc_rstn & aresetn_n) */
+ reg32_write(src_ddrc_rcr, 0x8F000004);
+ reg32_write(src_ddrc_rcr, 0x8F000000);
- g_cdd_rr_max[fsp] = look_for_max(ddr4_cdd, 1, 12);
- g_cdd_ww_max[fsp] = look_for_max(ddr4_cdd, 13, 24);
- g_cdd_rw_max[fsp] = look_for_max(ddr4_cdd, 25, 40);
- g_cdd_wr_max[fsp] = look_for_max(ddr4_cdd, 41, 56);
- }
-}
+ /*
+ * Step4: Disable auto-refreshes, self-refresh, powerdown, and
+ * assertion of dfi_dram_clk_disable by setting RFSHCTL3.dis_auto_refresh = 1,
+ * PWRCTL.powerdown_en = 0, and PWRCTL.selfref_en = 0,
+ * PWRCTL.en_dfi_dram_clk_disable = 0
+ */
+ reg32_write(DDRC_DBG1(0), 0x00000000);
+ reg32_write(DDRC_RFSHCTL3(0), 0x0000001);
+ reg32_write(DDRC_PWRCTL(0), 0xa0);
-void update_umctl2_rank_space_setting(unsigned int pstat_num,
- enum ddrc_type type)
-{
- unsigned int i,ddr_type;
- unsigned int rdata, tmp, tmp_t;
- unsigned int ddrc_w2r,ddrc_r2w,ddrc_wr_gap,ddrc_rd_gap;
- unsigned long addr_slot;
+ pr_debug("checking ddr type\n");
+ /*
+ * below is first read, so if boot hangs here, imx8m*_early_clock_init()
+ * might not have been called
+ */
+ tmp = reg32_read(DDRC_MSTR(0));
+ if (tmp & (0x1 << 5) && dram->ddrc_type != DDRC_TYPE_MN)
+ reg32_write(MX8M_DDRC_DDR_SS_GPR0, 0x01); /* LPDDR4 mode */
- ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
- for (i = 0; i < pstat_num; i++) {
- addr_slot = i ? (i + 1) * 0x1000 : 0;
- if (ddr_type == 0x20) {
- /* update r2w:[13:8], w2r:[5:0] */
- rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
- ddrc_w2r = rdata & 0x3f;
- if (type == DDRC_TYPE_MP)
- tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
- else
- tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
- ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+ /* determine the initial boot frequency */
+ target_freq = reg32_read(DDRC_MSTR2(0)) & 0x3;
+ target_freq = (tmp & (0x1 << 29)) ? target_freq : 0x0;
- ddrc_r2w = (rdata >> 8) & 0x3f;
- if (type == DDRC_TYPE_MP)
- tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
- else
- tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
- ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+ /* Step5: Set SWCT.sw_done to 0 */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
- tmp_t = (rdata & 0xffffc0c0) | (ddrc_r2w << 8) | ddrc_w2r;
- reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
- } else {
- /* update w2r:[5:0] */
- rdata = reg32_read(DDRC_DRAMTMG9(0) + addr_slot);
- ddrc_w2r = rdata & 0x3f;
- if (type == DDRC_TYPE_MP)
- tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
- else
- tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
- ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
- tmp_t = (rdata & 0xffffffc0) | ddrc_w2r;
- reg32_write((DDRC_DRAMTMG9(0) + addr_slot), tmp_t);
+ /* Set the default boot frequency point */
+ clrsetbits_le32(DDRC_DFIMISC(0), (0x1f << 8), target_freq << 8);
+ /* Step6: Set DFIMISC.dfi_init_complete_en to 0 */
+ clrbits_le32(DDRC_DFIMISC(0), 0x1);
- /* update r2w:[13:8] */
- rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
- ddrc_r2w = (rdata >> 8) & 0x3f;
- if (type == DDRC_TYPE_MP)
- tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
- else
- tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
- ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+ /* Step7: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+ do {
+ tmp = reg32_read(DDRC_SWSTAT(0));
+ } while ((tmp & 0x1) == 0x0);
- tmp_t = (rdata & 0xffffc0ff) | (ddrc_r2w << 8);
- reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
- }
+ /*
+ * Step8 ~ Step13: Start PHY initialization and training by
+ * accessing relevant PUB registers
+ */
+ pr_debug("ddrphy config start\n");
- if (type != DDRC_TYPE_MQ) {
- /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
- rdata = reg32_read(DDRC_RANKCTL(0) + addr_slot);
- ddrc_wr_gap = (rdata >> 8) & 0xf;
- if (type == DDRC_TYPE_MP)
- tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1);
- else
- tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1) + 1;
- ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+ ret = ddr_cfg_phy(dram, dram_timing);
+ if (ret)
+ return ret;
- ddrc_rd_gap = (rdata >> 4) & 0xf;
- if (type == DDRC_TYPE_MP)
- tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1);
- else
- tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1) + 1;
- ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+ pr_debug("ddrphy config done\n");
- tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
- reg32_write((DDRC_RANKCTL(0) + addr_slot), tmp_t);
- }
- }
+ /*
+ * step14 CalBusy.0 =1, indicates the calibrator is actively
+ * calibrating. Wait Calibrating done.
+ */
+ do {
+ tmp = reg32_read(DDRPHY_CalBusy(0));
+ } while ((tmp & 0x1));
- if (type == DDRC_TYPE_MQ) {
- /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
- rdata = reg32_read(DDRC_RANKCTL(0));
- ddrc_wr_gap = (rdata >> 8) & 0xf;
- tmp = ddrc_wr_gap + (g_cdd_ww_max[0] >> 1) + 1;
- ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+ pr_debug("ddrphy calibration done\n");
- ddrc_rd_gap = (rdata >> 4) & 0xf;
- tmp = ddrc_rd_gap + (g_cdd_rr_max[0] >> 1) + 1;
- ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+ /* Step15: Set SWCTL.sw_done to 0 */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
- tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
- reg32_write(DDRC_RANKCTL(0), tmp_t);
- }
+ /* Apply rank-to-rank workaround */
+ update_umctl2_rank_space_setting(dram_timing->fsp_msg_num - 1, dram->ddrc_type);
+
+ /* Step16: Set DFIMISC.dfi_init_start to 1 */
+ setbits_le32(DDRC_DFIMISC(0), (0x1 << 5));
+
+ /* Step17: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+ do {
+ tmp = reg32_read(DDRC_SWSTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /* Step18: Polling DFISTAT.dfi_init_complete = 1 */
+ do {
+ tmp = reg32_read(DDRC_DFISTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /* Step19: Set SWCTL.sw_done to 0 */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
+
+ /* Step20: Set DFIMISC.dfi_init_start to 0 */
+ clrbits_le32(DDRC_DFIMISC(0), (0x1 << 5));
+
+ /* Step21: optional */
+
+ /* Step22: Set DFIMISC.dfi_init_complete_en to 1 */
+ setbits_le32(DDRC_DFIMISC(0), 0x1);
+
+ /* Step23: Set PWRCTL.selfref_sw to 0 */
+ clrbits_le32(DDRC_PWRCTL(0), (0x1 << 5));
+
+ /* Step24: Set SWCTL.sw_done to 1; need polling SWSTAT.sw_done_ack */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+ do {
+ tmp = reg32_read(DDRC_SWSTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /* Step25: Wait for dwc_ddr_umctl2 to move to normal operating mode by monitoring
+ * STAT.operating_mode signal */
+ do {
+ tmp = reg32_read(DDRC_STAT(0));
+ } while ((tmp & 0x3) != 0x1);
+
+ /* Step26: Set back register in Step4 to the original values if desired */
+ reg32_write(DDRC_RFSHCTL3(0), 0x0000000);
+ /* enable selfref_en by default */
+ setbits_le32(DDRC_PWRCTL(0), 0x1);
+
+ /* enable port 0 */
+ reg32_write(DDRC_PCTRL_0(0), 0x00000001);
+ pr_debug("ddrmix config done\n");
+
+ /* save the dram timing config into memory */
+ dram_config_save(dram, dram_timing, IMX8M_SAVED_DRAM_TIMING_BASE);
+
+ return 0;
}
diff --git a/drivers/ddr/imx/imx9_ddr_init.c b/drivers/ddr/imx/imx9_ddr_init.c
new file mode 100644
index 0000000000..cdee18e4ad
--- /dev/null
+++ b/drivers/ddr/imx/imx9_ddr_init.c
@@ -0,0 +1,698 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ */
+
+#define pr_fmt(fmt) "imx9-ddr: " fmt
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <soc/imx9/ddr.h>
+#include <mach/imx/generic.h>
+#include <linux/iopoll.h>
+#include <soc/imx/clk-fracn-gppll.h>
+#include <mach/imx/imx9-regs.h>
+
+#define MX9_SRC_DPHY_BASE_ADDR (MX9_SRC_BASE_ADDR + 0x1400)
+#define REG_DDR_SDRAM_MD_CNTL (MX9_DDR_CTL_BASE + 0x120)
+#define REG_DDR_CS0_BNDS (MX9_DDR_CTL_BASE + 0x0)
+#define REG_DDR_CS1_BNDS (MX9_DDR_CTL_BASE + 0x8)
+#define REG_DDRDSR_2 (MX9_DDR_CTL_BASE + 0xB24)
+#define REG_DDR_TIMING_CFG_0 (MX9_DDR_CTL_BASE + 0x104)
+#define REG_DDR_SDRAM_CFG (MX9_DDR_CTL_BASE + 0x110)
+#define REG_DDR_TIMING_CFG_4 (MX9_DDR_CTL_BASE + 0x160)
+#define REG_DDR_DEBUG_19 (MX9_DDR_CTL_BASE + 0xF48)
+#define REG_DDR_SDRAM_CFG_3 (MX9_DDR_CTL_BASE + 0x260)
+#define REG_DDR_SDRAM_CFG_4 (MX9_DDR_CTL_BASE + 0x264)
+#define REG_DDR_SDRAM_MD_CNTL_2 (MX9_DDR_CTL_BASE + 0x270)
+#define REG_DDR_SDRAM_MPR4 (MX9_DDR_CTL_BASE + 0x28C)
+#define REG_DDR_SDRAM_MPR5 (MX9_DDR_CTL_BASE + 0x290)
+
+#define REG_DDR_ERR_EN (MX9_DDR_CTL_BASE + 0x1000)
+#define REG_SRC_DPHY_SW_CTRL (MX9_SRC_DPHY_BASE_ADDR + 0x20)
+#define REG_SRC_DPHY_SINGLE_RESET_SW_CTRL (MX9_SRC_DPHY_BASE_ADDR + 0x24)
+
+#define IMX9_SAVED_DRAM_TIMING_BASE 0x2051C000
+
+static unsigned int g_cdd_rr_max[4];
+static unsigned int g_cdd_rw_max[4];
+static unsigned int g_cdd_wr_max[4];
+static unsigned int g_cdd_ww_max[4];
+
+static void ddrphy_coldreset(void)
+{
+ /* dramphy_apb_n default 1 , assert -> 0, de_assert -> 1 */
+ /* dramphy_reset_n default 0 , assert -> 0, de_assert -> 1 */
+ /* dramphy_PwrOKIn default 0 , assert -> 1, de_assert -> 0 */
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ /* src_gen_dphy_PwrOKIn_sw_rst_de_assert() */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ mdelay(10);
+ /* src_gen_dphy_PwrOKIn_sw_rst_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert() */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+}
+
+static void check_ddrc_idle(void)
+{
+ u32 regval;
+
+ readl_poll_timeout(REG_DDRDSR_2, regval, regval & BIT(31), 0);
+}
+
+static void check_dfi_init_complete(void)
+{
+ u32 regval;
+
+ readl_poll_timeout(REG_DDRDSR_2, regval, regval & BIT(2), 0);
+
+ setbits_le32(REG_DDRDSR_2, BIT(2));
+}
+
+static void ddrc_config(struct dram_timing_info *dram_timing)
+{
+ u32 num = dram_timing->ddrc_cfg_num;
+ struct dram_cfg_param *ddrc_config;
+ int i = 0;
+
+ ddrc_config = dram_timing->ddrc_cfg;
+ for (i = 0; i < num; i++) {
+ writel(ddrc_config->val, (ulong)ddrc_config->reg);
+ ddrc_config++;
+ }
+
+ if (dram_timing->fsp_cfg) {
+ ddrc_config = dram_timing->fsp_cfg[0].ddrc_cfg;
+ while (ddrc_config->reg != 0) {
+ writel(ddrc_config->val, (ulong)ddrc_config->reg);
+ ddrc_config++;
+ }
+ }
+}
+
+static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
+ unsigned int addr_end)
+{
+ unsigned int i, imax = 0;
+
+ for (i = addr_start; i <= addr_end; i++) {
+ if (((data[i] >> 7) == 0) && data[i] > imax)
+ imax = data[i];
+ }
+
+ return imax;
+}
+
+static void get_trained_CDD(struct dram_controller *dram, u32 fsp)
+{
+ unsigned int i, tmp;
+ unsigned int cdd_cha[12], cdd_chb[12];
+ unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
+ unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
+
+ for (i = 0; i < 6; i++) {
+ tmp = dwc_ddrphy_apb_rd(dram, 0x54013 + i);
+ cdd_cha[i * 2] = tmp & 0xff;
+ cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ for (i = 0; i < 7; i++) {
+ tmp = dwc_ddrphy_apb_rd(dram, 0x5402c + i);
+
+ if (i == 0) {
+ cdd_chb[0] = (tmp >> 8) & 0xff;
+ } else if (i == 6) {
+ cdd_chb[11] = tmp & 0xff;
+ } else {
+ cdd_chb[i * 2 - 1] = tmp & 0xff;
+ cdd_chb[i * 2] = (tmp >> 8) & 0xff;
+ }
+ }
+
+ cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
+ cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
+ cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
+ cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
+ cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
+ cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
+ cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
+ cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
+ g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
+ g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
+ g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
+ g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
+}
+
+static u32 ddrc_get_fsp_reg_setting(struct dram_cfg_param *ddrc_cfg, unsigned int cfg_num, u32 reg)
+{
+ unsigned int i;
+
+ for (i = 0; i < cfg_num; i++) {
+ if (reg == ddrc_cfg[i].reg)
+ return ddrc_cfg[i].val;
+ }
+
+ return 0;
+}
+
+static void ddrc_update_fsp_reg_setting(struct dram_cfg_param *ddrc_cfg, int cfg_num,
+ u32 reg, u32 val)
+{
+ unsigned int i;
+
+ for (i = 0; i < cfg_num; i++) {
+ if (reg == ddrc_cfg[i].reg) {
+ ddrc_cfg[i].val = val;
+ return;
+ }
+ }
+}
+
+static void update_umctl2_rank_space_setting(struct dram_timing_info *dram_timing,
+ unsigned int pstat_num)
+{
+ u32 tmp, tmp_t;
+ u32 wwt, rrt, wrt, rwt;
+ u32 ext_wwt, ext_rrt, ext_wrt, ext_rwt;
+ u32 max_wwt, max_rrt, max_wrt, max_rwt;
+ u32 i;
+
+ for (i = 0; i < pstat_num; i++) {
+ /* read wwt, rrt, wrt, rwt fields from timing_cfg_0 */
+ if (!dram_timing->fsp_cfg_num) {
+ tmp = ddrc_get_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_0);
+ } else {
+ tmp = ddrc_get_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_0);
+ }
+ wwt = (tmp >> 24) & 0x3;
+ rrt = (tmp >> 26) & 0x3;
+ wrt = (tmp >> 28) & 0x3;
+ rwt = (tmp >> 30) & 0x3;
+
+ /* read rxt_wwt, ext_rrt, ext_wrt, ext_rwt fields from timing_cfg_4 */
+ if (!dram_timing->fsp_cfg_num) {
+ tmp_t = ddrc_get_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_4);
+ } else {
+ tmp_t = ddrc_get_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_4);
+ }
+ ext_wwt = (tmp_t >> 8) & 0x3;
+ ext_rrt = (tmp_t >> 10) & 0x3;
+ ext_wrt = (tmp_t >> 12) & 0x3;
+ ext_rwt = (tmp_t >> 14) & 0x3;
+
+ wwt = (ext_wwt << 2) | wwt;
+ rrt = (ext_rrt << 2) | rrt;
+ wrt = (ext_wrt << 2) | wrt;
+ rwt = (ext_rwt << 2) | rwt;
+
+ max_wwt = max(g_cdd_ww_max[0], wwt);
+ max_rrt = max(g_cdd_rr_max[0], rrt);
+ max_wrt = max(g_cdd_wr_max[0], wrt);
+ max_rwt = max(g_cdd_rw_max[0], rwt);
+ /* verify values to see if are bigger then 15 (4 bits) */
+ if (max_wwt > 15)
+ max_wwt = 15;
+ if (max_rrt > 15)
+ max_rrt = 15;
+ if (max_wrt > 15)
+ max_wrt = 15;
+ if (max_rwt > 15)
+ max_rwt = 15;
+
+ /* recalculate timings for controller registers */
+ wwt = max_wwt & 0x3;
+ rrt = max_rrt & 0x3;
+ wrt = max_wrt & 0x3;
+ rwt = max_rwt & 0x3;
+
+ ext_wwt = (max_wwt & 0xC) >> 2;
+ ext_rrt = (max_rrt & 0xC) >> 2;
+ ext_wrt = (max_wrt & 0xC) >> 2;
+ ext_rwt = (max_rwt & 0xC) >> 2;
+
+ /* update timing_cfg_0 and timing_cfg_4 */
+ tmp = (tmp & 0x00ffffff) | (rwt << 30) | (wrt << 28) |
+ (rrt << 26) | (wwt << 24);
+ tmp_t = (tmp_t & 0xFFFF00FF) | (ext_rwt << 14) |
+ (ext_wrt << 12) | (ext_rrt << 10) | (ext_wwt << 8);
+
+ if (!dram_timing->fsp_cfg_num) {
+ ddrc_update_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_0, tmp);
+ ddrc_update_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_4, tmp_t);
+ } else {
+ ddrc_update_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_0, tmp);
+ ddrc_update_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_4, tmp_t);
+ }
+ }
+}
+
+static u32 ddrc_mrr(u32 chip_select, u32 mode_reg_num, u32 *mode_reg_val)
+{
+ u32 temp;
+
+ writel(0x80000000, REG_DDR_SDRAM_MD_CNTL_2);
+ temp = 0x80000000 | (chip_select << 28) | (mode_reg_num << 0);
+ writel(temp, REG_DDR_SDRAM_MD_CNTL);
+ while ((readl(REG_DDR_SDRAM_MD_CNTL) & 0x80000000) == 0x80000000)
+ ;
+ while (!(readl(REG_DDR_SDRAM_MPR5)))
+ ;
+ *mode_reg_val = (readl(REG_DDR_SDRAM_MPR4) & 0xFF0000) >> 16;
+ writel(0x0, REG_DDR_SDRAM_MPR5);
+ while ((readl(REG_DDR_SDRAM_MPR5)))
+ ;
+ writel(0x0, REG_DDR_SDRAM_MPR4);
+ writel(0x0, REG_DDR_SDRAM_MD_CNTL_2);
+
+ return 0;
+}
+
+static void ddrc_mrs(u32 cs_sel, u32 opcode, u32 mr)
+{
+ u32 regval;
+
+ regval = (cs_sel << 28) | (opcode << 6) | (mr);
+ writel(regval, REG_DDR_SDRAM_MD_CNTL);
+ setbits_le32(REG_DDR_SDRAM_MD_CNTL, BIT(31));
+ check_ddrc_idle();
+}
+
+static u32 lpddr4_mr_read(u32 mr_rank, u32 mr_addr)
+{
+ u32 chip_select, regval;
+
+ if (mr_rank == 1)
+ chip_select = 0; /* CS0 */
+ else if (mr_rank == 2)
+ chip_select = 1; /* CS1 */
+ else
+ chip_select = 4; /* CS0 & CS1 */
+
+ ddrc_mrr(chip_select, mr_addr, &regval);
+
+ return regval;
+}
+
+static void update_mr_fsp_op0(struct dram_cfg_param *cfg, unsigned int num)
+{
+ int i;
+
+ ddrc_mrs(0x4, 0x88, 13); /* FSP-OP->1, FSP-WR->0, VRCG=1, DMD=0 */
+ for (i = 0; i < num; i++) {
+ if (cfg[i].reg)
+ ddrc_mrs(0x4, cfg[i].val, cfg[i].reg);
+ }
+ ddrc_mrs(0x4, 0xc0, 13); /* FSP-OP->1, FSP-WR->1, VRCG=0, DMD=0 */
+}
+
+static void save_trained_mr12_14(struct dram_cfg_param *cfg, u32 cfg_num, u32 mr12, u32 mr14)
+{
+ int i;
+
+ for (i = 0; i < cfg_num; i++) {
+ if (cfg->reg == 12)
+ cfg->val = mr12;
+ else if (cfg->reg == 14)
+ cfg->val = mr14;
+ cfg++;
+ }
+}
+
+#define MHZ(x) ((x) * 1000000UL)
+
+#define SHARED_GPR_DRAM_CLK 2
+#define SHARED_GPR_DRAM_CLK_SEL_PLL 0
+#define SHARED_GPR_DRAM_CLK_SEL_CCM BIT(0)
+
+static struct imx_fracn_gppll_rate_table imx9_fracpll_tbl[] = {
+ { .rate = 1000000000U, .rdiv = 1, .mfi = 166, .odiv = 4, .mfn = 2, .mfd = 3 }, /* 1000MHz */
+ { .rate = 933000000U, .rdiv = 1, .mfi = 155, .odiv = 4, .mfn = 1, .mfd = 2 }, /* 933MHz */
+ { .rate = 700000000U, .rdiv = 1, .mfi = 145, .odiv = 5, .mfn = 5, .mfd = 6 }, /* 700MHz */
+ { .rate = 484000000U, .rdiv = 1, .mfi = 121, .odiv = 6, .mfn = 0, .mfd = 1 }, /* 480MHz */
+ { .rate = 445333333U, .rdiv = 1, .mfi = 167, .odiv = 9, .mfn = 0, .mfd = 1 },
+ { .rate = 466000000U, .rdiv = 1, .mfi = 155, .odiv = 8, .mfn = 1, .mfd = 3 }, /* 466MHz */
+ { .rate = 400000000U, .rdiv = 1, .mfi = 200, .odiv = 12, .mfn = 0, .mfd = 1 }, /* 400MHz */
+ { .rate = 300000000U, .rdiv = 1, .mfi = 150, .odiv = 12, .mfn = 0, .mfd = 1 },
+};
+
+static int dram_pll_init(u32 freq)
+{
+ return fracn_gppll_set_rate(IOMEM(MX9_ANATOP_DRAM_PLL_BASE_ADDR),
+ CLK_FRACN_GPPLL_FRACN, imx9_fracpll_tbl,
+ ARRAY_SIZE(imx9_fracpll_tbl), freq);
+}
+
+static void ccm_shared_gpr_set(u32 gpr, u32 val)
+{
+ writel(val, IOMEM(MX9_CCM_BASE_ADDR + 0x4800));
+}
+
+#define DRAM_ALT_CLK_ROOT 76
+#define DRAM_APB_CLK_ROOT 77
+
+#define CLK_ROOT_MUX GENMASK(9, 8)
+#define CLK_ROOT_DIV GENMASK(9, 0)
+
+static void ccm_clk_root_cfg(u32 clk_root_id, int mux, u32 div)
+{
+ void __iomem *base = IOMEM(MX9_CCM_BASE_ADDR) + clk_root_id * 0x80;
+
+ writel(FIELD_PREP(CLK_ROOT_MUX, mux) | FIELD_PREP(CLK_ROOT_DIV, div - 1), base);
+};
+
+static void dram_enable_bypass(ulong clk_val)
+{
+ switch (clk_val) {
+ case MHZ(625):
+ ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, 3, 1);
+ break;
+ case MHZ(400):
+ ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, 2, 2);
+ break;
+ case MHZ(333):
+ ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, 1, 3);
+ break;
+ case MHZ(200):
+ ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, 2, 4);
+ break;
+ case MHZ(100):
+ ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, 2, 8);
+ break;
+ default:
+ printf("No matched freq table %lu\n", clk_val);
+ return;
+ }
+
+ /* Set DRAM APB to 133Mhz */
+ ccm_clk_root_cfg(DRAM_APB_CLK_ROOT, 2, 3);
+ /* Switch from DRAM clock root from PLL to CCM */
+ ccm_shared_gpr_set(SHARED_GPR_DRAM_CLK, SHARED_GPR_DRAM_CLK_SEL_CCM);
+}
+
+static void dram_disable_bypass(void)
+{
+ /* Set DRAM APB to 133Mhz */
+ ccm_clk_root_cfg(DRAM_APB_CLK_ROOT, 2, 3);
+ /* Switch from DRAM clock root from CCM to PLL */
+ ccm_shared_gpr_set(SHARED_GPR_DRAM_CLK, SHARED_GPR_DRAM_CLK_SEL_PLL);
+}
+
+static void ddrphy_init_set_dfi_clk(struct dram_controller *dram, unsigned int drate_mhz)
+{
+ switch (drate_mhz) {
+ case 4000:
+ dram_pll_init(MHZ(1000));
+ dram_disable_bypass();
+ break;
+ case 3733:
+ case 3732:
+ dram_pll_init(MHZ(933));
+ dram_disable_bypass();
+ break;
+ case 3200:
+ dram_pll_init(MHZ(800));
+ dram_disable_bypass();
+ break;
+ case 3000:
+ dram_pll_init(MHZ(750));
+ dram_disable_bypass();
+ break;
+ case 2800:
+ dram_pll_init(MHZ(700));
+ dram_disable_bypass();
+ break;
+ case 2400:
+ dram_pll_init(MHZ(600));
+ dram_disable_bypass();
+ break;
+ case 1866:
+ dram_pll_init(MHZ(466));
+ dram_disable_bypass();
+ break;
+ case 1600:
+ dram_pll_init(MHZ(400));
+ dram_disable_bypass();
+ break;
+ case 1066:
+ dram_pll_init(MHZ(266));
+ dram_disable_bypass();
+ break;
+ case 667:
+ dram_pll_init(MHZ(167));
+ dram_disable_bypass();
+ break;
+ case 625:
+ dram_enable_bypass(MHZ(625));
+ break;
+ case 400:
+ dram_enable_bypass(MHZ(400));
+ break;
+ case 333:
+ dram_enable_bypass(MHZ(333));
+ break;
+ case 200:
+ dram_enable_bypass(MHZ(200));
+ break;
+ case 100:
+ dram_enable_bypass(MHZ(100));
+ break;
+ default:
+ return;
+ }
+}
+
+static u32 ddrphy_addr_remap(u32 paddr_apb_from_ctlr)
+{
+ u32 paddr_apb_qual;
+ u32 paddr_apb_unqual_dec_22_13;
+ u32 paddr_apb_unqual_dec_19_13;
+ u32 paddr_apb_unqual_dec_12_1;
+ u32 paddr_apb_unqual;
+ u32 paddr_apb_phy;
+
+ paddr_apb_qual = (paddr_apb_from_ctlr << 1);
+ paddr_apb_unqual_dec_22_13 = ((paddr_apb_qual & 0x7fe000) >> 13);
+ paddr_apb_unqual_dec_12_1 = ((paddr_apb_qual & 0x1ffe) >> 1);
+
+ switch (paddr_apb_unqual_dec_22_13) {
+ case 0x000 ... 0x00b:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13;
+ break;
+ case 0x100 ... 0x10b:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x100 + 0xc;
+ break;
+ case 0x200 ... 0x20b:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x200 + 0x18;
+ break;
+ case 0x300 ... 0x30b:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x300 + 0x24;
+ break;
+ case 0x010 ... 0x019:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x10 + 0x30;
+ break;
+ case 0x110 ... 0x119:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x110 + 0x3a;
+ break;
+ case 0x210 ... 0x219:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x210 + 0x44;
+ break;
+ case 0x310 ... 0x319:
+ paddr_apb_unqual_dec_19_13 = paddr_apb_unqual_dec_22_13 - 0x310 + 0x4e;
+ break;
+ case 0x020:
+ paddr_apb_unqual_dec_19_13 = 0x58;
+ break;
+ case 0x120:
+ paddr_apb_unqual_dec_19_13 = 0x59;
+ break;
+ case 0x220:
+ paddr_apb_unqual_dec_19_13 = 0x5a;
+ break;
+ case 0x320:
+ paddr_apb_unqual_dec_19_13 = 0x5b;
+ break;
+ case 0x040:
+ paddr_apb_unqual_dec_19_13 = 0x5c;
+ break;
+ case 0x140:
+ paddr_apb_unqual_dec_19_13 = 0x5d;
+ break;
+ case 0x240:
+ paddr_apb_unqual_dec_19_13 = 0x5e;
+ break;
+ case 0x340:
+ paddr_apb_unqual_dec_19_13 = 0x5f;
+ break;
+ case 0x050:
+ paddr_apb_unqual_dec_19_13 = 0x60;
+ break;
+ case 0x051:
+ paddr_apb_unqual_dec_19_13 = 0x61;
+ break;
+ case 0x052:
+ paddr_apb_unqual_dec_19_13 = 0x62;
+ break;
+ case 0x053:
+ paddr_apb_unqual_dec_19_13 = 0x63;
+ break;
+ case 0x054:
+ paddr_apb_unqual_dec_19_13 = 0x64;
+ break;
+ case 0x055:
+ paddr_apb_unqual_dec_19_13 = 0x65;
+ break;
+ case 0x056:
+ paddr_apb_unqual_dec_19_13 = 0x66;
+ break;
+ case 0x057:
+ paddr_apb_unqual_dec_19_13 = 0x67;
+ break;
+ case 0x070:
+ paddr_apb_unqual_dec_19_13 = 0x68;
+ break;
+ case 0x090:
+ paddr_apb_unqual_dec_19_13 = 0x69;
+ break;
+ case 0x190:
+ paddr_apb_unqual_dec_19_13 = 0x6a;
+ break;
+ case 0x290:
+ paddr_apb_unqual_dec_19_13 = 0x6b;
+ break;
+ case 0x390:
+ paddr_apb_unqual_dec_19_13 = 0x6c;
+ break;
+ case 0x0c0:
+ paddr_apb_unqual_dec_19_13 = 0x6d;
+ break;
+ case 0x0d0:
+ paddr_apb_unqual_dec_19_13 = 0x6e;
+ break;
+ default:
+ paddr_apb_unqual_dec_19_13 = 0x00;
+ break;
+ }
+
+ paddr_apb_unqual = (paddr_apb_unqual_dec_19_13 << 13) | (paddr_apb_unqual_dec_12_1 << 1);
+
+ paddr_apb_phy = paddr_apb_unqual << 1;
+
+ return paddr_apb_phy;
+}
+
+struct dram_controller imx9_dram_controller = {
+ .phy_base = IOMEM(MX9_DDR_PHY_BASE),
+ .phy_remap = ddrphy_addr_remap,
+ .get_trained_CDD = get_trained_CDD,
+ .set_dfi_clk = ddrphy_init_set_dfi_clk,
+};
+
+int imx9_ddr_init(struct dram_timing_info *dram_timing, enum dram_type dram_type)
+{
+ unsigned int initial_drate;
+ struct dram_timing_info *saved_timing;
+ void *fsp;
+ int ret;
+ u32 mr12, mr14;
+ u32 regval;
+ struct dram_controller *dram = &imx9_dram_controller;
+
+ debug("DDRINFO: start DRAM init\n");
+
+ dram->dram_type = dram_type;
+
+ /* reset ddrphy */
+ ddrphy_coldreset();
+
+ debug("DDRINFO: cfg clk\n");
+
+ initial_drate = dram_timing->fsp_msg[0].drate;
+ /* default to the frequency point 0 clock */
+ ddrphy_init_set_dfi_clk(dram, initial_drate);
+
+ /*
+ * Start PHY initialization and training by
+ * accessing relevant PUB registers
+ */
+ debug("DDRINFO:ddrphy config start\n");
+
+ ret = ddr_cfg_phy(dram, dram_timing);
+ if (ret)
+ return ret;
+
+ debug("DDRINFO: ddrphy config done\n");
+
+ update_umctl2_rank_space_setting(dram_timing, dram_timing->fsp_msg_num - 1);
+
+ /* rogram the ddrc registers */
+ debug("DDRINFO: ddrc config start\n");
+ ddrc_config(dram_timing);
+ debug("DDRINFO: ddrc config done\n");
+
+ writel(0x200000, REG_DDR_DEBUG_19);
+
+ check_dfi_init_complete();
+
+ regval = readl(REG_DDR_SDRAM_CFG);
+ writel((regval | 0x80000000), REG_DDR_SDRAM_CFG);
+
+ check_ddrc_idle();
+
+ mr12 = lpddr4_mr_read(1, 12);
+ mr14 = lpddr4_mr_read(1, 14);
+
+ /* save the dram timing config into memory */
+ fsp = dram_config_save(dram, dram_timing, IMX9_SAVED_DRAM_TIMING_BASE);
+
+ saved_timing = (struct dram_timing_info *)IMX9_SAVED_DRAM_TIMING_BASE;
+ saved_timing->fsp_cfg = fsp;
+ saved_timing->fsp_cfg_num = dram_timing->fsp_cfg_num;
+ if (saved_timing->fsp_cfg_num) {
+ memcpy(saved_timing->fsp_cfg, dram_timing->fsp_cfg,
+ dram_timing->fsp_cfg_num * sizeof(struct dram_fsp_cfg));
+
+ save_trained_mr12_14(saved_timing->fsp_cfg[0].mr_cfg,
+ ARRAY_SIZE(saved_timing->fsp_cfg[0].mr_cfg), mr12, mr14);
+ /*
+ * Configure mode registers in fsp1 to mode register 0 because DDRC
+ * doesn't automatically set.
+ */
+ if (saved_timing->fsp_cfg_num > 1)
+ update_mr_fsp_op0(saved_timing->fsp_cfg[1].mr_cfg,
+ ARRAY_SIZE(saved_timing->fsp_cfg[1].mr_cfg));
+ }
+
+ return 0;
+}
diff --git a/drivers/ddr/imx8m/Kconfig b/drivers/ddr/imx8m/Kconfig
deleted file mode 100644
index 720448f551..0000000000
--- a/drivers/ddr/imx8m/Kconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-menu "i.MX8M DDR controllers"
- depends on ARCH_IMX8MQ || ARCH_IMX8MM || ARCH_IMX8MN || ARCH_IMX8MP
-
-config IMX8M_DRAM
- bool "imx8m dram controller support"
-
-endmenu
diff --git a/drivers/ddr/imx8m/Makefile b/drivers/ddr/imx8m/Makefile
deleted file mode 100644
index 2be313900f..0000000000
--- a/drivers/ddr/imx8m/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Copyright 2018 NXP
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
-
-pbl-$(CONFIG_IMX8M_DRAM) += helper.o ddrphy_utils.o ddrphy_train.o ddrphy_csr.o ddr_init.o
diff --git a/drivers/ddr/imx8m/ddr_init.c b/drivers/ddr/imx8m/ddr_init.c
deleted file mode 100644
index 18969ddb53..0000000000
--- a/drivers/ddr/imx8m/ddr_init.c
+++ /dev/null
@@ -1,213 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2018-2019 NXP
- */
-
-#define pr_fmt(fmt) "imx8m-ddr: " fmt
-
-#include <common.h>
-#include <errno.h>
-#include <io.h>
-#include <soc/imx8m/ddr.h>
-#include <mach/generic.h>
-#include <mach/imx8m-regs.h>
-#include <mach/imx8m-ccm-regs.h>
-
-bool imx8m_ddr_old_spreadsheet = true;
-
-static void ddr_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num)
-{
- int i = 0;
-
- for (i = 0; i < num; i++) {
- if (ddrc_cfg->reg == DDRC_ADDRMAP7(0))
- imx8m_ddr_old_spreadsheet = false;
- reg32_write((unsigned long)ddrc_cfg->reg, ddrc_cfg->val);
- ddrc_cfg++;
- }
-
- /*
- * Older NXP DDR configuration spreadsheets don't initialize ADDRMAP7,
- * which falsifies the memory size read back from the controller
- * in barebox proper.
- */
- if (imx8m_ddr_old_spreadsheet) {
- pr_warn("Working around old spreadsheet. Please regenerate\n");
- /*
- * Alternatively, stick { DDRC_ADDRMAP7(0), 0xf0f } into
- * struct dram_timing_info::ddrc_cfg of your old timing file
- */
- reg32_write(DDRC_ADDRMAP7(0), 0xf0f);
- }
-}
-
-/*
- * We store the timing parameters here. the TF-A will pick these up.
- * Note that the timing used we leave the driver with is a PLL bypass 25MHz
- * mode. So if your board runs horribly slow you'll likely have to provide a
- * TF-A binary.
- */
-#define IMX8M_SAVED_DRAM_TIMING_BASE 0x180000
-
-int imx8m_ddr_init(struct dram_timing_info *dram_timing,
- unsigned type)
-{
- unsigned long src_ddrc_rcr = MX8M_SRC_DDRC_RCR_ADDR;
- unsigned int tmp, initial_drate, target_freq;
- enum ddrc_type ddrc_type = get_ddrc_type(type);
- int ret;
-
- pr_debug("start DRAM init\n");
-
- /* Step1: Follow the power up procedure */
- switch (ddrc_type) {
- case DDRC_TYPE_MQ:
- reg32_write(src_ddrc_rcr + 0x04, 0x8f00000f);
- reg32_write(src_ddrc_rcr, 0x8f00000f);
- reg32_write(src_ddrc_rcr + 0x04, 0x8f000000);
- break;
- case DDRC_TYPE_MM:
- case DDRC_TYPE_MN:
- case DDRC_TYPE_MP:
- reg32_write(src_ddrc_rcr, 0x8f00001f);
- reg32_write(src_ddrc_rcr, 0x8f00000f);
- break;
- }
-
- pr_debug("cfg clk\n");
-
- /* disable iso */
- reg32_write(0x303A00EC, 0x0000ffff); /* PGC_CPU_MAPPING */
- reg32setbit(0x303A00F8, 5); /* PU_PGC_SW_PUP_REQ */
-
- initial_drate = dram_timing->fsp_msg[0].drate;
- /* default to the frequency point 0 clock */
- ddrphy_init_set_dfi_clk(initial_drate, ddrc_type);
-
- /* D-aasert the presetn */
- reg32_write(src_ddrc_rcr, 0x8F000006);
-
- /* Step2: Program the dwc_ddr_umctl2 registers */
- pr_debug("ddrc config start\n");
- ddr_cfg_umctl2(dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num);
- pr_debug("ddrc config done\n");
-
- /* Step3: De-assert reset signal(core_ddrc_rstn & aresetn_n) */
- reg32_write(src_ddrc_rcr, 0x8F000004);
- reg32_write(src_ddrc_rcr, 0x8F000000);
-
- /*
- * Step4: Disable auto-refreshes, self-refresh, powerdown, and
- * assertion of dfi_dram_clk_disable by setting RFSHCTL3.dis_auto_refresh = 1,
- * PWRCTL.powerdown_en = 0, and PWRCTL.selfref_en = 0,
- * PWRCTL.en_dfi_dram_clk_disable = 0
- */
- reg32_write(DDRC_DBG1(0), 0x00000000);
- reg32_write(DDRC_RFSHCTL3(0), 0x0000001);
- reg32_write(DDRC_PWRCTL(0), 0xa0);
-
- /* if ddr type is LPDDR4, do it */
- tmp = reg32_read(DDRC_MSTR(0));
- if (tmp & (0x1 << 5) && ddrc_type != DDRC_TYPE_MN)
- reg32_write(DDRC_DDR_SS_GPR0, 0x01); /* LPDDR4 mode */
-
- /* determine the initial boot frequency */
- target_freq = reg32_read(DDRC_MSTR2(0)) & 0x3;
- target_freq = (tmp & (0x1 << 29)) ? target_freq : 0x0;
-
- /* Step5: Set SWCT.sw_done to 0 */
- reg32_write(DDRC_SWCTL(0), 0x00000000);
-
- /* Set the default boot frequency point */
- clrsetbits_le32(DDRC_DFIMISC(0), (0x1f << 8), target_freq << 8);
- /* Step6: Set DFIMISC.dfi_init_complete_en to 0 */
- clrbits_le32(DDRC_DFIMISC(0), 0x1);
-
- /* Step7: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */
- reg32_write(DDRC_SWCTL(0), 0x00000001);
- do {
- tmp = reg32_read(DDRC_SWSTAT(0));
- } while ((tmp & 0x1) == 0x0);
-
- /*
- * Step8 ~ Step13: Start PHY initialization and training by
- * accessing relevant PUB registers
- */
- pr_debug("ddrphy config start\n");
-
- ret = ddr_cfg_phy(dram_timing, type);
- if (ret)
- return ret;
-
- pr_debug("ddrphy config done\n");
-
- /*
- * step14 CalBusy.0 =1, indicates the calibrator is actively
- * calibrating. Wait Calibrating done.
- */
- do {
- tmp = reg32_read(DDRPHY_CalBusy(0));
- } while ((tmp & 0x1));
-
- pr_debug("ddrphy calibration done\n");
-
- /* Step15: Set SWCTL.sw_done to 0 */
- reg32_write(DDRC_SWCTL(0), 0x00000000);
-
- /* Apply rank-to-rank workaround */
- update_umctl2_rank_space_setting(dram_timing->fsp_msg_num - 1, ddrc_type);
-
- /* Step16: Set DFIMISC.dfi_init_start to 1 */
- setbits_le32(DDRC_DFIMISC(0), (0x1 << 5));
-
- /* Step17: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */
- reg32_write(DDRC_SWCTL(0), 0x00000001);
- do {
- tmp = reg32_read(DDRC_SWSTAT(0));
- } while ((tmp & 0x1) == 0x0);
-
- /* Step18: Polling DFISTAT.dfi_init_complete = 1 */
- do {
- tmp = reg32_read(DDRC_DFISTAT(0));
- } while ((tmp & 0x1) == 0x0);
-
- /* Step19: Set SWCTL.sw_done to 0 */
- reg32_write(DDRC_SWCTL(0), 0x00000000);
-
- /* Step20: Set DFIMISC.dfi_init_start to 0 */
- clrbits_le32(DDRC_DFIMISC(0), (0x1 << 5));
-
- /* Step21: optional */
-
- /* Step22: Set DFIMISC.dfi_init_complete_en to 1 */
- setbits_le32(DDRC_DFIMISC(0), 0x1);
-
- /* Step23: Set PWRCTL.selfref_sw to 0 */
- clrbits_le32(DDRC_PWRCTL(0), (0x1 << 5));
-
- /* Step24: Set SWCTL.sw_done to 1; need polling SWSTAT.sw_done_ack */
- reg32_write(DDRC_SWCTL(0), 0x00000001);
- do {
- tmp = reg32_read(DDRC_SWSTAT(0));
- } while ((tmp & 0x1) == 0x0);
-
- /* Step25: Wait for dwc_ddr_umctl2 to move to normal operating mode by monitoring
- * STAT.operating_mode signal */
- do {
- tmp = reg32_read(DDRC_STAT(0));
- } while ((tmp & 0x3) != 0x1);
-
- /* Step26: Set back register in Step4 to the original values if desired */
- reg32_write(DDRC_RFSHCTL3(0), 0x0000000);
- /* enable selfref_en by default */
- setbits_le32(DDRC_PWRCTL(0), 0x1);
-
- /* enable port 0 */
- reg32_write(DDRC_PCTRL_0(0), 0x00000001);
- pr_debug(" ddrmix config done\n");
-
- /* save the dram timing config into memory */
- dram_config_save(dram_timing, IMX8M_SAVED_DRAM_TIMING_BASE);
-
- return 0;
-}
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 46b9b90d82..e7516466d9 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -7,4 +7,17 @@ config MXS_APBH_DMA
select STMP_DEVICE
help
Experimental!
+
+config OF_DMA_COHERENCY
+ bool "Respect device tree DMA coherency settings" if COMPILE_TEST
+ depends on HAS_DMA && OFDEVICE
+ help
+ For most platforms supported, either all DMA is coherent or it isn't.
+ Platforms that have DMA masters of mixed coherency or that differ
+ from the architecture default will select this option to parse
+ DMA coherency out of the DT. This allows barebox to choose the
+ correct cache maintenance operation during runtime and will cause
+ barebox to fix up its own DMA coherency setting into the kernel
+ DT if it differs.
+
endmenu
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 39829cab50..77bd8abba5 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_MXS_APBH_DMA) += apbh_dma.o
obj-$(CONFIG_HAS_DMA) += map.o
+obj-$(CONFIG_DMA_API_DEBUG) += debug.o
+obj-$(CONFIG_MXS_APBH_DMA) += apbh_dma.o
+obj-$(CONFIG_OF_DMA_COHERENCY) += of_fixups.o
diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c
index 83bd783d34..2f19033aaf 100644
--- a/drivers/dma/apbh_dma.c
+++ b/drivers/dma/apbh_dma.c
@@ -24,41 +24,12 @@
#include <init.h>
#include <io.h>
-
-#define HW_APBHX_CTRL0 0x000
-#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
-#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
-#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8
-#define BP_APBH_CTRL0_RESET_CHANNEL 16
-#define HW_APBHX_CTRL1 0x010
-#define BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN 16
-#define HW_APBHX_CTRL2 0x020
-#define HW_APBHX_CHANNEL_CTRL 0x030
-#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16
-#define BP_APBHX_VERSION_MAJOR 24
-#define HW_APBHX_CHn_NXTCMDAR_MX23(n) (0x050 + (n) * 0x70)
-#define HW_APBHX_CHn_NXTCMDAR_MX28(n) (0x110 + (n) * 0x70)
-#define HW_APBHX_CHn_SEMA_MX23(n) (0x080 + (n) * 0x70)
-#define HW_APBHX_CHn_SEMA_MX28(n) (0x140 + (n) * 0x70)
-#define BM_APBHX_CHn_SEMA_PHORE (0xff << 16)
-#define BP_APBHX_CHn_SEMA_PHORE 16
-
-static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS];
-
-enum mxs_dma_id {
- UNKNOWN_DMA_ID,
- IMX23_DMA,
- IMX28_DMA,
-};
-
struct apbh_dma {
void __iomem *regs;
struct clk *clk;
enum mxs_dma_id id;
};
-#define apbh_dma_is_imx23(aphb) ((apbh)->id == IMX23_DMA)
-
static struct apbh_dma *apbh_dma;
/*
@@ -66,185 +37,9 @@ static struct apbh_dma *apbh_dma;
*/
static int mxs_dma_validate_chan(int channel)
{
- struct mxs_dma_chan *pchan;
-
if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
return -EINVAL;
- pchan = mxs_dma_channels + channel;
- if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED))
- return -EINVAL;
-
- return 0;
-}
-
-/*
- * Return the address of the command within a descriptor.
- */
-static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)
-{
- return desc->address + offsetof(struct mxs_dma_desc, cmd);
-}
-
-/*
- * Read a DMA channel's hardware semaphore.
- *
- * As used by the MXS platform's DMA software, the DMA channel's hardware
- * semaphore reflects the number of DMA commands the hardware will process, but
- * has not yet finished. This is a volatile value read directly from hardware,
- * so it must be be viewed as immediately stale.
- *
- * If the channel is not marked busy, or has finished processing all its
- * commands, this value should be zero.
- *
- * See mxs_dma_append() for details on how DMA command blocks must be configured
- * to maintain the expected behavior of the semaphore's value.
- */
-static int mxs_dma_read_semaphore(int channel)
-{
- struct apbh_dma *apbh = apbh_dma;
- uint32_t tmp;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- if (apbh_dma_is_imx23(apbh))
- tmp = readl(apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
- else
- tmp = readl(apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
-
- tmp &= BM_APBHX_CHn_SEMA_PHORE;
- tmp >>= BP_APBHX_CHn_SEMA_PHORE;
-
- return tmp;
-}
-
-/*
- * Enable a DMA channel.
- *
- * If the given channel has any DMA descriptors on its active list, this
- * function causes the DMA hardware to begin processing them.
- *
- * This function marks the DMA channel as "busy," whether or not there are any
- * descriptors to process.
- */
-static int mxs_dma_enable(int channel)
-{
- struct apbh_dma *apbh = apbh_dma;
- unsigned int sem;
- struct mxs_dma_chan *pchan;
- struct mxs_dma_desc *pdesc;
- int channel_bit, ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- if (pchan->pending_num == 0) {
- pchan->flags |= MXS_DMA_FLAGS_BUSY;
- return 0;
- }
-
- pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node);
- if (pdesc == NULL)
- return -EFAULT;
-
- if (pchan->flags & MXS_DMA_FLAGS_BUSY) {
- if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN))
- return 0;
-
- sem = mxs_dma_read_semaphore(channel);
- if (sem == 0)
- return 0;
-
- if (sem == 1) {
- pdesc = list_entry(pdesc->node.next,
- struct mxs_dma_desc, node);
- if (apbh_dma_is_imx23(apbh))
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel));
- else
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel));
- }
-
- if (apbh_dma_is_imx23(apbh))
- writel(pchan->pending_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
- else
- writel(pchan->pending_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
-
- pchan->active_num += pchan->pending_num;
- pchan->pending_num = 0;
- } else {
- pchan->active_num += pchan->pending_num;
- pchan->pending_num = 0;
- if (apbh_dma_is_imx23(apbh)) {
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel));
- writel(pchan->active_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
- channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL;
- } else {
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel));
- writel(pchan->active_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
- channel_bit = channel;
- }
- writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR);
- }
-
- pchan->flags |= MXS_DMA_FLAGS_BUSY;
- return 0;
-}
-
-/*
- * Disable a DMA channel.
- *
- * This function shuts down a DMA channel and marks it as "not busy." Any
- * descriptors on the active list are immediately moved to the head of the
- * "done" list, whether or not they have actually been processed by the
- * hardware. The "ready" flags of these descriptors are NOT cleared, so they
- * still appear to be active.
- *
- * This function immediately shuts down a DMA channel's hardware, aborting any
- * I/O that may be in progress, potentially leaving I/O hardware in an undefined
- * state. It is unwise to call this function if there is ANY chance the hardware
- * is still processing a command.
- */
-static int mxs_dma_disable(int channel)
-{
- struct mxs_dma_chan *pchan;
- struct apbh_dma *apbh = apbh_dma;
- int channel_bit, ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- if (!(pchan->flags & MXS_DMA_FLAGS_BUSY))
- return -EINVAL;
-
- if (apbh_dma_is_imx23(apbh))
- channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL;
- else
- channel_bit = channel + 0;
-
- writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
-
- pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
- pchan->active_num = 0;
- pchan->pending_num = 0;
- list_splice_init(&pchan->active, &pchan->done);
-
return 0;
}
@@ -254,11 +49,6 @@ static int mxs_dma_disable(int channel)
static int mxs_dma_reset(int channel)
{
struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
if (apbh_dma_is_imx23(apbh))
writel(1 << (channel + BP_APBH_CTRL0_RESET_CHANNEL),
@@ -271,30 +61,6 @@ static int mxs_dma_reset(int channel)
}
/*
- * Enable or disable DMA interrupt.
- *
- * This function enables the given DMA channel to interrupt the CPU.
- */
-static int mxs_dma_enable_irq(int channel, int enable)
-{
- struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- if (enable)
- writel(1 << (channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
- apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET);
- else
- writel(1 << (channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
- apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
-
- return 0;
-}
-
-/*
* Clear DMA interrupt.
*
* The software that is using the DMA channel must register to receive its
@@ -303,11 +69,6 @@ static int mxs_dma_enable_irq(int channel, int enable)
static int mxs_dma_ack_irq(int channel)
{
struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
writel(1 << channel, apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
writel(1 << channel, apbh->regs + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
@@ -316,229 +77,11 @@ static int mxs_dma_ack_irq(int channel)
}
/*
- * Request to reserve a DMA channel
- */
-static int mxs_dma_request(int channel)
-{
- struct mxs_dma_chan *pchan;
-
- if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
- return -EINVAL;
-
- pchan = mxs_dma_channels + channel;
- if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID)
- return -ENODEV;
-
- if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED)
- return -EBUSY;
-
- pchan->flags |= MXS_DMA_FLAGS_ALLOCATED;
- pchan->active_num = 0;
- pchan->pending_num = 0;
-
- INIT_LIST_HEAD(&pchan->active);
- INIT_LIST_HEAD(&pchan->done);
-
- return 0;
-}
-
-/*
- * Release a DMA channel.
- *
- * This function releases a DMA channel from its current owner.
- *
- * The channel will NOT be released if it's marked "busy" (see
- * mxs_dma_enable()).
- */
-static int mxs_dma_release(int channel)
-{
- struct mxs_dma_chan *pchan;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- if (pchan->flags & MXS_DMA_FLAGS_BUSY)
- return -EBUSY;
-
- pchan->dev = 0;
- pchan->active_num = 0;
- pchan->pending_num = 0;
- pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED;
-
- return 0;
-}
-
-/*
- * Allocate DMA descriptor
- */
-struct mxs_dma_desc *mxs_dma_desc_alloc(void)
-{
- struct mxs_dma_desc *pdesc;
- dma_addr_t dma_address;
-
- pdesc = dma_alloc_coherent(sizeof(struct mxs_dma_desc),
- &dma_address);
-
- if (pdesc == NULL)
- return NULL;
-
- pdesc->address = dma_address;
-
- return pdesc;
-};
-
-/*
- * Free DMA descriptor
- */
-void mxs_dma_desc_free(struct mxs_dma_desc *pdesc)
-{
- if (pdesc == NULL)
- return;
-
- free(pdesc);
-}
-
-/*
- * Add a DMA descriptor to a channel.
- *
- * If the descriptor list for this channel is not empty, this function sets the
- * CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so
- * it will chain to the new descriptor's command.
- *
- * Then, this function marks the new descriptor as "ready," adds it to the end
- * of the active descriptor list, and increments the count of pending
- * descriptors.
- *
- * The MXS platform DMA software imposes some rules on DMA commands to maintain
- * important invariants. These rules are NOT checked, but they must be carefully
- * applied by software that uses MXS DMA channels.
- *
- * Invariant:
- * The DMA channel's hardware semaphore must reflect the number of DMA
- * commands the hardware will process, but has not yet finished.
- *
- * Explanation:
- * A DMA channel begins processing commands when its hardware semaphore is
- * written with a value greater than zero, and it stops processing commands
- * when the semaphore returns to zero.
- *
- * When a channel finishes a DMA command, it will decrement its semaphore if
- * the DECREMENT_SEMAPHORE bit is set in that command's flags bits.
- *
- * In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set,
- * unless it suits the purposes of the software. For example, one could
- * construct a series of five DMA commands, with the DECREMENT_SEMAPHORE
- * bit set only in the last one. Then, setting the DMA channel's hardware
- * semaphore to one would cause the entire series of five commands to be
- * processed. However, this example would violate the invariant given above.
- *
- * Rule:
- * ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA
- * channel's hardware semaphore will be decremented EVERY time a command is
- * processed.
- */
-int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc)
-{
- struct mxs_dma_chan *pchan;
- struct mxs_dma_desc *last;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- pdesc->cmd.next = mxs_dma_cmd_address(pdesc);
- pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST;
-
- if (!list_empty(&pchan->active)) {
- last = list_entry(pchan->active.prev, struct mxs_dma_desc,
- node);
-
- pdesc->flags &= ~MXS_DMA_DESC_FIRST;
- last->flags &= ~MXS_DMA_DESC_LAST;
-
- last->cmd.next = mxs_dma_cmd_address(pdesc);
- last->cmd.data |= MXS_DMA_DESC_CHAIN;
- }
- pdesc->flags |= MXS_DMA_DESC_READY;
- if (pdesc->flags & MXS_DMA_DESC_FIRST)
- pchan->pending_num++;
- list_add_tail(&pdesc->node, &pchan->active);
-
- return ret;
-}
-
-/*
- * Clean up processed DMA descriptors.
- *
- * This function removes processed DMA descriptors from the "active" list. Pass
- * in a non-NULL list head to get the descriptors moved to your list. Pass NULL
- * to get the descriptors moved to the channel's "done" list. Descriptors on
- * the "done" list can be retrieved with mxs_dma_get_finished().
- *
- * This function marks the DMA channel as "not busy" if no unprocessed
- * descriptors remain on the "active" list.
- */
-static int mxs_dma_finish(int channel, struct list_head *head)
-{
- int sem;
- struct mxs_dma_chan *pchan;
- struct list_head *p, *q;
- struct mxs_dma_desc *pdesc;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- sem = mxs_dma_read_semaphore(channel);
- if (sem < 0)
- return sem;
-
- if (sem == pchan->active_num)
- return 0;
-
- list_for_each_safe(p, q, &pchan->active) {
- if ((pchan->active_num) <= sem)
- break;
-
- pdesc = list_entry(p, struct mxs_dma_desc, node);
- pdesc->flags &= ~MXS_DMA_DESC_READY;
-
- if (head)
- list_move_tail(p, head);
- else
- list_move_tail(p, &pchan->done);
-
- if (pdesc->flags & MXS_DMA_DESC_LAST)
- pchan->active_num--;
- }
-
- if (sem == 0)
- pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
-
- return 0;
-}
-
-/*
* Wait for DMA channel to complete
*/
static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
{
struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(chan);
- if (ret)
- return ret;
while (--timeout) {
if (readl(apbh->regs + HW_APBHX_CTRL1) & (1 << chan))
@@ -546,38 +89,47 @@ static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
udelay(1);
}
- if (timeout == 0) {
- ret = -ETIMEDOUT;
- mxs_dma_reset(chan);
- }
+ if (!timeout)
+ return -ETIMEDOUT;
- return ret;
+ return 0;
}
/*
* Execute the DMA channel
*/
-int mxs_dma_go(int chan)
+int mxs_dma_go(int chan, struct mxs_dma_cmd *cmd, int ncmds)
{
+ struct apbh_dma *apbh = apbh_dma;
uint32_t timeout = 10000;
- int ret;
+ int i, ret, channel_bit;
- LIST_HEAD(tmp_desc_list);
+ ret = mxs_dma_validate_chan(chan);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ncmds - 1; i++) {
+ cmd[i].next = (unsigned long)(&cmd[i + 1]);
+ cmd[i].data |= MXS_DMA_DESC_CHAIN;
+ }
- mxs_dma_enable_irq(chan, 1);
- mxs_dma_enable(chan);
+ if (apbh_dma_is_imx23(apbh)) {
+ writel(cmd, apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(chan));
+ writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX23(chan));
+ channel_bit = chan + BP_APBH_CTRL0_CLKGATE_CHANNEL;
+ } else {
+ writel(cmd, apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(chan));
+ writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX28(chan));
+ channel_bit = chan;
+ }
+ writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR);
/* Wait for DMA to finish. */
ret = mxs_dma_wait_complete(timeout, chan);
- /* Clear out the descriptors we just ran. */
- mxs_dma_finish(chan, &tmp_desc_list);
-
/* Shut the DMA channel down. */
mxs_dma_ack_irq(chan);
mxs_dma_reset(chan);
- mxs_dma_enable_irq(chan, 0);
- mxs_dma_disable(chan);
return ret;
}
@@ -585,11 +137,10 @@ int mxs_dma_go(int chan)
/*
* Initialize the DMA hardware
*/
-static int apbh_dma_probe(struct device_d *dev)
+static int apbh_dma_probe(struct device *dev)
{
struct resource *iores;
struct apbh_dma *apbh;
- struct mxs_dma_chan *pchan;
enum mxs_dma_id id;
int ret, channel;
@@ -627,28 +178,11 @@ static int apbh_dma_probe(struct device_d *dev)
apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
for (channel = 0; channel < MXS_MAX_DMA_CHANNELS; channel++) {
- pchan = mxs_dma_channels + channel;
- pchan->flags = MXS_DMA_FLAGS_VALID;
-
- ret = mxs_dma_request(channel);
-
- if (ret) {
- printf("MXS DMA: Can't acquire DMA channel %i\n",
- channel);
-
- goto err;
- }
-
mxs_dma_reset(channel);
mxs_dma_ack_irq(channel);
}
return 0;
-
-err:
- while (--channel >= 0)
- mxs_dma_release(channel);
- return ret;
}
static struct platform_device_id apbh_ids[] = {
@@ -674,8 +208,9 @@ static __maybe_unused struct of_device_id apbh_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, apbh_dt_ids);
-static struct driver_d apbh_dma_driver = {
+static struct driver apbh_dma_driver = {
.name = "dma-apbh",
.id_table = apbh_ids,
.of_compatible = DRV_OF_COMPAT(apbh_dt_ids),
diff --git a/drivers/dma/debug.c b/drivers/dma/debug.c
new file mode 100644
index 0000000000..32a4175044
--- /dev/null
+++ b/drivers/dma/debug.c
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <dma.h>
+#include <linux/list.h>
+#include <linux/printk.h>
+#include "debug.h"
+
+static LIST_HEAD(dma_mappings);
+
+struct dma_debug_entry {
+ struct list_head list;
+ struct device *dev;
+ dma_addr_t dev_addr;
+ size_t size;
+ int direction;
+ bool dev_owned;
+};
+
+static const char *dir2name[] = {
+ [DMA_BIDIRECTIONAL] = "bidirectional",
+ [DMA_TO_DEVICE] = "to-device",
+ [DMA_FROM_DEVICE] = "from-device",
+ [DMA_NONE] = "none",
+};
+
+#define dma_dev_printf(level, args...) do { \
+ if (level > LOGLEVEL) \
+ break; \
+ dev_printf((level), args); \
+ if ((level) <= MSG_WARNING) \
+ dump_stack(); \
+} while (0)
+
+#define dma_dev_warn(args...) dma_dev_printf(MSG_WARNING, args)
+
+static void dma_printf(int level, struct dma_debug_entry *entry,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list va;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ dma_dev_printf(level, entry->dev, "%s mapping 0x%llx+0x%zx: %pV\n",
+ dir2name[(entry)->direction], (u64)(entry)->dev_addr,
+ (entry)->size, &vaf);
+
+ va_end(va);
+}
+
+#define dma_warn(args...) dma_printf(MSG_WARNING, args)
+#define dma_debug(args...) dma_printf(MSG_DEBUG, args)
+
+static inline int region_contains(struct dma_debug_entry *entry,
+ dma_addr_t buf_start, size_t buf_size)
+{
+ dma_addr_t dev_addr_end = entry->dev_addr + entry->size - 1;
+ dma_addr_t buf_end = buf_start + buf_size - 1;
+
+ /* Is the buffer completely within the mapping? */
+ if (entry->dev_addr <= buf_start && dev_addr_end >= buf_end)
+ return 1;
+
+ /* Does the buffer partially overlap the mapping? */
+ if (entry->dev_addr <= buf_end && dev_addr_end >= buf_start)
+ return -1;
+
+ return 0;
+}
+
+static struct dma_debug_entry *
+dma_debug_entry_find(struct device *dev, dma_addr_t dev_addr, size_t size)
+{
+ struct dma_debug_entry *entry;
+
+ /*
+ * DMA functions should be called with a device argument to support
+ * non-1:1 device mappings.
+ */
+ if (!dev)
+ dma_dev_warn(NULL, "unportable NULL device passed with buffer 0x%llx+0x%zx!\n",
+ (u64)dev_addr, size);
+
+ list_for_each_entry(entry, &dma_mappings, list) {
+ if (dev != entry->dev)
+ continue;
+
+ switch (region_contains(entry, dev_addr, size)) {
+ case 1:
+ return entry;
+ case -1:
+ /* The same device shouldn't have two mappings for the same address */
+ dma_warn(entry, "unexpected partial overlap looking for 0x%llx+0x%zx!\n",
+ (u64)dev_addr, size);
+ fallthrough;
+ case 0:
+ continue;
+ }
+ }
+
+ return NULL;
+}
+
+void debug_dma_map(struct device *dev, void *addr,
+ size_t size,
+ int direction, dma_addr_t dev_addr)
+{
+ struct dma_debug_entry *entry;
+
+ entry = dma_debug_entry_find(dev, dev_addr, size);
+ if (entry) {
+ /* The same device shouldn't have two mappings for the same address */
+ dma_warn(entry, "duplicate mapping\n");
+ return;
+ }
+
+ entry = xmalloc(sizeof(*entry));
+
+ entry->dev = dev;
+ entry->dev_addr = dev_addr;
+ entry->size = size;
+ entry->direction = direction;
+ entry->dev_owned = true;
+
+ list_add(&entry->list, &dma_mappings);
+
+ dma_debug(entry, "allocated\n");
+
+ if (!IS_ALIGNED(dev_addr, DMA_ALIGNMENT))
+ dma_dev_warn(dev, "Mapping insufficiently aligned %s buffer 0x%llx+0x%zx: %u bytes required!\n",
+ dir2name[direction], (u64)addr, size, DMA_ALIGNMENT);
+}
+
+void debug_dma_unmap(struct device *dev, dma_addr_t addr,
+ size_t size, int direction)
+{
+ struct dma_debug_entry *entry;
+
+ entry = dma_debug_entry_find(dev, addr, size);
+ if (!entry) {
+ /* Potential double free */
+ dma_dev_warn(dev, "Unmapping non-mapped %s buffer 0x%llx+0x%zx!\n",
+ dir2name[direction], (u64)addr, size);
+ return;
+ }
+
+ /* Mismatched size or direction may result in memory corruption */
+ if (entry->size != size)
+ dma_warn(entry, "mismatch unmapping 0x%zx bytes\n", size);
+ if (entry->direction != direction)
+ dma_warn(entry, "mismatch unmapping %s\n",
+ dir2name[direction]);
+
+ dma_debug(entry, "deallocating\n");
+ list_del(&entry->list);
+ free(entry);
+}
+
+void debug_dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ int direction)
+{
+ struct dma_debug_entry *entry;
+
+ entry = dma_debug_entry_find(dev, dma_handle, size);
+ if (!entry) {
+ dma_dev_warn(dev, "sync for CPU of never-mapped %s buffer 0x%llx+0x%zx!\n",
+ dir2name[direction], (u64)dma_handle, size);
+ return;
+ }
+
+ if (!entry->dev_owned)
+ dma_dev_warn(dev, "unexpected sync for CPU of already CPU-mapped %s buffer 0x%llx+0x%zx!\n",
+ dir2name[direction], (u64)dma_handle, size);
+
+ entry->dev_owned = false;
+}
+
+void debug_dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle,
+ size_t size, int direction)
+{
+ struct dma_debug_entry *entry;
+
+ /*
+ * If dma_map_single was omitted, CPU cache may contain dirty cache lines
+ * for a buffer used for DMA. These lines may be evicted and written back
+ * after device DMA and before consumption by CPU, resulting in memory
+ * corruption
+ */
+ entry = dma_debug_entry_find(dev, dma_handle, size);
+ if (!entry) {
+ dma_dev_warn(dev, "Syncing for device of never-mapped %s buffer 0x%llx+0x%zx!\n",
+ dir2name[direction], (u64)dma_handle, size);
+ return;
+ }
+
+ if (entry->dev_owned)
+ dma_dev_warn(dev, "unexpected sync for device of already device-mapped %s buffer 0x%llx+0x%zx!\n",
+ dir2name[direction], (u64)dma_handle, size);
+
+ entry->dev_owned = true;
+}
diff --git a/drivers/dma/debug.h b/drivers/dma/debug.h
new file mode 100644
index 0000000000..020bb5c196
--- /dev/null
+++ b/drivers/dma/debug.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ *
+ * Author: Joerg Roedel <joerg.roedel@amd.com>
+ */
+
+#ifndef _KERNEL_DMA_DEBUG_H
+#define _KERNEL_DMA_DEBUG_H
+
+#include <linux/types.h>
+
+struct device;
+
+#ifdef CONFIG_DMA_API_DEBUG
+extern void debug_dma_map(struct device *dev, void *addr,
+ size_t size,
+ int direction, dma_addr_t dma_addr);
+
+extern void debug_dma_unmap(struct device *dev, dma_addr_t addr,
+ size_t size, int direction);
+
+extern void debug_dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ int direction);
+
+extern void debug_dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle,
+ size_t size, int direction);
+
+#else /* CONFIG_DMA_API_DEBUG */
+static inline void debug_dma_map(struct device *dev, void *addr,
+ size_t size,
+ int direction, dma_addr_t dma_addr)
+{
+}
+
+static inline void debug_dma_unmap(struct device *dev, dma_addr_t addr,
+ size_t size, int direction)
+{
+}
+
+static inline void debug_dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle,
+ size_t size, int direction)
+{
+}
+
+static inline void debug_dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle,
+ size_t size, int direction)
+{
+}
+
+#endif /* CONFIG_DMA_API_DEBUG */
+#endif /* _KERNEL_DMA_DEBUG_H */
diff --git a/drivers/dma/map.c b/drivers/dma/map.c
index a3e1b3b5b5..cd0f5c3d34 100644
--- a/drivers/dma/map.c
+++ b/drivers/dma/map.c
@@ -1,42 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* SPDX-FileCopyrightText: 2012 Marc Kleine-Budde <mkl@pengutronix.de> */
-
#include <dma.h>
+#include <driver.h>
+#include "debug.h"
+
+void *dma_alloc(size_t size)
+{
+ return xmemalign(DMA_ALIGNMENT, ALIGN(size, DMA_ALIGNMENT));
+}
-static inline dma_addr_t cpu_to_dma(struct device_d *dev, unsigned long cpu_addr)
+void *dma_zalloc(size_t size)
{
- dma_addr_t dma_addr = cpu_addr;
+ void *buf;
- if (dev)
- dma_addr -= dev->dma_offset;
+ buf = dma_alloc(size);
+ if (buf)
+ memset(buf, 0x00, size);
- return dma_addr;
+ return buf;
+}
+
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t address,
+ size_t size, enum dma_data_direction dir)
+{
+ void *ptr = dma_to_cpu(dev, address);
+
+ debug_dma_sync_single_for_cpu(dev, address, size, dir);
+
+ if (!dev_is_dma_coherent(dev))
+ arch_sync_dma_for_cpu(ptr, size, dir);
}
-static inline unsigned long dma_to_cpu(struct device_d *dev, dma_addr_t addr)
+void dma_sync_single_for_device(struct device *dev, dma_addr_t address,
+ size_t size, enum dma_data_direction dir)
{
- unsigned long cpu_addr = addr;
+ void *ptr = dma_to_cpu(dev, address);
- if (dev)
- cpu_addr += dev->dma_offset;
+ debug_dma_sync_single_for_device(dev, address, size, dir);
- return cpu_addr;
+ if (!dev_is_dma_coherent(dev))
+ arch_sync_dma_for_device(ptr, size, dir);
}
-dma_addr_t dma_map_single(struct device_d *dev, void *ptr, size_t size,
- enum dma_data_direction dir)
+dma_addr_t dma_map_single(struct device *dev, void *ptr,
+ size_t size, enum dma_data_direction dir)
{
- unsigned long addr = (unsigned long)ptr;
+ dma_addr_t dma_addr = cpu_to_dma(dev, ptr);
+
+ debug_dma_map(dev, ptr, size, dir, dma_addr);
- dma_sync_single_for_device(addr, size, dir);
+ if (!dev_is_dma_coherent(dev))
+ arch_sync_dma_for_device(ptr, size, dir);
- return cpu_to_dma(dev, addr);
+ return dma_addr;
}
-void dma_unmap_single(struct device_d *dev, dma_addr_t dma_addr, size_t size,
- enum dma_data_direction dir)
+void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size, enum dma_data_direction dir)
{
- unsigned long addr = dma_to_cpu(dev, dma_addr);
+ if (!dev_is_dma_coherent(dev))
+ dma_sync_single_for_cpu(dev, dma_addr, size, dir);
- dma_sync_single_for_cpu(addr, size, dir);
+ debug_dma_unmap(dev, dma_addr, size, dir);
}
diff --git a/drivers/dma/of_fixups.c b/drivers/dma/of_fixups.c
new file mode 100644
index 0000000000..668313bbfb
--- /dev/null
+++ b/drivers/dma/of_fixups.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <of.h>
+#include <of_address.h>
+#include <driver.h>
+
+static int of_dma_coherent_fixup(struct device_node *root, void *data)
+{
+ struct device_node *soc;
+ enum dev_dma_coherence coherency = (enum dev_dma_coherence)(uintptr_t)data;
+
+ soc = of_find_node_by_path_from(root, "/soc");
+ if (!soc)
+ return -ENOENT;
+
+ of_property_write_bool(soc, "dma-noncoherent", coherency == DEV_DMA_NON_COHERENT);
+ of_property_write_bool(soc, "dma-coherent", coherency == DEV_DMA_COHERENT);
+
+ return 0;
+}
+
+static int of_dma_coherent_fixup_register(void)
+{
+ struct device_node *soc;
+ enum dev_dma_coherence soc_dma_coherency;
+
+ soc = of_find_node_by_path("/soc");
+ if (!soc)
+ return -ENOENT;
+
+ if (of_property_read_bool(soc, "dma-coherent"))
+ soc_dma_coherency = DEV_DMA_COHERENT;
+ else if (of_property_read_bool(soc, "dma-noncoherent"))
+ soc_dma_coherency = DEV_DMA_NON_COHERENT;
+ else
+ soc_dma_coherency = DEV_DMA_COHERENCE_DEFAULT;
+
+ return of_register_fixup(of_dma_coherent_fixup, (void *)(uintptr_t)soc_dma_coherency);
+}
+coredevice_initcall(of_dma_coherent_fixup_register);
diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c
index 3103d7722a..23cb0e1fbb 100644
--- a/drivers/eeprom/at24.c
+++ b/drivers/eeprom/at24.c
@@ -99,6 +99,8 @@ static struct platform_device_id at24_ids[] = {
/* old variants can't be handled with this generic entry! */
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
+ { "24mac402", AT24_DEVICE_MAGIC(2048 / 8, AT24_FLAG_READONLY) },
+ { "24mac602", AT24_DEVICE_MAGIC(2048 / 8, AT24_FLAG_READONLY) },
/* spd is a 24c02 in memory DIMMs */
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
@@ -361,12 +363,7 @@ static int at24_nvmem_write(void *ctx, unsigned off, const void *buf, size_t cou
return at24_write(ctx, buf, off, count);
}
-static const struct nvmem_bus at24_nvmem_bus = {
- .write = at24_nvmem_write,
- .read = at24_nvmem_read,
-};
-
-static int at24_probe(struct device_d *dev)
+static int at24_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct at24_platform_data chip;
@@ -390,8 +387,8 @@ static int at24_probe(struct device_d *dev)
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
- if (dev->device_node &&
- !of_property_read_u32(dev->device_node, "pagesize", &page_size))
+ if (dev->of_node &&
+ !of_property_read_u32(dev->of_node, "pagesize", &page_size))
chip.page_size = page_size;
else {
/*
@@ -427,7 +424,7 @@ static int at24_probe(struct device_d *dev)
at24->chip = chip;
at24->num_addresses = num_addresses;
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
if (alias) {
devname = xstrdup(alias);
} else {
@@ -441,7 +438,7 @@ static int at24_probe(struct device_d *dev)
writable = !(chip.flags & AT24_FLAG_READONLY);
- if (of_get_property(dev->device_node, "read-only", NULL))
+ if (of_get_property(dev->of_node, "read-only", NULL))
writable = 0;
if (writable) {
@@ -456,10 +453,10 @@ static int at24_probe(struct device_d *dev)
}
at24->wp_gpio = -1;
- if (dev->device_node) {
+ if (dev->of_node) {
enum of_gpio_flags flags;
- at24->wp_gpio = of_get_named_gpio_flags(dev->device_node,
- "wp-gpios", 0, &flags);
+ at24->wp_gpio = of_get_named_gpio_flags(dev->of_node,
+ "wp-gpios", 0, &flags);
if (gpio_is_valid(at24->wp_gpio)) {
at24->wp_active_low = flags & OF_GPIO_ACTIVE_LOW;
gpio_request(at24->wp_gpio, "eeprom-wp");
@@ -487,7 +484,8 @@ static int at24_probe(struct device_d *dev)
at24->nvmem_config.dev = dev;
at24->nvmem_config.priv = at24;
at24->nvmem_config.read_only = !writable;
- at24->nvmem_config.bus = &at24_nvmem_bus;
+ at24->nvmem_config.reg_write = at24_nvmem_write;
+ at24->nvmem_config.reg_read = at24_nvmem_read;
at24->nvmem_config.stride = 1;
at24->nvmem_config.word_size = 1;
at24->nvmem_config.size = chip.byte_len;
@@ -515,7 +513,7 @@ err_out:
}
-static struct driver_d at24_driver = {
+static struct driver at24_driver = {
.name = "at24",
.probe = at24_probe,
.id_table = at24_ids,
diff --git a/drivers/eeprom/at25.c b/drivers/eeprom/at25.c
index ee4663270b..ca1df82122 100644
--- a/drivers/eeprom/at25.c
+++ b/drivers/eeprom/at25.c
@@ -233,7 +233,7 @@ static struct cdev_operations at25_fops = {
.write = at25_ee_write,
};
-static int at25_np_to_chip(struct device_d *dev,
+static int at25_np_to_chip(struct device *dev,
struct device_node *np,
struct spi_eeprom *chip)
{
@@ -291,7 +291,7 @@ static int at25_np_to_chip(struct device_d *dev,
return 0;
}
-static int at25_probe(struct device_d *dev)
+static int at25_probe(struct device *dev)
{
int err, sr;
int addrlen;
@@ -301,8 +301,8 @@ static int at25_probe(struct device_d *dev)
at25 = xzalloc(sizeof(*at25));
/* Chip description */
- if (dev->device_node) {
- err = at25_np_to_chip(dev, dev->device_node, &at25->chip);
+ if (dev->of_node) {
+ err = at25_np_to_chip(dev, dev->of_node, &at25->chip);
if (err)
goto fail;
} else {
@@ -354,7 +354,7 @@ static int at25_probe(struct device_d *dev)
goto fail;
dev_dbg(dev, "%s probed\n", at25->cdev.name);
- of_parse_partitions(&at25->cdev, dev->device_node);
+ of_parse_partitions(&at25->cdev, dev->of_node);
of_partitions_register_fixup(&at25->cdev);
return 0;
@@ -372,8 +372,9 @@ static __maybe_unused struct of_device_id at25_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, at25_dt_ids);
-static struct driver_d at25_driver = {
+static struct driver at25_driver = {
.name = DRIVERNAME,
.of_compatible = DRV_OF_COMPAT(at25_dt_ids),
.probe = at25_probe,
diff --git a/drivers/efi/Makefile b/drivers/efi/Makefile
index bd8fadac42..4e3c39e144 100644
--- a/drivers/efi/Makefile
+++ b/drivers/efi/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += efi-device.o
+obj-y += efi-handle.o
+obj-$(CONFIG_EFI_PAYLOAD) += efi-device.o
diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c
index af5406afa6..33c82c81dd 100644
--- a/drivers/efi/efi-device.c
+++ b/drivers/efi/efi-device.c
@@ -6,7 +6,6 @@
*/
#include <bootsource.h>
-#include <command.h>
#include <common.h>
#include <driver.h>
#include <malloc.h>
@@ -27,35 +26,13 @@ static int efi_locate_handle(enum efi_locate_search_type search_type,
unsigned long *no_handles,
efi_handle_t **buffer)
{
- efi_status_t efiret;
- unsigned long buffer_size = 0;
- efi_handle_t *buf;
-
- efiret = BS->locate_handle(search_type, protocol, search_key, &buffer_size,
- NULL);
- if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL)
- return -efi_errno(efiret);
-
- buf = malloc(buffer_size);
- if (!buf)
- return -ENOMEM;
-
- efiret = BS->locate_handle(search_type, protocol, search_key, &buffer_size,
- buf);
- if (EFI_ERROR(efiret)) {
- free(buf);
- return -efi_errno(efiret);
- }
-
- *no_handles = buffer_size / sizeof(efi_handle_t);
- *buffer = buf;
-
- return 0;
+ return __efi_locate_handle(BS, search_type, protocol, search_key, no_handles,
+ buffer);
}
-static struct efi_device *efi_find_device(efi_handle_t *handle)
+static struct efi_device *efi_find_device(efi_handle_t handle)
{
- struct device_d *dev;
+ struct device *dev;
struct efi_device *efidev;
bus_for_each_device(&efi_bus, dev) {
@@ -68,7 +45,7 @@ static struct efi_device *efi_find_device(efi_handle_t *handle)
return NULL;
}
-static void efi_devinfo(struct device_d *dev)
+static void efi_devinfo(struct device *dev)
{
struct efi_device *efidev = to_efi_device(dev);
int i;
@@ -80,10 +57,10 @@ static void efi_devinfo(struct device_d *dev)
efi_guid_string(&efidev->guids[i]));
}
-static efi_handle_t *efi_find_parent(efi_handle_t *handle)
+static efi_handle_t efi_find_parent(efi_handle_t handle)
{
unsigned long handle_count = 0;
- efi_handle_t *handles = NULL, *parent;
+ efi_handle_t *handles = NULL, parent;
unsigned long num_guids;
efi_guid_t **guids;
int ret, i, j, k;
@@ -91,7 +68,7 @@ static efi_handle_t *efi_find_parent(efi_handle_t *handle)
struct efi_open_protocol_information_entry *entry_buffer;
unsigned long entry_count;
- ret = efi_locate_handle(by_protocol, &efi_device_path_protocol_guid,
+ ret = efi_locate_handle(BY_PROTOCOL, &efi_device_path_protocol_guid,
NULL, &handle_count, &handles);
if (ret)
return NULL;
@@ -134,7 +111,7 @@ out:
return parent;
}
-static struct efi_device *efi_add_device(efi_handle_t *handle, efi_guid_t **guids,
+static struct efi_device *efi_add_device(efi_handle_t handle, efi_guid_t **guids,
int num_guids)
{
struct efi_device *efidev;
@@ -192,10 +169,11 @@ static int efi_register_device(struct efi_device *efidev)
* of the main MAC messaging device. Don't register these in barebox as
* they would show up as duplicate ethernet devices.
*/
- if (device_path_to_type(efidev->devpath) == MESSAGING_DEVICE_PATH) {
+ if (device_path_to_type(efidev->devpath) == DEVICE_PATH_TYPE_MESSAGING_DEVICE) {
u8 subtype = device_path_to_subtype(efidev->devpath);
- if (subtype == MSG_IPv4_DP || subtype == MSG_IPv6_DP)
+ if (subtype == DEVICE_PATH_SUB_TYPE_MSG_IPv4 ||
+ subtype == DEVICE_PATH_SUB_TYPE_MSG_IPv6)
return -EINVAL;
}
@@ -238,14 +216,14 @@ static int efi_register_device(struct efi_device *efidev)
void efi_register_devices(void)
{
unsigned long handle_count = 0;
- efi_handle_t *handles = NULL;
+ efi_handle_t *handles = NULL;
unsigned long num_guids;
efi_guid_t **guids;
int ret, i;
struct efi_device **efidevs;
int registered;
- ret = efi_locate_handle(by_protocol, &efi_device_path_protocol_guid,
+ ret = efi_locate_handle(BY_PROTOCOL, &efi_device_path_protocol_guid,
NULL, &handle_count, &handles);
if (ret)
return;
@@ -290,7 +268,7 @@ int efi_connect_all(void)
efi_handle_t *handle_buffer;
int i;
- efiret = BS->locate_handle_buffer(all_handles, NULL, NULL, &handle_count,
+ efiret = BS->locate_handle_buffer(ALL_HANDLES, NULL, NULL, &handle_count,
&handle_buffer);
if (EFI_ERROR(efiret))
return -efi_errno(efiret);
@@ -304,7 +282,7 @@ int efi_connect_all(void)
return 0;
}
-static int efi_bus_match(struct device_d *dev, struct driver_d *drv)
+static int efi_bus_match(struct device *dev, struct driver *drv)
{
struct efi_driver *efidrv = to_efi_driver(drv);
struct efi_device *efidev = to_efi_device(dev);
@@ -321,7 +299,7 @@ static int efi_bus_match(struct device_d *dev, struct driver_d *drv)
return 1;
}
-static int efi_bus_probe(struct device_d *dev)
+static int efi_bus_probe(struct device *dev)
{
struct efi_driver *efidrv = to_efi_driver(dev->driver);
struct efi_device *efidev = to_efi_device(dev);
@@ -329,7 +307,7 @@ static int efi_bus_probe(struct device_d *dev)
return efidrv->probe(efidev);
}
-static void efi_bus_remove(struct device_d *dev)
+static void efi_bus_remove(struct device *dev)
{
struct efi_driver *efidrv = to_efi_driver(dev->driver);
struct efi_device *efidev = to_efi_device(dev);
@@ -345,15 +323,14 @@ struct bus_type efi_bus = {
.remove = efi_bus_remove,
};
-static void efi_businfo(struct device_d *dev)
+static void efi_businfo(struct device *dev)
{
- int i;
+ struct efi_config_table *t;
+ int i = 0;
printf("Tables:\n");
- for (i = 0; i < efi_sys_table->nr_tables; i++) {
- efi_config_table_t *t = &efi_sys_table->tables[i];
-
- printf(" %d: %pUl: %s\n", i, &t->guid,
+ for_each_efi_config_table(t) {
+ printf(" %d: %pUl: %s\n", i++, &t->guid,
efi_guid_string(&t->guid));
}
}
@@ -403,10 +380,7 @@ static void efi_set_bootsource(void)
enum bootsource src = BOOTSOURCE_UNKNOWN;
int instance = BOOTSOURCE_INSTANCE_UNKNOWN;
- efi_handle_t *efi_parent;
-
- if (!efi_loaded_image->parent_handle)
- goto out;
+ efi_handle_t efi_parent;
efi_parent = efi_find_parent(efi_loaded_image->device_handle);
@@ -463,14 +437,14 @@ static int efi_init_devices(void)
return 0;
}
-core_initcall(efi_init_devices);
+core_efi_initcall(efi_init_devices);
void efi_pause_devices(void)
{
- struct device_d *dev;
+ struct device *dev;
bus_for_each_device(&efi_bus, dev) {
- struct driver_d *drv = dev->driver;
+ struct driver *drv = dev->driver;
struct efi_device *efidev = to_efi_device(dev);
struct efi_driver *efidrv;
@@ -486,10 +460,10 @@ void efi_pause_devices(void)
void efi_continue_devices(void)
{
- struct device_d *dev;
+ struct device *dev;
bus_for_each_device(&efi_bus, dev) {
- struct driver_d *drv = dev->driver;
+ struct driver *drv = dev->driver;
struct efi_device *efidev = to_efi_device(dev);
struct efi_driver *efidrv;
@@ -502,172 +476,3 @@ void efi_continue_devices(void)
efidrv->dev_continue(efidev);
}
}
-
-static void efi_devpath(efi_handle_t handle)
-{
- efi_status_t efiret;
- void *devpath;
- char *dev_path_str;
-
- efiret = BS->open_protocol(handle, &efi_device_path_protocol_guid,
- &devpath, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (EFI_ERROR(efiret))
- return;
-
- dev_path_str = device_path_to_str(devpath);
- if (dev_path_str) {
- printf(" Devpath: \n %s\n", dev_path_str);
- free(dev_path_str);
- }
-}
-
-static void efi_dump(efi_handle_t *handles, unsigned long handle_count)
-{
- int i, j;
- unsigned long num_guids;
- efi_guid_t **guids;
-
- if (!handles || !handle_count)
- return;
-
- for (i = 0; i < handle_count; i++) {
- printf("handle-%p\n", handles[i]);
-
- BS->protocols_per_handle(handles[i], &guids, &num_guids);
- printf(" Protocols:\n");
- for (j = 0; j < num_guids; j++)
- printf(" %d: %pUl: %s\n", j, guids[j],
- efi_guid_string(guids[j]));
- efi_devpath(handles[i]);
- }
- printf("\n");
-}
-
-static unsigned char to_digit(unsigned char c)
-{
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (c >= 'A' && c <= 'F')
- c -= 'A' - 10;
- else
- c -= 'a' - 10;
-
- return c;
-}
-
-#define read_xbit(src, dest, bit) \
- do { \
- int __i; \
- for (__i = (bit - 4); __i >= 0; __i -= 4, src++) \
- dest |= to_digit(*src) << __i; \
- } while (0)
-
-static int do_efi_protocol_dump(int argc, char **argv)
-{
- unsigned long handle_count = 0;
- efi_handle_t *handles = NULL;
- int ret;
- efi_guid_t guid;
- u32 a = 0;
- u16 b = 0;
- u16 c = 0;
- u8 d0 = 0;
- u8 d1 = 0;
- u8 d2 = 0;
- u8 d3 = 0;
- u8 d4 = 0;
- u8 d5 = 0;
- u8 d6 = 0;
- u8 d7 = 0;
-
- /* Format 220e73b6-6bdb-4413-8405-b974b108619a */
- if (argc == 1) {
- char *s = argv[0];
- int len = strlen(s);
-
- if (len != 36)
- return -EINVAL;
-
- read_xbit(s, a, 32);
- if (*s != '-')
- return -EINVAL;
- s++;
- read_xbit(s, b, 16);
- if (*s != '-')
- return -EINVAL;
- s++;
- read_xbit(s, c, 16);
- if (*s != '-')
- return -EINVAL;
- s++;
- read_xbit(s, d0, 8);
- read_xbit(s, d1, 8);
- if (*s != '-')
- return -EINVAL;
- s++;
- read_xbit(s, d2, 8);
- read_xbit(s, d3, 8);
- read_xbit(s, d4, 8);
- read_xbit(s, d5, 8);
- read_xbit(s, d6, 8);
- read_xbit(s, d7, 8);
- } else if (argc == 11) {
- /* Format :
- * 220e73b6 6bdb 4413 84 05 b9 74 b1 08 61 9a
- * or
- * 0x220e73b6 0x6bdb 0x14413 0x84 0x05 0xb9 0x74 0xb1 0x08 0x61 0x9a
- */
- a = simple_strtoul(argv[0], NULL, 16);
- b = simple_strtoul(argv[1], NULL, 16);
- c = simple_strtoul(argv[2], NULL, 16);
- d0 = simple_strtoul(argv[3], NULL, 16);
- d1 = simple_strtoul(argv[4], NULL, 16);
- d2 = simple_strtoul(argv[5], NULL, 16);
- d3 = simple_strtoul(argv[6], NULL, 16);
- d4 = simple_strtoul(argv[7], NULL, 16);
- d5 = simple_strtoul(argv[8], NULL, 16);
- d6 = simple_strtoul(argv[9], NULL, 16);
- d7 = simple_strtoul(argv[10], NULL, 16);
- } else {
- return -EINVAL;
- }
-
- guid = EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7);
-
- printf("Searching for:\n");
- printf(" %pUl: %s\n", &guid, efi_guid_string(&guid));
-
- ret = efi_locate_handle(by_protocol, &guid, NULL, &handle_count, &handles);
- if (!ret)
- efi_dump(handles, handle_count);
-
- return 0;
-}
-
-static int do_efi_handle_dump(int argc, char *argv[])
-{
- unsigned long handle_count = 0;
- efi_handle_t *handles = NULL;
- int ret;
-
- if (argc > 1)
- return do_efi_protocol_dump(--argc, ++argv);
-
- ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles);
- if (!ret)
- efi_dump(handles, handle_count);
-
- return 0;
-}
-
-BAREBOX_CMD_HELP_START(efi_handle_dump)
-BAREBOX_CMD_HELP_TEXT("Dump all the efi handle with protocol and devpath\n")
-BAREBOX_CMD_HELP_END
-
-BAREBOX_CMD_START(efi_handle_dump)
- .cmd = do_efi_handle_dump,
- BAREBOX_CMD_DESC("Usage: efi_handle_dump")
- BAREBOX_CMD_OPTS("[a-b-c-d0d1-d3d4d5d6d7] or [a b c d0 d1 d2 d3 d4 d5 d6 d7]")
- BAREBOX_CMD_GROUP(CMD_GRP_MISC)
- BAREBOX_CMD_HELP(cmd_efi_handle_dump_help)
-BAREBOX_CMD_END
diff --git a/drivers/efi/efi-handle.c b/drivers/efi/efi-handle.c
new file mode 100644
index 0000000000..be9013cb64
--- /dev/null
+++ b/drivers/efi/efi-handle.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#include <common.h>
+#include <efi.h>
+#include <efi/efi-util.h>
+#include <efi/efi-device.h>
+#include <efi/efi-mode.h>
+
+int __efi_locate_handle(struct efi_boot_services *bs,
+ enum efi_locate_search_type search_type,
+ efi_guid_t *protocol,
+ void *search_key,
+ unsigned long *no_handles,
+ efi_handle_t **buffer)
+{
+ efi_status_t efiret;
+ unsigned long buffer_size = 0;
+ efi_handle_t *buf;
+
+ efiret = bs->locate_handle(search_type, protocol, search_key, &buffer_size,
+ NULL);
+ if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL)
+ return -efi_errno(efiret);
+
+ buf = malloc(buffer_size);
+ if (!buf)
+ return -ENOMEM;
+
+ efiret = bs->locate_handle(search_type, protocol, search_key, &buffer_size,
+ buf);
+ if (EFI_ERROR(efiret)) {
+ free(buf);
+ return -efi_errno(efiret);
+ }
+
+ *no_handles = buffer_size / sizeof(efi_handle_t);
+ *buffer = buf;
+
+ return 0;
+}
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index d3cca41a7e..3252b61bc7 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -23,13 +23,32 @@ config FIRMWARE_ZYNQMP_FPGA
help
Load a bitstream to the PL of Zynq Ultrascale+
-config ARM_SCMI_PROTOCOL
- tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
- depends on ARM || COMPILE_TEST
- depends on ARM_SMCCC
+config QEMU_FW_CFG
+ bool "QEMU FW CFG interface"
help
- ARM System Control and Management Interface (SCMI) protocol is a
- set of operating system-independent software interfaces that are
- used in system management.
+ This driver exposes the QEMU FW CFG conduit as a single
+ character device.
+
+ The selector key can be set via ioctl or device parameter
+ and read/writes are translated to the MMIO/IO port appropriate
+ for the platform.
+
+config TI_SCI_PROTOCOL
+ bool "TI System Control Interface (TISCI) Message Protocol"
+ depends on TI_MESSAGE_MANAGER
+ default ARCH_K3
+ help
+ TI System Control Interface (TISCI) Message Protocol is used to manage
+ compute systems such as ARM, DSP etc with the system controller in
+ complex System on Chip(SoC) such as those found on certain keystone
+ generation SoC from TI.
+
+ System controller provides various facilities including power
+ management function support.
+
+ This protocol library is used by client drivers to use the features
+ provided by the system controller.
+
+source "drivers/firmware/arm_scmi/Kconfig"
endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 26d6f3275a..4eabf42fd6 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -2,4 +2,6 @@
obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o
obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o socfpga_sdr.o
obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o
+obj-$(CONFIG_QEMU_FW_CFG) += qemu_fw_cfg.o
+obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-y += arm_scmi/
diff --git a/drivers/firmware/altera_serial.c b/drivers/firmware/altera_serial.c
index dcb49ad4dd..4fe4ec462f 100644
--- a/drivers/firmware/altera_serial.c
+++ b/drivers/firmware/altera_serial.c
@@ -48,7 +48,7 @@ struct fpga_spi {
int nstat_gpio; /* input GPIO to read the status line */
int confd_gpio; /* input GPIO to read the config done line */
int nconfig_gpio; /* output GPIO to start the FPGA's config */
- struct device_d *dev;
+ struct device *dev;
struct spi_device *spi;
const struct altera_ps_data *data;
bool padding_done;
@@ -83,7 +83,7 @@ static struct altera_ps_data a10_data = {
static int altera_spi_open(struct firmware_handler *fh)
{
struct fpga_spi *this = container_of(fh, struct fpga_spi, fh);
- struct device_d *dev = this->dev;
+ struct device *dev = this->dev;
int ret;
dev_dbg(dev, "Initiating programming\n");
@@ -151,7 +151,7 @@ static int altera_spi_open(struct firmware_handler *fh)
static int altera_spi_write(struct firmware_handler *fh, const void *buf, size_t sz)
{
struct fpga_spi *this = container_of(fh, struct fpga_spi, fh);
- struct device_d *dev = this->dev;
+ struct device *dev = this->dev;
struct spi_transfer t[2];
struct spi_message m;
u32 dummy;
@@ -205,7 +205,7 @@ static int altera_spi_write(struct firmware_handler *fh, const void *buf, size_t
static int altera_spi_close(struct firmware_handler *fh)
{
struct fpga_spi *this = container_of(fh, struct fpga_spi, fh);
- struct device_d *dev = this->dev;
+ struct device *dev = this->dev;
struct spi_transfer t;
struct spi_message m;
u32 dummy = 0;
@@ -265,9 +265,9 @@ static int altera_spi_close(struct firmware_handler *fh)
return -EIO;
}
-static int altera_spi_of(struct device_d *dev, struct fpga_spi *this)
+static int altera_spi_of(struct device *dev, struct fpga_spi *this)
{
- struct device_node *n = dev->device_node;
+ struct device_node *n = dev->of_node;
const char *name;
int ret;
@@ -329,12 +329,12 @@ static void altera_spi_init_mode(struct spi_device *spi, int spi_bits_per_word)
spi->mode = SPI_MODE_0 | SPI_LSB_FIRST;
}
-static int altera_spi_probe(struct device_d *dev)
+static int altera_spi_probe(struct device *dev)
{
int rc;
struct fpga_spi *this;
struct firmware_handler *fh;
- const char *alias = of_alias_get(dev->device_node);
+ const char *alias = of_alias_get(dev->of_node);
const char *model = NULL;
const struct altera_ps_data *data;
@@ -359,11 +359,11 @@ static int altera_spi_probe(struct device_d *dev)
fh->open = altera_spi_open;
fh->write = altera_spi_write;
fh->close = altera_spi_close;
- of_property_read_string(dev->device_node, "compatible", &model);
+ of_property_read_string(dev->of_node, "compatible", &model);
if (model)
fh->model = xstrdup(model);
fh->dev = dev;
- fh->device_node = dev->device_node;
+ fh->device_node = dev->of_node;
this->spi = (struct spi_device *)dev->type_data;
this->data = data;
@@ -391,8 +391,9 @@ static struct of_device_id altera_spi_id_table[] = {
{ .compatible = "altr,fpga-arria10-passive-serial", .data = &a10_data },
{ }
};
+MODULE_DEVICE_TABLE(of, altera_spi_id_table);
-static struct driver_d altera_spi_driver = {
+static struct driver altera_spi_driver = {
.name = "altera-fpga",
.of_compatible = DRV_OF_COMPAT(altera_spi_id_table),
.probe = altera_spi_probe,
diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
new file mode 100644
index 0000000000..29b0152901
--- /dev/null
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "ARM System Control and Management Interface Protocol"
+
+config ARM_SCMI_PROTOCOL
+ tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
+ depends on ARM || ARM64 || COMPILE_TEST
+ select IDR
+ help
+ ARM System Control and Management Interface (SCMI) protocol is a
+ set of operating system-independent software interfaces that are
+ used in system management. SCMI is extensible and currently provides
+ interfaces for: Discovery and self-description of the interfaces
+ it supports, Power domain management which is the ability to place
+ a given device or domain into the various power-saving states that
+ it supports, Performance management which is the ability to control
+ the performance of a domain that is composed of compute engines
+ such as application processors and other accelerators, Clock
+ management which is the ability to set and inquire rates on platform
+ managed clocks and Sensor management which is the ability to read
+ sensor data.
+
+ This protocol library provides interface for all the client drivers
+ making use of the features offered by the SCMI.
+
+if ARM_SCMI_PROTOCOL
+
+config ARM_SCMI_HAVE_TRANSPORT
+ bool
+ help
+ This declares whether at least one SCMI transport has been configured.
+ Used to trigger a build bug when trying to build SCMI without any
+ configured transport.
+
+config ARM_SCMI_HAVE_SHMEM
+ bool
+ help
+ This declares whether a shared memory based transport for SCMI is
+ available.
+
+config ARM_SCMI_HAVE_MSG
+ bool
+ help
+ This declares whether a message passing based transport for SCMI is
+ available.
+
+config ARM_SCMI_TRANSPORT_OPTEE
+ bool "SCMI transport based on OP-TEE service"
+ depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL
+ select ARM_SCMI_HAVE_TRANSPORT
+ select ARM_SCMI_HAVE_SHMEM
+ select ARM_SCMI_HAVE_MSG
+ default y
+ help
+ This enables the OP-TEE service based transport for SCMI.
+
+ If you want the ARM SCMI PROTOCOL stack to include support for a
+ transport based on OP-TEE SCMI service, answer Y.
+
+config ARM_SCMI_TRANSPORT_SMC
+ bool "SCMI transport based on SMC"
+ select ARM_SMCCC
+ select ARM_SCMI_HAVE_TRANSPORT
+ select ARM_SCMI_HAVE_SHMEM
+ default y
+ help
+ Enable SMC based transport for SCMI.
+
+ If you want the ARM SCMI PROTOCOL stack to include support for a
+ transport based on SMC, answer Y.
+
+endif #ARM_SCMI_PROTOCOL
+
+config ARM_SCMI_POWER_DOMAIN
+ tristate "SCMI power domain driver"
+ depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+ default ARM_SCMI_PROTOCOL
+ select PM_GENERIC_DOMAINS
+ help
+ This enables support for the SCMI power domains which can be
+ enabled or disabled via the SCP firmware
+
+ This driver can also be built as a module. If so, the module
+ will be called scmi_pm_domain. Note this may needed early in boot
+ before rootfs may be available.
+
+endmenu
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 4b21e6609a..0054164e58 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,10 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
scmi-bus-y = bus.o
+scmi-core-objs := $(scmi-bus-y)
+
scmi-driver-y = driver.o
-scmi-transport-y = shmem.o
-scmi-transport-$(CONFIG_ARM_SMCCC) += smc.o
-scmi-protocols-y = base.o reset.o clock.o voltage.o
+scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
+scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
+scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
+scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o
+scmi-protocols-y = base.o clock.o power.o reset.o sensors.o voltage.o
+scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
-scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
- $(scmi-transport-y)
+obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
+
+obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index d4af40c40c..439d8eb7b6 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -5,7 +5,7 @@
* Copyright (C) 2018-2021 ARM Ltd.
*/
-#define pr_fmt(fmt) "SCMI BASE - " fmt
+#define pr_fmt(fmt) "SCMI Notifications BASE - " fmt
#include <common.h>
#include <linux/scmi_protocol.h>
@@ -15,12 +15,6 @@
#define SCMI_BASE_NUM_SOURCES 1
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
-struct scmi_msg_resp_base_attributes {
- u8 num_protocols;
- u8 num_agents;
- __le16 reserved;
-};
-
enum scmi_base_protocol_cmd {
BASE_DISCOVER_VENDOR = 0x3,
BASE_DISCOVER_SUB_VENDOR = 0x4,
@@ -33,6 +27,18 @@ enum scmi_base_protocol_cmd {
BASE_RESET_AGENT_CONFIGURATION = 0xb,
};
+struct scmi_msg_resp_base_attributes {
+ u8 num_protocols;
+ u8 num_agents;
+ __le16 reserved;
+};
+
+struct scmi_msg_resp_base_discover_agent {
+ __le32 agent_id;
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+};
+
+
/**
* scmi_base_attributes_get() - gets the implementation details
* that are associated with the base protocol.
@@ -99,7 +105,7 @@ scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)
ret = ph->xops->do_xfer(ph, t);
if (!ret)
- memcpy(vendor_id, t->rx.buf, size);
+ strscpy(vendor_id, t->rx.buf, size);
ph->xops->xfer_put(ph, t);
@@ -157,7 +163,8 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
struct scmi_xfer *t;
__le32 *num_skip, *num_ret;
u32 tot_num_ret = 0, loop_num_ret;
- struct device_d *dev = ph->dev;
+ struct device *dev = ph->dev;
+ struct scmi_revision_info *rev = ph->get_priv(ph);
ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
sizeof(*num_skip), 0, &t);
@@ -169,6 +176,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
list = t->rx.buf + sizeof(*num_ret);
do {
+ size_t real_list_sz;
+ u32 calc_list_sz;
+
/* Set the number of protocols to be skipped/already read */
*num_skip = cpu_to_le32(tot_num_ret);
@@ -177,18 +187,46 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
break;
loop_num_ret = le32_to_cpu(*num_ret);
- if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
- dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
+ if (!loop_num_ret)
+ break;
+
+ if (loop_num_ret > rev->num_protocols - tot_num_ret) {
+ dev_err(dev,
+ "No. Returned protocols > Total protocols.\n");
break;
}
+ if (t->rx.len < (sizeof(u32) * 2)) {
+ dev_err(dev, "Truncated reply - rx.len:%zd\n",
+ t->rx.len);
+ ret = -EPROTO;
+ break;
+ }
+
+ real_list_sz = t->rx.len - sizeof(u32);
+ calc_list_sz = (1 + (loop_num_ret - 1) / sizeof(u32)) *
+ sizeof(u32);
+ if (calc_list_sz != real_list_sz) {
+ dev_warn(dev,
+ "Malformed reply - real_sz:%zd calc_sz:%u (loop_num_ret:%d)\n",
+ real_list_sz, calc_list_sz, loop_num_ret);
+ /*
+ * Bail out if the expected list size is bigger than the
+ * total payload size of the received reply.
+ */
+ if (calc_list_sz > real_list_sz) {
+ ret = -EPROTO;
+ break;
+ }
+ }
+
for (loop = 0; loop < loop_num_ret; loop++)
protocols_imp[tot_num_ret + loop] = *(list + loop);
tot_num_ret += loop_num_ret;
ph->xops->reset_rx_to_maxsz(ph, t);
- } while (loop_num_ret);
+ } while (tot_num_ret < rev->num_protocols);
ph->xops->xfer_put(ph, t);
@@ -211,18 +249,21 @@ static int scmi_base_discover_agent_get(const struct scmi_protocol_handle *ph,
int id, char *name)
{
int ret;
+ struct scmi_msg_resp_base_discover_agent *agent_info;
struct scmi_xfer *t;
ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_AGENT,
- sizeof(__le32), SCMI_MAX_STR_SIZE, &t);
+ sizeof(__le32), sizeof(*agent_info), &t);
if (ret)
return ret;
put_unaligned_le32(id, t->tx.buf);
ret = ph->xops->do_xfer(ph, t);
- if (!ret)
- strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
+ if (!ret) {
+ agent_info = t->rx.buf;
+ strscpy(name, agent_info->name, SCMI_SHORT_NAME_MAX_SIZE);
+ }
ph->xops->xfer_put(ph, t);
@@ -234,23 +275,27 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
int id, ret;
u8 *prot_imp;
u32 version;
- char name[SCMI_MAX_STR_SIZE];
- struct device_d *dev = ph->dev;
+ char name[SCMI_SHORT_NAME_MAX_SIZE];
+ struct device *dev = ph->dev;
struct scmi_revision_info *rev = scmi_revision_area_get(ph);
ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
- prot_imp = kcalloc(MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
- if (!prot_imp)
- return -ENOMEM;
-
rev->major_ver = PROTOCOL_REV_MAJOR(version),
rev->minor_ver = PROTOCOL_REV_MINOR(version);
ph->set_priv(ph, rev);
- scmi_base_attributes_get(ph);
+ ret = scmi_base_attributes_get(ph);
+ if (ret)
+ return ret;
+
+ prot_imp = devm_kcalloc(dev, rev->num_protocols, sizeof(u8),
+ GFP_KERNEL);
+ if (!prot_imp)
+ return -ENOMEM;
+
scmi_base_vendor_id_get(ph, false);
scmi_base_vendor_id_get(ph, true);
scmi_base_implementation_version_get(ph);
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index e8297be4d2..1d9a0f089b 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -10,12 +10,133 @@
#include <common.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
#include <linux/idr.h>
#include <driver.h>
#include "common.h"
-static DEFINE_IDR(scmi_protocols);
+BLOCKING_NOTIFIER_HEAD(scmi_requested_devices_nh);
+EXPORT_SYMBOL_GPL(scmi_requested_devices_nh);
+
+static DEFINE_IDR(scmi_requested_devices);
+/* Protect access to scmi_requested_devices */
+static DEFINE_MUTEX(scmi_requested_devices_mtx);
+
+struct scmi_requested_dev {
+ const struct scmi_device_id *id_table;
+ struct list_head node;
+};
+
+/* Track globally the creation of SCMI SystemPower related devices */
+static atomic_t scmi_syspower_registered = ATOMIC_INIT(0);
+
+/**
+ * scmi_protocol_device_request - Helper to request a device
+ *
+ * @id_table: A protocol/name pair descriptor for the device to be created.
+ *
+ * This helper let an SCMI driver request specific devices identified by the
+ * @id_table to be created for each active SCMI instance.
+ *
+ * The requested device name MUST NOT be already existent for any protocol;
+ * at first the freshly requested @id_table is annotated in the IDR table
+ * @scmi_requested_devices and then the requested device is advertised to any
+ * registered party via the @scmi_requested_devices_nh notification chain.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_protocol_device_request(const struct scmi_device_id *id_table)
+{
+ int ret = 0;
+ unsigned int id = 0;
+ struct list_head *head, *phead = NULL;
+ struct scmi_requested_dev *rdev;
+
+ pr_debug("Requesting SCMI device (%s) for protocol %x\n",
+ id_table->name, id_table->protocol_id);
+
+ if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) &&
+ !IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX)) {
+ pr_warn("SCMI Raw mode active. Rejecting '%s'/0x%02X\n",
+ id_table->name, id_table->protocol_id);
+ return -EINVAL;
+ }
+
+ /*
+ * Search for the matching protocol rdev list and then search
+ * of any existent equally named device...fails if any duplicate found.
+ */
+ mutex_lock(&scmi_requested_devices_mtx);
+ idr_for_each_entry(&scmi_requested_devices, head, id) {
+ if (!phead) {
+ /* A list found registered in the IDR is never empty */
+ rdev = list_first_entry(head, struct scmi_requested_dev,
+ node);
+ if (rdev->id_table->protocol_id ==
+ id_table->protocol_id)
+ phead = head;
+ }
+ list_for_each_entry(rdev, head, node) {
+ if (!strcmp(rdev->id_table->name, id_table->name)) {
+ pr_err("Ignoring duplicate request [%d] %s\n",
+ rdev->id_table->protocol_id,
+ rdev->id_table->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * No duplicate found for requested id_table, so let's create a new
+ * requested device entry for this new valid request.
+ */
+ rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
+ if (!rdev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ rdev->id_table = id_table;
+
+ /*
+ * Append the new requested device table descriptor to the head of the
+ * related protocol list, eventually creating such head if not already
+ * there.
+ */
+ if (!phead) {
+ phead = kzalloc(sizeof(*phead), GFP_KERNEL);
+ if (!phead) {
+ kfree(rdev);
+ ret = -ENOMEM;
+ goto out;
+ }
+ INIT_LIST_HEAD(phead);
+
+ ret = idr_alloc_one(&scmi_requested_devices, (void *)phead,
+ id_table->protocol_id);
+ if (ret != id_table->protocol_id) {
+ pr_err("Failed to save SCMI device - ret:%d\n", ret);
+ kfree(rdev);
+ kfree(phead);
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = 0;
+ }
+ list_add(&rdev->node, phead);
+
+out:
+ mutex_unlock(&scmi_requested_devices_mtx);
+
+ if (!ret)
+ blocking_notifier_call_chain(&scmi_requested_devices_nh,
+ SCMI_BUS_NOTIFY_DEVICE_REQUEST,
+ (void *)rdev->id_table);
+
+ return ret;
+}
static const struct scmi_device_id *
scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
@@ -36,7 +157,7 @@ scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
return NULL;
}
-static int scmi_dev_match(struct device_d *dev, struct driver_d *drv)
+static int scmi_dev_match(struct device *dev, struct driver *drv)
{
struct scmi_driver *scmi_drv = to_scmi_driver(drv);
struct scmi_device *scmi_dev = to_scmi_dev(dev);
@@ -49,20 +170,20 @@ static int scmi_dev_match(struct device_d *dev, struct driver_d *drv)
return -1;
}
-static int scmi_match_by_id_table(struct device_d *dev, void *data)
+static int scmi_match_by_id_table(struct device *dev, void *data)
{
struct scmi_device *sdev = to_scmi_dev(dev);
struct scmi_device_id *id_table = data;
return sdev->protocol_id == id_table->protocol_id &&
- !strcmp(sdev->name, id_table->name);
+ (id_table->name && !strcmp(sdev->name, id_table->name));
}
-struct scmi_device *scmi_child_dev_find(struct device_d *parent,
- int prot_id, const char *name)
+static struct scmi_device *scmi_child_dev_find(struct device *parent,
+ int prot_id, const char *name)
{
struct scmi_device_id id_table;
- struct device_d *dev;
+ struct device *dev;
id_table.protocol_id = prot_id;
id_table.name = name;
@@ -74,30 +195,10 @@ struct scmi_device *scmi_child_dev_find(struct device_d *parent,
return to_scmi_dev(dev);
}
-const struct scmi_protocol *scmi_protocol_get(int protocol_id)
-{
- const struct scmi_protocol *proto;
-
- proto = idr_find(&scmi_protocols, protocol_id);
- if (!proto) {
- pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
- return NULL;
- }
-
- pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);
-
- return proto;
-}
-
-static int scmi_dev_probe(struct device_d *dev)
+static int scmi_dev_probe(struct device *dev)
{
struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
struct scmi_device *scmi_dev = to_scmi_dev(dev);
- const struct scmi_device_id *id;
-
- id = scmi_dev_match_id(scmi_dev, scmi_drv);
- if (!id)
- return -ENODEV;
if (!scmi_dev->handle)
return -EPROBE_DEFER;
@@ -105,26 +206,20 @@ static int scmi_dev_probe(struct device_d *dev)
return scmi_drv->probe(scmi_dev);
}
-static void scmi_dev_remove(struct device_d *dev)
-{
- struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
- struct scmi_device *scmi_dev = to_scmi_dev(dev);
-
- if (scmi_drv->remove)
- scmi_drv->remove(scmi_dev);
-}
-
-static struct bus_type scmi_bus_type = {
+struct bus_type scmi_bus_type = {
.name = "scmi_protocol",
.match = scmi_dev_match,
.probe = scmi_dev_probe,
- .remove = scmi_dev_remove,
};
+EXPORT_SYMBOL_GPL(scmi_bus_type);
int scmi_driver_register(struct scmi_driver *driver)
{
int retval;
+ if (!driver->probe)
+ return -EINVAL;
+
retval = scmi_protocol_device_request(driver->id_table);
if (retval)
return retval;
@@ -132,20 +227,60 @@ int scmi_driver_register(struct scmi_driver *driver)
driver->driver.bus = &scmi_bus_type;
driver->driver.name = driver->name;
- retval = register_driver(&driver->driver);
+ retval = driver_register(&driver->driver);
if (!retval)
- pr_debug("registered new scmi driver %s\n", driver->name);
+ pr_debug("Registered new scmi driver %s\n", driver->name);
return retval;
}
EXPORT_SYMBOL_GPL(scmi_driver_register);
-struct scmi_device *
-scmi_device_alloc(struct device_node *np, struct device_d *parent, int protocol,
- const char *name)
+static void __scmi_device_destroy(struct scmi_device *scmi_dev)
+{
+ pr_debug("(%s) Destroying SCMI device '%s' for protocol 0x%x (%s)\n",
+ of_node_full_name(scmi_dev->dev.parent->of_node),
+ dev_name(&scmi_dev->dev), scmi_dev->protocol_id,
+ scmi_dev->name);
+
+ if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM)
+ atomic_set(&scmi_syspower_registered, 0);
+
+ kfree_const(scmi_dev->name);
+ device_unregister(&scmi_dev->dev);
+}
+
+static struct scmi_device *
+__scmi_device_create(struct device_node *np, struct device *parent,
+ int protocol, const char *name)
{
+ int retval;
struct scmi_device *scmi_dev;
+ /*
+ * If the same protocol/name device already exist under the same parent
+ * (i.e. SCMI instance) just return the existent device.
+ * This avoids any race between the SCMI driver, creating devices for
+ * each DT defined protocol at probe time, and the concurrent
+ * registration of SCMI drivers.
+ */
+ scmi_dev = scmi_child_dev_find(parent, protocol, name);
+ if (scmi_dev)
+ return scmi_dev;
+
+ /*
+ * Ignore any possible subsequent failures while creating the device
+ * since we are doomed anyway at that point; not using a mutex which
+ * spans across this whole function to keep things simple and to avoid
+ * to serialize all the __scmi_device_create calls across possibly
+ * different SCMI server instances (parent)
+ */
+ if (protocol == SCMI_PROTOCOL_SYSTEM &&
+ atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) {
+ dev_warn(parent,
+ "SCMI SystemPower protocol device must be unique !\n");
+ return NULL;
+ }
+
scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL);
if (!scmi_dev)
return NULL;
@@ -159,68 +294,110 @@ scmi_device_alloc(struct device_node *np, struct device_d *parent, int protocol,
scmi_dev->dev.id = DEVICE_ID_DYNAMIC;
scmi_dev->protocol_id = protocol;
scmi_dev->dev.parent = parent;
- scmi_dev->dev.device_node = np;
+ scmi_dev->dev.of_node = np;
scmi_dev->dev.bus = &scmi_bus_type;
dev_set_name(&scmi_dev->dev, "scmi_dev");
- return scmi_dev;
-}
+ scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
-void scmi_device_destroy(struct scmi_device *scmi_dev)
-{
- kfree_const(scmi_dev->name);
- unregister_device(&scmi_dev->dev);
-}
+ retval = device_register(&scmi_dev->dev);
+ if (retval)
+ goto put_dev;
-void scmi_set_handle(struct scmi_device *scmi_dev)
-{
- scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
-}
+ if (!np->dev)
+ np->dev = &scmi_dev->dev;
-int scmi_protocol_register(const struct scmi_protocol *proto)
-{
- int ret;
+ pr_debug("(%s) Created SCMI device '%s' for protocol 0x%x (%s)\n",
+ of_node_full_name(parent->of_node),
+ dev_name(&scmi_dev->dev), protocol, name);
- if (!proto) {
- pr_err("invalid protocol\n");
- return -EINVAL;
- }
+ return scmi_dev;
+put_dev:
+ kfree_const(scmi_dev->name);
+ put_device(&scmi_dev->dev);
+ return NULL;
+}
- if (!proto->instance_init) {
- pr_err("missing init for protocol 0x%x\n", proto->id);
- return -EINVAL;
+/**
+ * scmi_device_create - A method to create one or more SCMI devices
+ *
+ * @np: A reference to the device node to use for the new device(s)
+ * @parent: The parent device to use identifying a specific SCMI instance
+ * @protocol: The SCMI protocol to be associated with this device
+ * @name: The requested-name of the device to be created; this is optional
+ * and if no @name is provided, all the devices currently known to
+ * be requested on the SCMI bus for @protocol will be created.
+ *
+ * This method can be invoked to create a single well-defined device (like
+ * a transport device or a device requested by an SCMI driver loaded after
+ * the core SCMI stack has been probed), or to create all the devices currently
+ * known to have been requested by the loaded SCMI drivers for a specific
+ * protocol (typically during SCMI core protocol enumeration at probe time).
+ *
+ * Return: The created device (or one of them if @name was NOT provided and
+ * multiple devices were created) or NULL if no device was created;
+ * note that NULL indicates an error ONLY in case a specific @name
+ * was provided: when @name param was not provided, a number of devices
+ * could have been potentially created for a whole protocol, unless no
+ * device was found to have been requested for that specific protocol.
+ */
+struct scmi_device *scmi_device_create(struct device_node *np,
+ struct device *parent, int protocol,
+ const char *name)
+{
+ struct list_head *phead;
+ struct scmi_requested_dev *rdev;
+ struct scmi_device *scmi_dev = NULL;
+
+ if (name)
+ return __scmi_device_create(np, parent, protocol, name);
+
+ mutex_lock(&scmi_requested_devices_mtx);
+ phead = idr_find(&scmi_requested_devices, protocol);
+ /* Nothing to do. */
+ if (!phead) {
+ mutex_unlock(&scmi_requested_devices_mtx);
+ return NULL;
}
- ret = idr_alloc_one(&scmi_protocols, (void *)proto, proto->id);
- if (ret != proto->id) {
- pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
- proto->id, ret);
- return ret;
+ /* Walk the list of requested devices for protocol and create them */
+ list_for_each_entry(rdev, phead, node) {
+ struct scmi_device *sdev;
+
+ sdev = __scmi_device_create(np, parent,
+ rdev->id_table->protocol_id,
+ rdev->id_table->name);
+ /* Report errors and carry on... */
+ if (sdev)
+ scmi_dev = sdev;
+ else
+ pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n",
+ of_node_full_name(parent->of_node),
+ rdev->id_table->protocol_id,
+ rdev->id_table->name);
}
+ mutex_unlock(&scmi_requested_devices_mtx);
- pr_debug("Registered SCMI Protocol 0x%x\n", proto->id);
-
- return 0;
+ return scmi_dev;
}
-EXPORT_SYMBOL_GPL(scmi_protocol_register);
+EXPORT_SYMBOL_GPL(scmi_device_create);
-void scmi_protocol_unregister(const struct scmi_protocol *proto)
+void scmi_device_destroy(struct device *parent, int protocol, const char *name)
{
- idr_remove(&scmi_protocols, proto->id);
-
- pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
+ struct scmi_device *scmi_dev;
- return;
+ scmi_dev = scmi_child_dev_find(parent, protocol, name);
+ if (scmi_dev)
+ __scmi_device_destroy(scmi_dev);
}
-EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
+EXPORT_SYMBOL_GPL(scmi_device_destroy);
int __init scmi_bus_init(void)
{
- int retval;
-
- retval = bus_register(&scmi_bus_type);
- if (retval)
- pr_err("scmi protocol bus register failed (%d)\n", retval);
-
- return retval;
+ return bus_register(&scmi_bus_type);
}
+
+MODULE_ALIAS("scmi-core");
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI protocol bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 8f9017206c..2c902835a0 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -2,13 +2,13 @@
/*
* System Control and Management Interface (SCMI) Clock Protocol
*
- * Copyright (C) 2018-2021 ARM Ltd.
+ * Copyright (C) 2018-2022 ARM Ltd.
*/
#include <common.h>
#include <qsort.h>
-#include "common.h"
+#include "protocols.h"
enum scmi_clock_protocol_cmd {
CLOCK_ATTRIBUTES = 0x3,
@@ -16,6 +16,7 @@ enum scmi_clock_protocol_cmd {
CLOCK_RATE_SET = 0x5,
CLOCK_RATE_GET = 0x6,
CLOCK_CONFIG_SET = 0x7,
+ CLOCK_NAME_GET = 0x8,
};
struct scmi_msg_resp_clock_protocol_attributes {
@@ -27,7 +28,9 @@ struct scmi_msg_resp_clock_protocol_attributes {
struct scmi_msg_resp_clock_attributes {
__le32 attributes;
#define CLOCK_ENABLE BIT(0)
- u8 name[SCMI_MAX_STR_SIZE];
+#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+ __le32 clock_enable_latency;
};
struct scmi_clock_set_config {
@@ -48,7 +51,7 @@ struct scmi_msg_resp_clock_describe_rates {
struct {
__le32 value_low;
__le32 value_high;
- } rate[0];
+ } rate[];
#define RATE_TO_U64(X) \
({ \
typeof(X) x = (X); \
@@ -67,11 +70,15 @@ struct scmi_clock_set_rate {
__le32 value_high;
};
+struct scmi_msg_resp_set_rate_complete {
+ __le32 id;
+ __le32 rate_low;
+ __le32 rate_high;
+};
+
struct clock_info {
u32 version;
int num_clocks;
- int max_async_req;
- unsigned cur_async_req;
struct scmi_clock_info *clk;
};
@@ -91,19 +98,19 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
attr = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
- if (!ret) {
+ if (!ret)
ci->num_clocks = le16_to_cpu(attr->num_clocks);
- ci->max_async_req = attr->max_async_req;
- }
ph->xops->xfer_put(ph, t);
return ret;
}
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
- u32 clk_id, struct scmi_clock_info *clk)
+ u32 clk_id, struct scmi_clock_info *clk,
+ u32 version)
{
int ret;
+ u32 attributes;
struct scmi_xfer *t;
struct scmi_msg_resp_clock_attributes *attr;
@@ -116,12 +123,30 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
attr = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
- if (!ret)
- strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
- else
- clk->name[0] = '\0';
+ if (!ret) {
+ u32 latency = 0;
+ attributes = le32_to_cpu(attr->attributes);
+ strscpy(clk->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
+ /* clock_enable_latency field is present only since SCMI v3.1 */
+ if (PROTOCOL_REV_MAJOR(version) >= 0x2)
+ latency = le32_to_cpu(attr->clock_enable_latency);
+ clk->enable_latency = latency ? : U32_MAX;
+ }
ph->xops->xfer_put(ph, t);
+
+ /*
+ * If supported overwrite short name with the extended one;
+ * on error just carry on and use already provided short name.
+ */
+ if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) {
+ if (SUPPORTS_EXTENDED_NAMES(attributes))
+ ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id,
+ clk->name,
+ SCMI_MAX_STR_SIZE);
+
+ }
+
return ret;
}
@@ -137,80 +162,134 @@ static int rate_cmp_func(const void *_r1, const void *_r2)
return 1;
}
-static int
-scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
- struct scmi_clock_info *clk)
-{
- u64 *rate = NULL;
- int ret, cnt;
- bool rate_discrete = false;
- u32 tot_rate_cnt = 0, rates_flag;
- u16 num_returned, num_remaining;
- struct scmi_xfer *t;
- struct scmi_msg_clock_describe_rates *clk_desc;
- struct scmi_msg_resp_clock_describe_rates *rlist;
+struct scmi_clk_ipriv {
+ struct device *dev;
+ u32 clk_id;
+ struct scmi_clock_info *clk;
+};
- ret = ph->xops->xfer_get_init(ph, CLOCK_DESCRIBE_RATES,
- sizeof(*clk_desc), 0, &t);
- if (ret)
- return ret;
+static void iter_clk_describe_prepare_message(void *message,
+ const unsigned int desc_index,
+ const void *priv)
+{
+ struct scmi_msg_clock_describe_rates *msg = message;
+ const struct scmi_clk_ipriv *p = priv;
- clk_desc = t->tx.buf;
- rlist = t->rx.buf;
+ msg->id = cpu_to_le32(p->clk_id);
+ /* Set the number of rates to be skipped/already read */
+ msg->rate_index = cpu_to_le32(desc_index);
+}
- do {
- clk_desc->id = cpu_to_le32(clk_id);
- /* Set the number of rates to be skipped/already read */
- clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
+static int
+iter_clk_describe_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ u32 flags;
+ struct scmi_clk_ipriv *p = priv;
+ const struct scmi_msg_resp_clock_describe_rates *r = response;
+
+ flags = le32_to_cpu(r->num_rates_flags);
+ st->num_remaining = NUM_REMAINING(flags);
+ st->num_returned = NUM_RETURNED(flags);
+ p->clk->rate_discrete = RATE_DISCRETE(flags);
+
+ /* Warn about out of spec replies ... */
+ if (!p->clk->rate_discrete &&
+ (st->num_returned != 3 || st->num_remaining != 0)) {
+ dev_warn(p->dev,
+ "Out-of-spec CLOCK_DESCRIBE_RATES reply for %s - returned:%d remaining:%d rx_len:%zd\n",
+ p->clk->name, st->num_returned, st->num_remaining,
+ st->rx_len);
- ret = ph->xops->do_xfer(ph, t);
- if (ret)
- goto err;
+ /*
+ * A known quirk: a triplet is returned but num_returned != 3
+ * Check for a safe payload size and fix.
+ */
+ if (st->num_returned != 3 && st->num_remaining == 0 &&
+ st->rx_len == sizeof(*r) + sizeof(__le32) * 2 * 3) {
+ st->num_returned = 3;
+ st->num_remaining = 0;
+ } else {
+ dev_err(p->dev,
+ "Cannot fix out-of-spec reply !\n");
+ return -EPROTO;
+ }
+ }
- rates_flag = le32_to_cpu(rlist->num_rates_flags);
- num_remaining = NUM_REMAINING(rates_flag);
- rate_discrete = RATE_DISCRETE(rates_flag);
- num_returned = NUM_RETURNED(rates_flag);
+ return 0;
+}
- if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
- dev_err(ph->dev, "No. of rates > MAX_NUM_RATES");
+static int
+iter_clk_describe_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
+{
+ int ret = 0;
+ struct scmi_clk_ipriv *p = priv;
+ const struct scmi_msg_resp_clock_describe_rates *r = response;
+
+ if (!p->clk->rate_discrete) {
+ switch (st->desc_index + st->loop_idx) {
+ case 0:
+ p->clk->range.min_rate = RATE_TO_U64(r->rate[0]);
break;
- }
-
- if (!rate_discrete) {
- clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
- clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
- clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
- dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
- clk->range.min_rate, clk->range.max_rate,
- clk->range.step_size);
+ case 1:
+ p->clk->range.max_rate = RATE_TO_U64(r->rate[1]);
+ break;
+ case 2:
+ p->clk->range.step_size = RATE_TO_U64(r->rate[2]);
+ break;
+ default:
+ ret = -EINVAL;
break;
}
+ } else {
+ u64 *rate = &p->clk->list.rates[st->desc_index + st->loop_idx];
- rate = &clk->list.rates[tot_rate_cnt];
- for (cnt = 0; cnt < num_returned; cnt++, rate++) {
- *rate = RATE_TO_U64(rlist->rate[cnt]);
- dev_dbg(ph->dev, "Rate %llu Hz\n", *rate);
- }
+ *rate = RATE_TO_U64(r->rate[st->loop_idx]);
+ p->clk->list.num_rates++;
+ }
- tot_rate_cnt += num_returned;
+ return ret;
+}
- ph->xops->reset_rx_to_maxsz(ph, t);
- /*
- * check for both returned and remaining to avoid infinite
- * loop due to buggy firmware
- */
- } while (num_returned && num_remaining);
+static int
+scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
+ struct scmi_clock_info *clk)
+{
+ int ret;
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_clk_describe_prepare_message,
+ .update_state = iter_clk_describe_update_state,
+ .process_response = iter_clk_describe_process_response,
+ };
+ struct scmi_clk_ipriv cpriv = {
+ .clk_id = clk_id,
+ .clk = clk,
+ .dev = ph->dev,
+ };
+
+ iter = ph->hops->iter_response_init(ph, &ops, SCMI_MAX_NUM_RATES,
+ CLOCK_DESCRIBE_RATES,
+ sizeof(struct scmi_msg_clock_describe_rates),
+ &cpriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ ret = ph->hops->iter_response_run(iter);
+ if (ret)
+ return ret;
- if (rate_discrete && rate) {
- clk->list.num_rates = tot_rate_cnt;
- qsort(rate, tot_rate_cnt, sizeof(*rate), rate_cmp_func);
+ if (!clk->rate_discrete) {
+ dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
+ clk->range.min_rate, clk->range.max_rate,
+ clk->range.step_size);
+ } else if (clk->list.num_rates) {
+ qsort(clk->list.rates, clk->list.num_rates,
+ sizeof(clk->list.rates[0]), rate_cmp_func);
}
- clk->rate_discrete = rate_discrete;
-
-err:
- ph->xops->xfer_put(ph, t);
return ret;
}
@@ -240,32 +319,20 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
u32 clk_id, u64 rate)
{
int ret;
- u32 flags = 0;
struct scmi_xfer *t;
struct scmi_clock_set_rate *cfg;
- struct clock_info *ci = ph->get_priv(ph);
ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
if (ret)
return ret;
- if (ci->max_async_req &&
- ci->cur_async_req++ < ci->max_async_req)
- flags |= CLOCK_SET_ASYNC;
-
cfg = t->tx.buf;
- cfg->flags = cpu_to_le32(flags);
+ cfg->flags = cpu_to_le32(0);
cfg->id = cpu_to_le32(clk_id);
cfg->value_low = cpu_to_le32(rate & 0xffffffff);
cfg->value_high = cpu_to_le32(rate >> 32);
- if (flags & CLOCK_SET_ASYNC)
- ret = ph->xops->do_xfer_with_response(ph, t);
- else
- ret = ph->xops->do_xfer(ph, t);
-
- if (ci->max_async_req)
- ci->cur_async_req--;
+ ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
return ret;
@@ -273,7 +340,7 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
static int
scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
- u32 config)
+ u32 config, bool atomic)
{
int ret;
struct scmi_xfer *t;
@@ -296,12 +363,24 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id)
{
- return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE);
+ return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, false);
}
static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id)
{
- return scmi_clock_config_set(ph, clk_id, 0);
+ return scmi_clock_config_set(ph, clk_id, 0, false);
+}
+
+static int scmi_clock_enable_atomic(const struct scmi_protocol_handle *ph,
+ u32 clk_id)
+{
+ return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, true);
+}
+
+static int scmi_clock_disable_atomic(const struct scmi_protocol_handle *ph,
+ u32 clk_id)
+{
+ return scmi_clock_config_set(ph, clk_id, 0, true);
}
static int scmi_clock_count_get(const struct scmi_protocol_handle *ph)
@@ -314,9 +393,13 @@ static int scmi_clock_count_get(const struct scmi_protocol_handle *ph)
static const struct scmi_clock_info *
scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id)
{
+ struct scmi_clock_info *clk;
struct clock_info *ci = ph->get_priv(ph);
- struct scmi_clock_info *clk = ci->clk + clk_id;
+ if (clk_id >= ci->num_clocks)
+ return NULL;
+
+ clk = ci->clk + clk_id;
if (!clk->name[0])
return NULL;
@@ -330,6 +413,8 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
.rate_set = scmi_clock_rate_set,
.enable = scmi_clock_enable,
.disable = scmi_clock_disable,
+ .enable_atomic = scmi_clock_enable_atomic,
+ .disable_atomic = scmi_clock_disable_atomic,
};
static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
@@ -338,25 +423,30 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
int clkid, ret;
struct clock_info *cinfo;
- ph->xops->version_get(ph, &version);
+ ret = ph->xops->version_get(ph, &version);
+ if (ret)
+ return ret;
dev_dbg(ph->dev, "Clock Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
- cinfo = kzalloc(sizeof(*cinfo), GFP_KERNEL);
+ cinfo = devm_kzalloc(ph->dev, sizeof(*cinfo), GFP_KERNEL);
if (!cinfo)
return -ENOMEM;
- scmi_clock_protocol_attributes_get(ph, cinfo);
+ ret = scmi_clock_protocol_attributes_get(ph, cinfo);
+ if (ret)
+ return ret;
- cinfo->clk = kcalloc(cinfo->num_clocks, sizeof(*cinfo->clk), GFP_KERNEL);
+ cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks,
+ sizeof(*cinfo->clk), GFP_KERNEL);
if (!cinfo->clk)
return -ENOMEM;
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
struct scmi_clock_info *clk = cinfo->clk + clkid;
- ret = scmi_clock_attributes_get(ph, clkid, clk);
+ ret = scmi_clock_attributes_get(ph, clkid, clk, version);
if (!ret)
scmi_clock_describe_rates_get(ph, clkid, clk);
}
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 5004a71dc9..f0231a17fe 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -4,7 +4,7 @@
* driver common header file containing some definitions, structures
* and function prototypes used in all the different SCMI protocols.
*
- * Copyright (C) 2018-2021 ARM Ltd.
+ * Copyright (C) 2018-2022 ARM Ltd.
*/
#ifndef _SCMI_COMMON_H
#define _SCMI_COMMON_H
@@ -19,36 +19,50 @@
#include <asm/unaligned.h>
-#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
-#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
-#define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))
-#define PROTOCOL_REV_MINOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x)))
-#define MAX_PROTOCOLS_IMP 16
-#define MAX_OPPS 16
-
-enum scmi_common_cmd {
- PROTOCOL_VERSION = 0x0,
- PROTOCOL_ATTRIBUTES = 0x1,
- PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+#include "protocols.h"
+
+#define SCMI_MAX_CHANNELS 256
+
+#define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC)
+
+enum scmi_error_codes {
+ SCMI_SUCCESS = 0, /* Success */
+ SCMI_ERR_SUPPORT = -1, /* Not supported */
+ SCMI_ERR_PARAMS = -2, /* Invalid Parameters */
+ SCMI_ERR_ACCESS = -3, /* Invalid access/permission denied */
+ SCMI_ERR_ENTRY = -4, /* Not found */
+ SCMI_ERR_RANGE = -5, /* Value out of range */
+ SCMI_ERR_BUSY = -6, /* Device busy */
+ SCMI_ERR_COMMS = -7, /* Communication Error */
+ SCMI_ERR_GENERIC = -8, /* Generic Error */
+ SCMI_ERR_HARDWARE = -9, /* Hardware Error */
+ SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
};
-/**
- * struct scmi_msg_resp_prot_version - Response for a message
- *
- * @minor_version: Minor version of the ABI that firmware supports
- * @major_version: Major version of the ABI that firmware supports
- *
- * In general, ABI version changes follow the rule that minor version increments
- * are backward compatible. Major revision changes in ABI may not be
- * backward compatible.
- *
- * Response to a generic message with message type SCMI_MSG_VERSION
- */
-struct scmi_msg_resp_prot_version {
- __le16 minor_version;
- __le16 major_version;
+static const int scmi_linux_errmap[] = {
+ /* better than switch case as long as return value is continuous */
+ 0, /* SCMI_SUCCESS */
+ -EOPNOTSUPP, /* SCMI_ERR_SUPPORT */
+ -EINVAL, /* SCMI_ERR_PARAM */
+ -EACCES, /* SCMI_ERR_ACCESS */
+ -ENOENT, /* SCMI_ERR_ENTRY */
+ -ERANGE, /* SCMI_ERR_RANGE */
+ -EBUSY, /* SCMI_ERR_BUSY */
+ -ECOMM, /* SCMI_ERR_COMMS */
+ -EIO, /* SCMI_ERR_GENERIC */
+ -EREMOTEIO, /* SCMI_ERR_HARDWARE */
+ -EPROTO, /* SCMI_ERR_PROTOCOL */
};
+static inline int scmi_to_linux_errno(int errno)
+{
+ int err_idx = -errno;
+
+ if (err_idx >= SCMI_SUCCESS && err_idx < ARRAY_SIZE(scmi_linux_errmap))
+ return scmi_linux_errmap[err_idx];
+ return -EIO;
+}
+
#define MSG_ID_MASK GENMASK(7, 0)
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
#define MSG_TYPE_MASK GENMASK(9, 8)
@@ -63,36 +77,17 @@ struct scmi_msg_resp_prot_version {
#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1)
/**
- * struct scmi_msg_hdr - Message(Tx/Rx) header
- *
- * @id: The identifier of the message being sent
- * @protocol_id: The identifier of the protocol used to send @id message
- * @seq: The token to identify the message. When a message returns, the
- * platform returns the whole message header unmodified including the
- * token
- * @status: Status of the transfer once it's complete
- * @poll_completion: Indicate if the transfer needs to be polled for
- * completion or interrupt mode is used
- */
-struct scmi_msg_hdr {
- u8 id;
- u8 protocol_id;
- u16 seq;
- u32 status;
- bool poll_completion;
-};
-
-/**
* pack_scmi_header() - packs and returns 32-bit header
*
* @hdr: pointer to header containing all the information on message id,
- * protocol id and sequence id.
+ * protocol id, sequence id and type.
*
* Return: 32-bit packed message header to be sent to the platform.
*/
static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
{
return FIELD_PREP(MSG_ID_MASK, hdr->id) |
+ FIELD_PREP(MSG_TYPE_MASK, hdr->type) |
FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) |
FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id);
}
@@ -107,144 +102,28 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
{
hdr->id = MSG_XTRACT_ID(msg_hdr);
hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr);
+ hdr->type = MSG_XTRACT_TYPE(msg_hdr);
}
-/**
- * struct scmi_msg - Message(Tx/Rx) structure
- *
- * @buf: Buffer pointer
- * @len: Length of data in the Buffer
- */
-struct scmi_msg {
- void *buf;
- size_t len;
-};
-
-/**
- * struct scmi_xfer - Structure representing a message flow
- *
- * @transfer_id: Unique ID for debug & profiling purpose
- * @hdr: Transmit message header
- * @tx: Transmit message
- * @rx: Receive message, the buffer should be pre-allocated to store
- * message. If request-ACK protocol is used, we can reuse the same
- * buffer for the rx path as we use for the tx path.
- * @done: command message transmit completion event
- * @async_done: pointer to delayed response message received event completion
- */
-struct scmi_xfer {
- int transfer_id;
- struct scmi_msg_hdr hdr;
- struct scmi_msg tx;
- struct scmi_msg rx;
- bool done;
- bool *async_done;
-};
-
-struct scmi_xfer_ops;
-
-/**
- * struct scmi_protocol_handle - Reference to an initialized protocol instance
- *
- * @dev: A reference to the associated SCMI instance device (handle->dev).
- * @xops: A reference to a struct holding refs to the core xfer operations that
- * can be used by the protocol implementation to generate SCMI messages.
- * @set_priv: A method to set protocol private data for this instance.
- * @get_priv: A method to get protocol private data previously set.
- *
- * This structure represents a protocol initialized against specific SCMI
- * instance and it will be used as follows:
- * - as a parameter fed from the core to the protocol initialization code so
- * that it can access the core xfer operations to build and generate SCMI
- * messages exclusively for the specific underlying protocol instance.
- * - as an opaque handle fed by an SCMI driver user when it tries to access
- * this protocol through its own protocol operations.
- * In this case this handle will be returned as an opaque object together
- * with the related protocol operations when the SCMI driver tries to access
- * the protocol.
- */
-struct scmi_protocol_handle {
- struct device_d *dev;
- const struct scmi_xfer_ops *xops;
- int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
- void *(*get_priv)(const struct scmi_protocol_handle *ph);
-};
-
-/**
- * struct scmi_xfer_ops - References to the core SCMI xfer operations.
- * @version_get: Get this version protocol.
- * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
- * @reset_rx_to_maxsz: Reset rx size to max transport size.
- * @do_xfer: Do the SCMI transfer.
- * @do_xfer_with_response: Do the SCMI transfer waiting for a response.
- * @xfer_put: Free the xfer slot.
- *
- * Note that all this operations expect a protocol handle as first parameter;
- * they then internally use it to infer the underlying protocol number: this
- * way is not possible for a protocol implementation to forge messages for
- * another protocol.
- */
-struct scmi_xfer_ops {
- int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
- int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
- size_t tx_size, size_t rx_size,
- struct scmi_xfer **p);
- void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
- struct scmi_xfer *xfer);
- int (*do_xfer)(const struct scmi_protocol_handle *ph,
- struct scmi_xfer *xfer);
- int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
- struct scmi_xfer *xfer);
- void (*xfer_put)(const struct scmi_protocol_handle *ph,
- struct scmi_xfer *xfer);
-};
-
struct scmi_revision_info *
scmi_revision_area_get(const struct scmi_protocol_handle *ph);
-int scmi_handle_put(const struct scmi_handle *handle);
-struct scmi_handle *scmi_handle_get(struct device_d *dev);
-void scmi_set_handle(struct scmi_device *scmi_dev);
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
u8 *prot_imp);
-typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
-
-/**
- * struct scmi_protocol - Protocol descriptor
- * @id: Protocol ID.
- * @instance_init: Mandatory protocol initialization function.
- * @instance_deinit: Optional protocol de-initialization function.
- * @ops: Optional reference to the operations provided by the protocol and
- * exposed in scmi_protocol.h.
- * @events: An optional reference to the events supported by this protocol.
- */
-struct scmi_protocol {
- const u8 id;
- const scmi_prot_init_ph_fn_t instance_init;
- const scmi_prot_init_ph_fn_t instance_deinit;
- const void *ops;
- const struct scmi_protocol_events *events;
-};
+extern struct bus_type scmi_bus_type;
int __init scmi_bus_init(void);
-void __exit scmi_bus_exit(void);
-
-#define DECLARE_SCMI_REGISTER(func) \
- int __init scmi_##func##_register(void);
-DECLARE_SCMI_REGISTER(base);
-DECLARE_SCMI_REGISTER(reset);
-DECLARE_SCMI_REGISTER(clock);
-DECLARE_SCMI_REGISTER(voltage);
-
-#define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \
-static const struct scmi_protocol *__this_proto = &(proto); \
- \
-int __init scmi_##name##_register(void) \
-{ \
- return scmi_protocol_register(__this_proto); \
-}
-const struct scmi_protocol *scmi_protocol_get(int protocol_id);
+struct scmi_handle *scmi_handle_get(struct device *dev);
+
+#define SCMI_BUS_NOTIFY_DEVICE_REQUEST 0
+#define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST 1
+extern struct blocking_notifier_head scmi_requested_devices_nh;
+
+struct scmi_device *scmi_device_create(struct device_node *np,
+ struct device *parent, int protocol,
+ const char *name);
+void scmi_device_destroy(struct device *parent, int protocol, const char *name);
int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id);
void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
@@ -253,13 +132,18 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
/**
* struct scmi_chan_info - Structure representing a SCMI channel information
*
+ * @id: An identifier for this channel: this matches the protocol number
+ * used to initialize this channel
* @dev: Reference to device in the SCMI hierarchy corresponding to this
* channel
+ * @rx_timeout_ms: The configured RX timeout in milliseconds.
* @handle: Pointer to SCMI entity handle
* @transport_info: Transport layer related information
*/
struct scmi_chan_info {
- struct device_d *dev;
+ int id;
+ struct device *dev;
+ unsigned int rx_timeout_ms;
struct scmi_handle *handle;
void *transport_info;
};
@@ -267,67 +151,118 @@ struct scmi_chan_info {
/**
* struct scmi_transport_ops - Structure representing a SCMI transport ops
*
+ * @link_supplier: Optional callback to add link to a supplier device
* @chan_available: Callback to check if channel is available or not
* @chan_setup: Callback to allocate and setup a channel
* @chan_free: Callback to free a channel
+ * @get_max_msg: Optional callback to provide max_msg dynamically
+ * Returns the maximum number of messages for the channel type
+ * (tx or rx) that can be pending simultaneously in the system
* @send_message: Callback to send a message
* @mark_txdone: Callback to mark tx as done
* @fetch_response: Callback to fetch response
* @clear_channel: Callback to clear a channel
- * @poll_done: Callback to poll transfer status
*/
struct scmi_transport_ops {
- bool (*chan_available)(struct device_d *dev, int idx);
- int (*chan_setup)(struct scmi_chan_info *cinfo, struct device_d *dev,
+ int (*link_supplier)(struct device *dev);
+ bool (*chan_available)(struct device_node *of_node, int idx);
+ int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev,
bool tx);
int (*chan_free)(int id, void *p, void *data);
+ unsigned int (*get_max_msg)(struct scmi_chan_info *base_cinfo);
int (*send_message)(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer);
- void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
+ void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret,
+ struct scmi_xfer *xfer);
void (*fetch_response)(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer);
void (*clear_channel)(struct scmi_chan_info *cinfo);
- bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer);
};
-int scmi_protocol_device_request(const struct scmi_device_id *id_table);
-void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table);
-struct scmi_device *scmi_child_dev_find(struct device_d *parent,
- int prot_id, const char *name);
-
/**
* struct scmi_desc - Description of SoC integration
*
* @ops: Pointer to the transport specific ops structure
* @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
- * @max_msg: Maximum number of messages that can be pending
- * simultaneously in the system
+ * @max_msg: Maximum number of messages for a channel type (tx or rx) that can
+ * be pending simultaneously in the system. May be overridden by the
+ * get_max_msg op.
* @max_msg_size: Maximum size of data per message that can be handled.
+ * @sync_cmds_completed_on_ret: Flag to indicate that the transport assures
+ * synchronous-command messages are atomically
+ * completed on .send_message: no need to poll
+ * actively waiting for a response.
+ * Used by core internally only when polling is
+ * selected as a waiting for reply method: i.e.
+ * if a completion irq was found use that anyway.
*/
struct scmi_desc {
const struct scmi_transport_ops *ops;
int max_rx_timeout_ms;
int max_msg;
int max_msg_size;
+ const bool sync_cmds_completed_on_ret;
};
-#ifdef CONFIG_ARM_SMCCC
+static inline bool is_polling_required(struct scmi_chan_info *cinfo,
+ const struct scmi_desc *desc)
+{
+ return true;
+}
+
+static inline bool is_transport_polling_capable(const struct scmi_desc *desc)
+{
+ return desc->sync_cmds_completed_on_ret;
+}
+
+static inline bool is_polling_enabled(struct scmi_chan_info *cinfo,
+ const struct scmi_desc *desc)
+{
+ return is_polling_required(cinfo, desc) &&
+ is_transport_polling_capable(desc);
+}
+
+void scmi_xfer_raw_put(const struct scmi_handle *handle,
+ struct scmi_xfer *xfer);
+struct scmi_xfer *scmi_xfer_raw_get(const struct scmi_handle *handle);
+struct scmi_chan_info *
+scmi_xfer_raw_channel_get(const struct scmi_handle *handle, u8 protocol_id);
+
+int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
+ struct scmi_xfer *xfer);
+
+int scmi_xfer_raw_wait_for_message_response(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer,
+ unsigned int timeout_ms);
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
extern const struct scmi_desc scmi_smc_desc;
#endif
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE
+extern const struct scmi_desc scmi_optee_desc;
+#endif
-void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
-void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
+void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
/* shmem related declarations */
struct scmi_shared_mem;
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
- struct scmi_xfer *xfer);
+ struct scmi_xfer *xfer, struct scmi_chan_info *cinfo);
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer);
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
-bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
- struct scmi_xfer *xfer);
+/* declarations for message passing transports */
+struct scmi_msg_payld;
+
+/* Maximum overhead of message w.r.t. struct scmi_desc.max_msg_size */
+#define SCMI_MSG_MAX_PROT_OVERHEAD (2 * sizeof(__le32))
+
+size_t msg_response_size(struct scmi_xfer *xfer);
+size_t msg_command_size(struct scmi_xfer *xfer);
+void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer);
+u32 msg_read_header(struct scmi_msg_payld *msg);
+void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
+ struct scmi_xfer *xfer);
#endif /* _SCMI_COMMON_H */
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index fc02a53a2b..e602f7a440 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -14,62 +14,53 @@
* Copyright (C) 2018-2021 ARM Ltd.
*/
-#define pr_fmt(fmt) "SCMI DRIVER - " fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <common.h>
#include <linux/bitmap.h>
#include <driver.h>
#include <linux/export.h>
-#include <io.h>
+#include <linux/notifier.h>
+#include <linux/io.h>
+#include <io-64-nonatomic-hi-lo.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <of_address.h>
#include <of_device.h>
#include <linux/slab.h>
#include <linux/idr.h>
+#include <linux/mutex.h>
#include <linux/processor.h>
#include "common.h"
-enum scmi_error_codes {
- SCMI_SUCCESS = 0, /* Success */
- SCMI_ERR_SUPPORT = -1, /* Not supported */
- SCMI_ERR_PARAMS = -2, /* Invalid Parameters */
- SCMI_ERR_ACCESS = -3, /* Invalid access/permission denied */
- SCMI_ERR_ENTRY = -4, /* Not found */
- SCMI_ERR_RANGE = -5, /* Value out of range */
- SCMI_ERR_BUSY = -6, /* Device busy */
- SCMI_ERR_COMMS = -7, /* Communication Error */
- SCMI_ERR_GENERIC = -8, /* Generic Error */
- SCMI_ERR_HARDWARE = -9, /* Hardware Error */
- SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
- SCMI_ERR_MAX
-};
+static DEFINE_IDR(scmi_protocols);
+static DEFINE_SPINLOCK(protocol_lock);
/* List of all SCMI devices active in system */
static LIST_HEAD(scmi_list);
/* Protection for the entire list */
+static DEFINE_MUTEX(scmi_list_mutex);
/* Track the unique id for the transfers for debug & profiling purpose */
-static unsigned transfer_last_id;
-
-static DEFINE_IDR(scmi_requested_devices);
-
-struct scmi_requested_dev {
- const struct scmi_device_id *id_table;
- struct list_head node;
-};
+static atomic_t transfer_last_id;
/**
* struct scmi_xfers_info - Structure to manage transfer information
*
- * @xfer_block: Preallocated Message array
* @xfer_alloc_table: Bitmap table for allocated messages.
* Index of this bitmap table is also used for message
* sequence identifier.
+ * @xfer_lock: Protection for message allocation
+ * @max_msg: Maximum number of messages that can be pending
+ * @free_xfers: A free list for available to use xfers. It is initialized with
+ * a number of xfers equal to the maximum allowed in-flight
+ * messages.
*/
struct scmi_xfers_info {
- struct scmi_xfer *xfer_block;
unsigned long *xfer_alloc_table;
+ spinlock_t xfer_lock;
+ int max_msg;
+ struct hlist_head free_xfers;
};
/**
@@ -87,7 +78,7 @@ struct scmi_xfers_info {
struct scmi_protocol_instance {
const struct scmi_handle *handle;
const struct scmi_protocol *proto;
- int users;
+ refcount_t users;
void *priv;
struct scmi_protocol_handle ph;
};
@@ -97,6 +88,7 @@ struct scmi_protocol_instance {
/**
* struct scmi_info - Structure representing a SCMI instance
*
+ * @id: A sequence number starting from zero identifying this instance
* @dev: Device pointer
* @desc: SoC description for this instance
* @version: SCMI revision information containing protocol version,
@@ -109,15 +101,26 @@ struct scmi_protocol_instance {
* @protocols: IDR for protocols' instance descriptors initialized for
* this SCMI instance: populated on protocol's first attempted
* usage.
+ * @protocols_mtx: A mutex to protect protocols instances initialization.
* @protocols_imp: List of protocols implemented, currently maximum of
- * MAX_PROTOCOLS_IMP elements allocated by the base protocol
+ * scmi_revision_info.num_protocols elements allocated by the
+ * base protocol
* @active_protocols: IDR storing device_nodes for protocols actually defined
* in the DT and confirmed as implemented by fw.
+ * @atomic_threshold: Optional system wide DT-configured threshold, expressed
+ * in microseconds, for atomic operations.
+ * Only SCMI synchronous commands reported by the platform
+ * to have an execution latency lesser-equal to the threshold
+ * should be considered for atomic mode operation: such
+ * decision is finally left up to the SCMI drivers.
* @node: List head
* @users: Number of users of this instance
+ * @dev_req_nb: A notifier to listen for device request/unrequest on the scmi
+ * bus
+ * @devreq_mtx: A mutex to serialize device creation for this SCMI instance
*/
struct scmi_info {
- struct device_d *dev;
+ struct device *dev;
const struct scmi_desc *desc;
struct scmi_revision_info version;
struct scmi_handle handle;
@@ -127,176 +130,340 @@ struct scmi_info {
struct idr rx_idr;
struct idr protocols;
/* Ensure mutual exclusive access to protocols instance array */
+ struct mutex protocols_mtx;
u8 *protocols_imp;
struct idr active_protocols;
+ unsigned int atomic_threshold;
struct list_head node;
- int users;
+ struct notifier_block dev_req_nb;
+ /* Serialize device creation process for this instance */
+ struct mutex devreq_mtx;
};
#define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
+#define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb)
-static const int scmi_linux_errmap[] = {
- /* better than switch case as long as return value is continuous */
- 0, /* SCMI_SUCCESS */
- -EOPNOTSUPP, /* SCMI_ERR_SUPPORT */
- -EINVAL, /* SCMI_ERR_PARAM */
- -EACCES, /* SCMI_ERR_ACCESS */
- -ENOENT, /* SCMI_ERR_ENTRY */
- -ERANGE, /* SCMI_ERR_RANGE */
- -EBUSY, /* SCMI_ERR_BUSY */
- -ECOMM, /* SCMI_ERR_COMMS */
- -EIO, /* SCMI_ERR_GENERIC */
- -EREMOTEIO, /* SCMI_ERR_HARDWARE */
- -EPROTO, /* SCMI_ERR_PROTOCOL */
-};
+static const struct scmi_protocol *scmi_protocol_get(int protocol_id)
+{
+ const struct scmi_protocol *proto;
-static inline int scmi_to_linux_errno(int errno)
+ proto = idr_find(&scmi_protocols, protocol_id);
+ if (!proto) {
+ pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
+ return NULL;
+ }
+
+ pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);
+
+ return proto;
+}
+
+int scmi_protocol_register(const struct scmi_protocol *proto)
{
- if (errno < SCMI_SUCCESS && errno > SCMI_ERR_MAX)
- return scmi_linux_errmap[-errno];
- return -EIO;
+ int ret;
+
+ if (!proto) {
+ pr_err("invalid protocol\n");
+ return -EINVAL;
+ }
+
+ if (!proto->instance_init) {
+ pr_err("missing init for protocol 0x%x\n", proto->id);
+ return -EINVAL;
+ }
+
+ spin_lock(&protocol_lock);
+ ret = idr_alloc_one(&scmi_protocols, (void *)proto, proto->id);
+ spin_unlock(&protocol_lock);
+ if (ret != proto->id) {
+ pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
+ proto->id, ret);
+ return ret;
+ }
+
+ pr_debug("Registered SCMI Protocol 0x%x\n", proto->id);
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(scmi_protocol_register);
/**
- * scmi_dump_header_dbg() - Helper to dump a message header.
+ * scmi_create_protocol_devices - Create devices for all pending requests for
+ * this SCMI instance.
*
- * @dev: Device pointer corresponding to the SCMI entity
- * @hdr: pointer to header.
+ * @np: The device node describing the protocol
+ * @info: The SCMI instance descriptor
+ * @prot_id: The protocol ID
+ * @name: The optional name of the device to be created: if not provided this
+ * call will lead to the creation of all the devices currently requested
+ * for the specified protocol.
*/
-static inline void scmi_dump_header_dbg(struct device_d *dev,
- struct scmi_msg_hdr *hdr)
+static void scmi_create_protocol_devices(struct device_node *np,
+ struct scmi_info *info,
+ int prot_id, const char *name)
+{
+ struct scmi_device *sdev;
+
+ mutex_lock(&info->devreq_mtx);
+ sdev = scmi_device_create(np, info->dev, prot_id, name);
+ if (name && !sdev)
+ dev_err(info->dev,
+ "failed to create device for protocol 0x%X (%s)\n",
+ prot_id, name);
+ mutex_unlock(&info->devreq_mtx);
+}
+
+static void scmi_destroy_protocol_devices(struct scmi_info *info,
+ int prot_id, const char *name)
{
- dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n",
- hdr->id, hdr->seq, hdr->protocol_id);
+ mutex_lock(&info->devreq_mtx);
+ scmi_device_destroy(info->dev, prot_id, name);
+ mutex_unlock(&info->devreq_mtx);
}
/**
- * scmi_xfer_get() - Allocate one message
+ * scmi_xfer_token_set - Reserve and set new token for the xfer at hand
*
- * @handle: Pointer to SCMI entity handle
* @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @xfer: The xfer to act upon
*
- * Helper function which is used by various message functions that are
- * exposed to clients of this driver for allocating a message traffic event.
+ * Pick the next unused monotonically increasing token and set it into
+ * xfer->hdr.seq: picking a monotonically increasing value avoids immediate
+ * reuse of freshly completed or timed-out xfers, thus mitigating the risk
+ * of incorrect association of a late and expired xfer with a live in-flight
+ * transaction, both happening to re-use the same token identifier.
+ *
+ * Since platform is NOT required to answer our request in-order we should
+ * account for a few rare but possible scenarios:
+ *
+ * - exactly 'next_token' may be NOT available so pick xfer_id >= next_token
+ * using find_next_zero_bit() starting from candidate next_token bit
+ *
+ * - all tokens ahead upto (MSG_TOKEN_ID_MASK - 1) are used in-flight but we
+ * are plenty of free tokens at start, so try a second pass using
+ * find_next_zero_bit() and starting from 0.
+ *
+ * X = used in-flight
+ *
+ * Normal
+ * ------
+ *
+ * |- xfer_id picked
+ * -----------+----------------------------------------------------------
+ * | | |X|X|X| | | | | | ... ... ... ... ... ... ... ... ... ... ...|X|X|
+ * ----------------------------------------------------------------------
+ * ^
+ * |- next_token
+ *
+ * Out-of-order pending at start
+ * -----------------------------
+ *
+ * |- xfer_id picked, last_token fixed
+ * -----+----------------------------------------------------------------
+ * |X|X| | | | |X|X| ... ... ... ... ... ... ... ... ... ... ... ...|X| |
+ * ----------------------------------------------------------------------
+ * ^
+ * |- next_token
+ *
+ *
+ * Out-of-order pending at end
+ * ---------------------------
*
- * This function can sleep depending on pending requests already in the system
- * for the SCMI entity.
+ * |- xfer_id picked, last_token fixed
+ * -----+----------------------------------------------------------------
+ * |X|X| | | | |X|X| ... ... ... ... ... ... ... ... ... ... |X|X|X||X|X|
+ * ----------------------------------------------------------------------
+ * ^
+ * |- next_token
*
- * Return: 0 if all went fine, else corresponding error.
+ * Context: Assumes to be called with @xfer_lock already acquired.
+ *
+ * Return: 0 on Success or error
*/
-static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
- struct scmi_xfers_info *minfo)
+static int scmi_xfer_token_set(struct scmi_xfers_info *minfo,
+ struct scmi_xfer *xfer)
{
- u16 xfer_id;
- struct scmi_xfer *xfer;
- unsigned long bit_pos;
- struct scmi_info *info = handle_to_scmi_info(handle);
+ unsigned long xfer_id, next_token;
- bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
- info->desc->max_msg);
- if (bit_pos == info->desc->max_msg)
- return ERR_PTR(-ENOMEM);
- set_bit(bit_pos, minfo->xfer_alloc_table);
+ /*
+ * Pick a candidate monotonic token in range [0, MSG_TOKEN_MAX - 1]
+ * using the pre-allocated transfer_id as a base.
+ * Note that the global transfer_id is shared across all message types
+ * so there could be holes in the allocated set of monotonic sequence
+ * numbers, but that is going to limit the effectiveness of the
+ * mitigation only in very rare limit conditions.
+ */
+ next_token = (xfer->transfer_id & (MSG_TOKEN_MAX - 1));
- xfer_id = bit_pos;
+ /* Pick the next available xfer_id >= next_token */
+ xfer_id = find_next_zero_bit(minfo->xfer_alloc_table,
+ MSG_TOKEN_MAX, next_token);
+ if (xfer_id == MSG_TOKEN_MAX) {
+ /*
+ * After heavily out-of-order responses, there are no free
+ * tokens ahead, but only at start of xfer_alloc_table so
+ * try again from the beginning.
+ */
+ xfer_id = find_next_zero_bit(minfo->xfer_alloc_table,
+ MSG_TOKEN_MAX, 0);
+ /*
+ * Something is wrong if we got here since there can be a
+ * maximum number of (MSG_TOKEN_MAX - 1) in-flight messages
+ * but we have not found any free token [0, MSG_TOKEN_MAX - 1].
+ */
+ if (WARN_ON_ONCE(xfer_id == MSG_TOKEN_MAX))
+ return -ENOMEM;
+ }
- xfer = &minfo->xfer_block[xfer_id];
- xfer->hdr.seq = xfer_id;
- xfer->done = false;
- xfer->transfer_id = ++transfer_last_id;
+ /* Update +/- last_token accordingly if we skipped some hole */
+ if (xfer_id != next_token)
+ atomic_add((int)(xfer_id - next_token), &transfer_last_id);
- return xfer;
+ xfer->hdr.seq = (u16)xfer_id;
+
+ return 0;
}
/**
- * __scmi_xfer_put() - Release a message
+ * scmi_xfer_token_clear - Release the token
*
* @minfo: Pointer to Tx/Rx Message management info based on channel type
- * @xfer: message that was reserved by scmi_xfer_get
+ * @xfer: The xfer to act upon
*/
-static void
-__scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
+static inline void scmi_xfer_token_clear(struct scmi_xfers_info *minfo,
+ struct scmi_xfer *xfer)
{
clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
}
-static void scmi_handle_response(struct scmi_chan_info *cinfo,
- u16 xfer_id, u8 msg_type)
+/**
+ * scmi_xfer_inflight_register_unlocked - Register the xfer as in-flight
+ *
+ * @xfer: The xfer to register
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Note that this helper assumes that the xfer to be registered as in-flight
+ * had been built using an xfer sequence number which still corresponds to a
+ * free slot in the xfer_alloc_table.
+ *
+ * Context: Assumes to be called with @xfer_lock already acquired.
+ */
+static inline void
+scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
+ struct scmi_xfers_info *minfo)
+{
+ /* Set in-flight */
+ set_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+ xfer->pending = true;
+}
+
+/**
+ * scmi_xfer_pending_set - Pick a proper sequence number and mark the xfer
+ * as pending in-flight
+ *
+ * @xfer: The xfer to act upon
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Return: 0 on Success or error otherwise
+ */
+static inline int scmi_xfer_pending_set(struct scmi_xfer *xfer,
+ struct scmi_xfers_info *minfo)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&minfo->xfer_lock, flags);
+ /* Set a new monotonic token as the xfer sequence number */
+ ret = scmi_xfer_token_set(minfo, xfer);
+ if (!ret)
+ scmi_xfer_inflight_register_unlocked(xfer, minfo);
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+ return ret;
+}
+
+/**
+ * scmi_xfer_get() - Allocate one message
+ *
+ * @handle: Pointer to SCMI entity handle
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Helper function which is used by various message functions that are
+ * exposed to clients of this driver for allocating a message traffic event.
+ *
+ * Picks an xfer from the free list @free_xfers (if any available) and perform
+ * a basic initialization.
+ *
+ * Note that, at this point, still no sequence number is assigned to the
+ * allocated xfer, nor it is registered as a pending transaction.
+ *
+ * The successfully initialized xfer is refcounted.
+ *
+ * Context: Holds @xfer_lock while manipulating @free_xfers.
+ *
+ * Return: An initialized xfer if all went fine, else pointer error.
+ */
+static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
+ struct scmi_xfers_info *minfo)
{
+ unsigned long flags;
struct scmi_xfer *xfer;
- struct device_d *dev = cinfo->dev;
- struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
- struct scmi_xfers_info *minfo = &info->tx_minfo;
- /* Are we even expecting this? */
- if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
- dev_err(dev, "message for %d is not expected!\n", xfer_id);
- info->desc->ops->clear_channel(cinfo);
- return;
+ spin_lock_irqsave(&minfo->xfer_lock, flags);
+ if (hlist_empty(&minfo->free_xfers)) {
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+ return ERR_PTR(-ENOMEM);
}
- xfer = &minfo->xfer_block[xfer_id];
+ /* grab an xfer from the free_list */
+ xfer = hlist_entry(minfo->free_xfers.first, struct scmi_xfer, node);
+ hlist_del_init(&xfer->node);
+
/*
- * Even if a response was indeed expected on this slot at this point,
- * a buggy platform could wrongly reply feeding us an unexpected
- * delayed response we're not prepared to handle: bail-out safely
- * blaming firmware.
+ * Allocate transfer_id early so that can be used also as base for
+ * monotonic sequence number generation if needed.
*/
- if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) {
- dev_err(dev,
- "Delayed Response for %d not expected! Buggy F/W ?\n",
- xfer_id);
- info->desc->ops->clear_channel(cinfo);
- /* It was unexpected, so nobody will clear the xfer if not us */
- __scmi_xfer_put(minfo, xfer);
- return;
- }
+ xfer->transfer_id = atomic_inc_return(&transfer_last_id);
- scmi_dump_header_dbg(dev, &xfer->hdr);
+ refcount_set(&xfer->users, 1);
+ atomic_set(&xfer->busy, SCMI_XFER_FREE);
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
- info->desc->ops->fetch_response(cinfo, xfer);
-
- if (msg_type == MSG_TYPE_DELAYED_RESP) {
- info->desc->ops->clear_channel(cinfo);
- *xfer->async_done = true;
- } else {
- xfer->done = true;
- }
+ return xfer;
}
/**
- * scmi_rx_callback() - callback for receiving messages
+ * __scmi_xfer_put() - Release a message
*
- * @cinfo: SCMI channel info
- * @msg_hdr: Message header
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @xfer: message that was reserved by scmi_xfer_get
*
- * Processes one received message to appropriate transfer information and
- * signals completion of the transfer.
+ * After refcount check, possibly release an xfer, clearing the token slot,
+ * removing xfer from @pending_xfers and putting it back into free_xfers.
*
- * NOTE: This function will be invoked in IRQ context, hence should be
- * as optimal as possible.
+ * This holds a spinlock to maintain integrity of internal data structures.
*/
-void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
+static void
+__scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
{
- u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
- u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
+ unsigned long flags;
- switch (msg_type) {
- case MSG_TYPE_COMMAND:
- case MSG_TYPE_DELAYED_RESP:
- scmi_handle_response(cinfo, xfer_id, msg_type);
- break;
- default:
- WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
- break;
+ spin_lock_irqsave(&minfo->xfer_lock, flags);
+ if (refcount_dec_and_test(&xfer->users)) {
+ if (xfer->pending) {
+ scmi_xfer_token_clear(minfo, xfer);
+ xfer->pending = false;
+ }
+ hlist_add_head(&xfer->node, &minfo->free_xfers);
}
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
}
/**
* xfer_put() - Release a transmit message
*
* @ph: Pointer to SCMI protocol handle
- * @xfer: message that was reserved by scmi_xfer_get
+ * @xfer: message that was reserved by xfer_get_init
*/
static void xfer_put(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer)
@@ -307,6 +474,46 @@ static void xfer_put(const struct scmi_protocol_handle *ph,
__scmi_xfer_put(&info->tx_minfo, xfer);
}
+static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
+ struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer, unsigned int timeout_ms)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ /*
+ * Do not fetch_response if an out-of-order delayed
+ * response is being processed.
+ */
+ spin_lock_irqsave(&xfer->lock, flags);
+ if (xfer->state == SCMI_XFER_SENT_OK) {
+ desc->ops->fetch_response(cinfo, xfer);
+ xfer->state = SCMI_XFER_RESP_OK;
+ }
+ spin_unlock_irqrestore(&xfer->lock, flags);
+
+ return ret;
+}
+
+/**
+ * scmi_wait_for_message_response - An helper to group all the possible ways of
+ * waiting for a synchronous message response.
+ *
+ * @cinfo: SCMI channel info
+ * @xfer: Reference to the transfer being waited for.
+ *
+ * Return: 0 on Success, error otherwise.
+ */
+static int scmi_wait_for_message_response(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer)
+{
+ struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
+ struct device *dev = info->dev;
+
+ return scmi_wait_for_reply(dev, info->desc, cinfo, xfer,
+ info->desc->max_rx_timeout_ms);
+}
+
/**
* do_xfer() - Do one transfer
*
@@ -323,20 +530,30 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
int ret;
const struct scmi_protocol_instance *pi = ph_to_pi(ph);
struct scmi_info *info = handle_to_scmi_info(pi->handle);
- struct device_d *dev = info->dev;
+ struct device *dev = info->dev;
struct scmi_chan_info *cinfo;
- u64 start;
+
+ /* Check for polling request on custom command xfers at first */
+ if (!is_transport_polling_capable(info->desc)) {
+ dev_warn_once(dev,
+ "Polling mode is not supported by transport.\n");
+ return -EINVAL;
+ }
+
+ cinfo = idr_find(&info->tx_idr, pi->proto->id);
+ if (unlikely(!cinfo))
+ return -EINVAL;
/*
- * Re-instate protocol id here from protocol handle so that cannot be
+ * Initialise protocol id now from protocol handle to avoid it being
* overridden by mistake (or malice) by the protocol code mangling with
- * the scmi_xfer structure.
+ * the scmi_xfer structure prior to this.
*/
xfer->hdr.protocol_id = pi->proto->id;
- cinfo = idr_find(&info->tx_idr, xfer->hdr.protocol_id);
- if (unlikely(!cinfo))
- return -EINVAL;
+ /* Clear any stale status */
+ xfer->hdr.status = SCMI_SUCCESS;
+ xfer->state = SCMI_XFER_SENT_OK;
ret = info->desc->ops->send_message(cinfo, xfer);
if (ret < 0) {
@@ -344,21 +561,12 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
return ret;
}
- /* And we wait for the response. */
- start = get_time_ns();
- while (!xfer->done) {
- if (is_timeout(start, info->desc->max_rx_timeout_ms * (u64)NSEC_PER_MSEC)) {
- dev_err(dev, "timed out in resp(caller: %pS)\n", (void *)_RET_IP_);
- ret = -ETIMEDOUT;
- break;
- }
- }
-
+ ret = scmi_wait_for_message_response(cinfo, xfer);
if (!ret && xfer->hdr.status)
ret = scmi_to_linux_errno(xfer->hdr.status);
if (info->desc->ops->mark_txdone)
- info->desc->ops->mark_txdone(cinfo, ret);
+ info->desc->ops->mark_txdone(cinfo, ret, xfer);
return ret;
}
@@ -372,47 +580,6 @@ static void reset_rx_to_maxsz(const struct scmi_protocol_handle *ph,
xfer->rx.len = info->desc->max_msg_size;
}
-#define SCMI_MAX_RESPONSE_TIMEOUT_NS (2 * NSEC_PER_SEC)
-
-/**
- * do_xfer_with_response() - Do one transfer and wait until the delayed
- * response is received
- *
- * @ph: Pointer to SCMI protocol handle
- * @xfer: Transfer to initiate and wait for response
- *
- * Return: -ETIMEDOUT in case of no delayed response, if transmit error,
- * return corresponding error, else if all goes well, return 0.
- */
-static int do_xfer_with_response(const struct scmi_protocol_handle *ph,
- struct scmi_xfer *xfer)
-{
- int ret;
- const struct scmi_protocol_instance *pi = ph_to_pi(ph);
- bool async_response = false;
- u64 start;
-
- xfer->hdr.protocol_id = pi->proto->id;
-
- xfer->async_done = &async_response;
-
- ret = do_xfer(ph, xfer);
- if (ret)
- goto out;
-
- start = get_time_ns();
- while (!*xfer->async_done) {
- if (is_timeout(start, SCMI_MAX_RESPONSE_TIMEOUT_NS)) {
- ret = -ETIMEDOUT;
- break;
- }
- }
-
-out:
- xfer->async_done = NULL;
- return ret;
-}
-
/**
* xfer_get_init() - Allocate and initialise one message for transmit
*
@@ -437,7 +604,7 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
const struct scmi_protocol_instance *pi = ph_to_pi(ph);
struct scmi_info *info = handle_to_scmi_info(pi->handle);
struct scmi_xfers_info *minfo = &info->tx_minfo;
- struct device_d *dev = info->dev;
+ struct device *dev = info->dev;
/* Ensure we have sane transfer sizes */
if (rx_size > info->desc->max_msg_size ||
@@ -451,11 +618,19 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
return ret;
}
+ /* Pick a sequence number and register this xfer as in-flight */
+ ret = scmi_xfer_pending_set(xfer, minfo);
+ if (ret) {
+ dev_err(pi->handle->dev,
+ "Failed to get monotonic token %d\n", ret);
+ __scmi_xfer_put(minfo, xfer);
+ return ret;
+ }
+
xfer->tx.len = tx_size;
xfer->rx.len = rx_size ? : info->desc->max_msg_size;
+ xfer->hdr.type = MSG_TYPE_COMMAND;
xfer->hdr.id = msg_id;
- xfer->hdr.protocol_id = pi->proto->id;
- xfer->hdr.poll_completion = false;
*p = xfer;
@@ -529,10 +704,335 @@ static const struct scmi_xfer_ops xfer_ops = {
.xfer_get_init = xfer_get_init,
.reset_rx_to_maxsz = reset_rx_to_maxsz,
.do_xfer = do_xfer,
- .do_xfer_with_response = do_xfer_with_response,
.xfer_put = xfer_put,
};
+struct scmi_msg_resp_domain_name_get {
+ __le32 flags;
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+/**
+ * scmi_common_extended_name_get - Common helper to get extended resources name
+ * @ph: A protocol handle reference.
+ * @cmd_id: The specific command ID to use.
+ * @res_id: The specific resource ID to use.
+ * @name: A pointer to the preallocated area where the retrieved name will be
+ * stored as a NULL terminated string.
+ * @len: The len in bytes of the @name char array.
+ *
+ * Return: 0 on Succcess
+ */
+static int scmi_common_extended_name_get(const struct scmi_protocol_handle *ph,
+ u8 cmd_id, u32 res_id, char *name,
+ size_t len)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_domain_name_get *resp;
+
+ ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(res_id),
+ sizeof(*resp), &t);
+ if (ret)
+ goto out;
+
+ put_unaligned_le32(res_id, t->tx.buf);
+ resp = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ strscpy(name, resp->name, len);
+
+ ph->xops->xfer_put(ph, t);
+out:
+ if (ret)
+ dev_warn(ph->dev,
+ "Failed to get extended name - id:%u (ret:%d). Using %s\n",
+ res_id, ret, name);
+ return ret;
+}
+
+/**
+ * struct scmi_iterator - Iterator descriptor
+ * @msg: A reference to the message TX buffer; filled by @prepare_message with
+ * a proper custom command payload for each multi-part command request.
+ * @resp: A reference to the response RX buffer; used by @update_state and
+ * @process_response to parse the multi-part replies.
+ * @t: A reference to the underlying xfer initialized and used transparently by
+ * the iterator internal routines.
+ * @ph: A reference to the associated protocol handle to be used.
+ * @ops: A reference to the custom provided iterator operations.
+ * @state: The current iterator state; used and updated in turn by the iterators
+ * internal routines and by the caller-provided @scmi_iterator_ops.
+ * @priv: A reference to optional private data as provided by the caller and
+ * passed back to the @@scmi_iterator_ops.
+ */
+struct scmi_iterator {
+ void *msg;
+ void *resp;
+ struct scmi_xfer *t;
+ const struct scmi_protocol_handle *ph;
+ struct scmi_iterator_ops *ops;
+ struct scmi_iterator_state state;
+ void *priv;
+};
+
+static void *scmi_iterator_init(const struct scmi_protocol_handle *ph,
+ struct scmi_iterator_ops *ops,
+ unsigned int max_resources, u8 msg_id,
+ size_t tx_size, void *priv)
+{
+ int ret;
+ struct scmi_iterator *i;
+
+ i = devm_kzalloc(ph->dev, sizeof(*i), GFP_KERNEL);
+ if (!i)
+ return ERR_PTR(-ENOMEM);
+
+ i->ph = ph;
+ i->ops = ops;
+ i->priv = priv;
+
+ ret = ph->xops->xfer_get_init(ph, msg_id, tx_size, 0, &i->t);
+ if (ret) {
+ devm_kfree(ph->dev, i);
+ return ERR_PTR(ret);
+ }
+
+ i->state.max_resources = max_resources;
+ i->msg = i->t->tx.buf;
+ i->resp = i->t->rx.buf;
+
+ return i;
+}
+
+static int scmi_iterator_run(void *iter)
+{
+ int ret = -EINVAL;
+ struct scmi_iterator_ops *iops;
+ const struct scmi_protocol_handle *ph;
+ struct scmi_iterator_state *st;
+ struct scmi_iterator *i = iter;
+
+ if (!i || !i->ops || !i->ph)
+ return ret;
+
+ iops = i->ops;
+ ph = i->ph;
+ st = &i->state;
+
+ do {
+ iops->prepare_message(i->msg, st->desc_index, i->priv);
+ ret = ph->xops->do_xfer(ph, i->t);
+ if (ret)
+ break;
+
+ st->rx_len = i->t->rx.len;
+ ret = iops->update_state(st, i->resp, i->priv);
+ if (ret)
+ break;
+
+ if (st->num_returned > st->max_resources - st->desc_index) {
+ dev_err(ph->dev,
+ "No. of resources can't exceed %d\n",
+ st->max_resources);
+ ret = -EINVAL;
+ break;
+ }
+
+ for (st->loop_idx = 0; st->loop_idx < st->num_returned;
+ st->loop_idx++) {
+ ret = iops->process_response(ph, i->resp, st, i->priv);
+ if (ret)
+ goto out;
+ }
+
+ st->desc_index += st->num_returned;
+ ph->xops->reset_rx_to_maxsz(ph, i->t);
+ /*
+ * check for both returned and remaining to avoid infinite
+ * loop due to buggy firmware
+ */
+ } while (st->num_returned && st->num_remaining);
+
+out:
+ /* Finalize and destroy iterator */
+ ph->xops->xfer_put(ph, i->t);
+ devm_kfree(ph->dev, i);
+
+ return ret;
+}
+
+struct scmi_msg_get_fc_info {
+ __le32 domain;
+ __le32 message_id;
+};
+
+struct scmi_msg_resp_desc_fc {
+ __le32 attr;
+#define SUPPORTS_DOORBELL(x) ((x) & BIT(0))
+#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x))
+ __le32 rate_limit;
+ __le32 chan_addr_low;
+ __le32 chan_addr_high;
+ __le32 chan_size;
+ __le32 db_addr_low;
+ __le32 db_addr_high;
+ __le32 db_set_lmask;
+ __le32 db_set_hmask;
+ __le32 db_preserve_lmask;
+ __le32 db_preserve_hmask;
+};
+
+static void
+scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
+ u8 describe_id, u32 message_id, u32 valid_size,
+ u32 domain, void __iomem **p_addr,
+ struct scmi_fc_db_info **p_db)
+{
+ int ret;
+ u32 flags;
+ u64 phys_addr;
+ u8 size;
+ void __iomem *addr;
+ struct scmi_xfer *t;
+ struct scmi_fc_db_info *db = NULL;
+ struct scmi_msg_get_fc_info *info;
+ struct scmi_msg_resp_desc_fc *resp;
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+ if (!p_addr) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ ret = ph->xops->xfer_get_init(ph, describe_id,
+ sizeof(*info), sizeof(*resp), &t);
+ if (ret)
+ goto err_out;
+
+ info = t->tx.buf;
+ info->domain = cpu_to_le32(domain);
+ info->message_id = cpu_to_le32(message_id);
+
+ /*
+ * Bail out on error leaving fc_info addresses zeroed; this includes
+ * the case in which the requested domain/message_id does NOT support
+ * fastchannels at all.
+ */
+ ret = ph->xops->do_xfer(ph, t);
+ if (ret)
+ goto err_xfer;
+
+ resp = t->rx.buf;
+ flags = le32_to_cpu(resp->attr);
+ size = le32_to_cpu(resp->chan_size);
+ if (size != valid_size) {
+ ret = -EINVAL;
+ goto err_xfer;
+ }
+
+ phys_addr = le32_to_cpu(resp->chan_addr_low);
+ phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
+ addr = devm_ioremap(ph->dev, phys_addr, size);
+ if (!addr) {
+ ret = -EADDRNOTAVAIL;
+ goto err_xfer;
+ }
+
+ *p_addr = addr;
+
+ if (p_db && SUPPORTS_DOORBELL(flags)) {
+ db = devm_kzalloc(ph->dev, sizeof(*db), GFP_KERNEL);
+ if (!db) {
+ ret = -ENOMEM;
+ goto err_db;
+ }
+
+ size = 1 << DOORBELL_REG_WIDTH(flags);
+ phys_addr = le32_to_cpu(resp->db_addr_low);
+ phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
+ addr = devm_ioremap(ph->dev, phys_addr, size);
+ if (!addr) {
+ ret = -EADDRNOTAVAIL;
+ goto err_db_mem;
+ }
+
+ db->addr = addr;
+ db->width = size;
+ db->set = le32_to_cpu(resp->db_set_lmask);
+ db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32;
+ db->mask = le32_to_cpu(resp->db_preserve_lmask);
+ db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32;
+
+ *p_db = db;
+ }
+
+ ph->xops->xfer_put(ph, t);
+
+ dev_dbg(ph->dev,
+ "Using valid FC for protocol %X [MSG_ID:%u / RES_ID:%u]\n",
+ pi->proto->id, message_id, domain);
+
+ return;
+
+err_db_mem:
+ devm_kfree(ph->dev, db);
+
+err_db:
+ *p_addr = NULL;
+
+err_xfer:
+ ph->xops->xfer_put(ph, t);
+
+err_out:
+ dev_warn(ph->dev,
+ "Failed to get FC for protocol %X [MSG_ID:%u / RES_ID:%u] - ret:%d. Using regular messaging.\n",
+ pi->proto->id, message_id, domain, ret);
+}
+
+#define SCMI_PROTO_FC_RING_DB(w) \
+do { \
+ u##w val = 0; \
+ \
+ if (db->mask) \
+ val = ioread##w(db->addr) & db->mask; \
+ iowrite##w((u##w)db->set | val, db->addr); \
+} while (0)
+
+static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
+{
+ if (!db || !db->addr)
+ return;
+
+ if (db->width == 1)
+ SCMI_PROTO_FC_RING_DB(8);
+ else if (db->width == 2)
+ SCMI_PROTO_FC_RING_DB(16);
+ else if (db->width == 4)
+ SCMI_PROTO_FC_RING_DB(32);
+ else /* db->width == 8 */
+#ifdef CONFIG_64BIT
+ SCMI_PROTO_FC_RING_DB(64);
+#else
+ {
+ u64 val = 0;
+
+ if (db->mask)
+ val = ioread64_hi_lo(db->addr) & db->mask;
+ iowrite64_hi_lo(db->set | val, db->addr);
+ }
+#endif
+}
+
+static const struct scmi_proto_helpers_ops helpers_ops = {
+ .extended_name_get = scmi_common_extended_name_get,
+ .iter_response_init = scmi_iterator_init,
+ .iter_response_run = scmi_iterator_run,
+ .fastchannel_init = scmi_common_fastchannel_init,
+ .fastchannel_db_ring = scmi_common_fastchannel_db_ring,
+};
+
/**
* scmi_revision_area_get - Retrieve version memory area.
*
@@ -559,10 +1059,11 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph)
* @proto: The protocol descriptor.
*
* Allocate a new protocol instance descriptor, using the provided @proto
- * description, against the specified SCMI instance @info, and initialize it;
+ * description, against the specified SCMI instance @info, and initialize it.
*
+ * Context: Assumes to be called with @protocols_mtx already acquired.
* Return: A reference to a freshly allocated and initialized protocol instance
- * or ERR_PTR on failure. On failure the @proto reference is at first
+ * or ERR_PTR on failure.
*/
static struct scmi_protocol_instance *
scmi_alloc_init_protocol_instance(struct scmi_info *info,
@@ -572,7 +1073,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
struct scmi_protocol_instance *pi;
const struct scmi_handle *handle = &info->handle;
- pi = kzalloc(sizeof(*pi), GFP_KERNEL);
+ pi = devm_kzalloc(handle->dev, sizeof(*pi), GFP_KERNEL);
if (!pi)
goto clean;
@@ -580,9 +1081,10 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
pi->handle = handle;
pi->ph.dev = handle->dev;
pi->ph.xops = &xfer_ops;
+ pi->ph.hops = &helpers_ops;
pi->ph.set_priv = scmi_set_protocol_priv;
pi->ph.get_priv = scmi_get_protocol_priv;
- pi->users++;
+ refcount_set(&pi->users, 1);
/* proto->init is assured NON NULL by scmi_protocol_register */
ret = pi->proto->instance_init(&pi->ph);
if (ret)
@@ -618,10 +1120,11 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
struct scmi_protocol_instance *pi;
struct scmi_info *info = handle_to_scmi_info(handle);
+ mutex_lock(&info->protocols_mtx);
pi = idr_find(&info->protocols, protocol_id);
if (pi) {
- pi->users++;
+ refcount_inc(&pi->users);
} else {
const struct scmi_protocol *proto;
@@ -632,6 +1135,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
else
pi = ERR_PTR(-EPROBE_DEFER);
}
+ mutex_unlock(&info->protocols_mtx);
return pi;
}
@@ -651,6 +1155,38 @@ int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id)
return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
}
+/**
+ * scmi_protocol_release - Protocol de-initialization helper.
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ *
+ * Remove one user for the specified protocol and triggers de-initialization
+ * and resources de-allocation once the last user has gone.
+ */
+void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
+{
+ struct scmi_info *info = handle_to_scmi_info(handle);
+ struct scmi_protocol_instance *pi;
+
+ mutex_lock(&info->protocols_mtx);
+ pi = idr_find(&info->protocols, protocol_id);
+ if (WARN_ON(!pi))
+ goto out;
+
+ if (refcount_dec_and_test(&pi->users)) {
+ if (pi->proto->instance_deinit)
+ pi->proto->instance_deinit(&pi->ph);
+
+ idr_remove(&info->protocols, protocol_id);
+
+ dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
+ protocol_id);
+ }
+
+out:
+ mutex_unlock(&info->protocols_mtx);
+}
+
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
u8 *prot_imp)
{
@@ -665,110 +1201,67 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
{
int i;
struct scmi_info *info = handle_to_scmi_info(handle);
+ struct scmi_revision_info *rev = handle->version;
if (!info->protocols_imp)
return false;
- for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
+ for (i = 0; i < rev->num_protocols; i++)
if (info->protocols_imp[i] == prot_id)
return true;
return false;
}
-/**
- * scmi_dev_protocol_get - get protocol operations and handle
- * @protocol_id: The protocol being requested.
- * @ph: A pointer reference used to pass back the associated protocol handle.
- *
- * Get hold of a protocol accounting for its usage, eventually triggering its
- * initialization, and returning the protocol specific operations and related
- * protocol handle which will be used as first argument in most of the
- * protocols operations methods.
- * Being a devres based managed method, protocol hold will be automatically
- * released, and possibly de-initialized on last user, once the SCMI driver
- * owning the scmi_device is unbound from it.
- *
- * Return: A reference to the requested protocol operations or error.
- * Must be checked for errors by caller.
- */
static const void __must_check *
scmi_dev_protocol_get(struct scmi_device *sdev, u8 protocol_id,
struct scmi_protocol_handle **ph)
{
struct scmi_protocol_instance *pi;
- struct scmi_handle *handle = sdev->handle;
if (!ph)
return ERR_PTR(-EINVAL);
- pi = scmi_get_protocol_instance(handle, protocol_id);
+ pi = scmi_get_protocol_instance(sdev->handle, protocol_id);
if (IS_ERR(pi))
- return pi;
+ return ERR_CAST(pi);
*ph = &pi->ph;
return pi->proto->ops;
}
-static inline
-struct scmi_handle *scmi_handle_get_from_info_unlocked(struct scmi_info *info)
+static int __must_check scmi_dev_protocol_acquire(struct scmi_device *sdev,
+ u8 protocol_id)
{
- info->users++;
- return &info->handle;
+ return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(sdev->handle, protocol_id));
}
-/**
- * scmi_handle_get() - Get the SCMI handle for a device
- *
- * @dev: pointer to device for which we want SCMI handle
- *
- * NOTE: The function does not track individual clients of the framework
- * and is expected to be maintained by caller of SCMI protocol library.
- * scmi_handle_put must be balanced with successful scmi_handle_get
- *
- * Return: pointer to handle if successful, NULL on error
- */
-struct scmi_handle *scmi_handle_get(struct device_d *dev)
+static void scmi_dev_protocol_put(struct scmi_device *sdev, u8 protocol_id)
{
- struct list_head *p;
- struct scmi_info *info;
- struct scmi_handle *handle = NULL;
-
- list_for_each(p, &scmi_list) {
- info = list_entry(p, struct scmi_info, node);
- if (dev->parent == info->dev) {
- handle = scmi_handle_get_from_info_unlocked(info);
- break;
- }
- }
-
- return handle;
+ scmi_protocol_release(sdev->handle, protocol_id);
}
/**
- * scmi_handle_put() - Release the handle acquired by scmi_handle_get
+ * scmi_is_transport_atomic - Method to check if underlying transport for an
+ * SCMI instance is configured as atomic.
*
- * @handle: handle acquired by scmi_handle_get
- *
- * NOTE: The function does not track individual clients of the framework
- * and is expected to be maintained by caller of SCMI protocol library.
- * scmi_handle_put must be balanced with successful scmi_handle_get
+ * @handle: A reference to the SCMI platform instance.
+ * @atomic_threshold: An optional return value for the system wide currently
+ * configured threshold for atomic operations.
*
- * Return: 0 is successfully released
- * if null was passed, it returns -EINVAL;
+ * Return: True if transport is configured as atomic
*/
-int scmi_handle_put(const struct scmi_handle *handle)
+static bool scmi_is_transport_atomic(const struct scmi_handle *handle,
+ unsigned int *atomic_threshold)
{
- struct scmi_info *info;
-
- if (!handle)
- return -EINVAL;
+ bool ret;
+ struct scmi_info *info = handle_to_scmi_info(handle);
- info = handle_to_scmi_info(handle);
- if (!WARN_ON(!info->users))
- info->users--;
+ ret = is_transport_polling_capable(info->desc);
+ if (ret && atomic_threshold)
+ *atomic_threshold = info->atomic_threshold;
- return 0;
+ return ret;
}
static int __scmi_xfer_info_init(struct scmi_info *sinfo,
@@ -776,34 +1269,71 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
{
int i;
struct scmi_xfer *xfer;
- struct device_d *dev = sinfo->dev;
+ struct device *dev = sinfo->dev;
const struct scmi_desc *desc = sinfo->desc;
/* Pre-allocated messages, no more than what hdr.seq can support */
- if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) {
- dev_err(dev, "Maximum message of %d exceeds supported %ld\n",
- desc->max_msg, MSG_TOKEN_MAX);
+ if (WARN_ON(!info->max_msg || info->max_msg > MSG_TOKEN_MAX)) {
+ dev_err(dev,
+ "Invalid maximum messages %d, not in range [1 - %lu]\n",
+ info->max_msg, MSG_TOKEN_MAX);
return -EINVAL;
}
- info->xfer_block = kcalloc(desc->max_msg, sizeof(*info->xfer_block), GFP_KERNEL);
- if (!info->xfer_block)
- return -ENOMEM;
-
- info->xfer_alloc_table = kcalloc(BITS_TO_LONGS(desc->max_msg),
- sizeof(long), GFP_KERNEL);
+ /* Allocate a bitmask sized to hold MSG_TOKEN_MAX tokens */
+ info->xfer_alloc_table = devm_bitmap_zalloc(dev, MSG_TOKEN_MAX,
+ GFP_KERNEL);
if (!info->xfer_alloc_table)
return -ENOMEM;
- /* Pre-initialize the buffer pointer to pre-allocated buffers */
- for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
- xfer->rx.buf = kcalloc(sizeof(u8), desc->max_msg_size,
+ /*
+ * Preallocate a number of xfers equal to max inflight messages,
+ * pre-initialize the buffer pointer to pre-allocated buffers and
+ * attach all of them to the free list
+ */
+ INIT_HLIST_HEAD(&info->free_xfers);
+ for (i = 0; i < info->max_msg; i++) {
+ xfer = devm_kzalloc(dev, sizeof(*xfer), GFP_KERNEL);
+ if (!xfer)
+ return -ENOMEM;
+
+ xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), desc->max_msg_size,
GFP_KERNEL);
if (!xfer->rx.buf)
return -ENOMEM;
xfer->tx.buf = xfer->rx.buf;
- xfer->done = false;
+ spin_lock_init(&xfer->lock);
+
+ /* Add initialized xfer to the free list */
+ hlist_add_head(&xfer->node, &info->free_xfers);
+ }
+
+ spin_lock_init(&info->xfer_lock);
+
+ return 0;
+}
+
+static int scmi_channels_max_msg_configure(struct scmi_info *sinfo)
+{
+ const struct scmi_desc *desc = sinfo->desc;
+
+ if (!desc->ops->get_max_msg) {
+ sinfo->tx_minfo.max_msg = desc->max_msg;
+ sinfo->rx_minfo.max_msg = desc->max_msg;
+ } else {
+ struct scmi_chan_info *base_cinfo;
+
+ base_cinfo = idr_find(&sinfo->tx_idr, SCMI_PROTOCOL_BASE);
+ if (!base_cinfo)
+ return -EINVAL;
+ sinfo->tx_minfo.max_msg = desc->ops->get_max_msg(base_cinfo);
+
+ /* RX channel is optional so can be skipped */
+ base_cinfo = idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE);
+ if (base_cinfo)
+ sinfo->rx_minfo.max_msg =
+ desc->ops->get_max_msg(base_cinfo);
}
return 0;
@@ -811,51 +1341,119 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
static int scmi_xfer_info_init(struct scmi_info *sinfo)
{
- int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
+ int ret;
+
+ ret = scmi_channels_max_msg_configure(sinfo);
+ if (ret)
+ return ret;
- if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE))
+ ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
+ if (!ret && !idr_is_empty(&sinfo->rx_idr))
ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo);
return ret;
}
-static int scmi_chan_setup(struct scmi_info *info, struct device_d *dev,
+/**
+ * scmi_handle_get() - Get the SCMI handle for a device
+ *
+ * @dev: pointer to device for which we want SCMI handle
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of SCMI protocol library.
+ * scmi_handle_put must be balanced with successful scmi_handle_get
+ *
+ * Return: pointer to handle if successful, NULL on error
+ */
+struct scmi_handle *scmi_handle_get(struct device *dev)
+{
+ struct list_head *p;
+ struct scmi_info *info;
+ struct scmi_handle *handle = NULL;
+
+ mutex_lock(&scmi_list_mutex);
+ list_for_each(p, &scmi_list) {
+ info = list_entry(p, struct scmi_info, node);
+ if (dev->parent == info->dev) {
+ handle = &info->handle;
+ break;
+ }
+ }
+ mutex_unlock(&scmi_list_mutex);
+
+ return handle;
+}
+
+static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
int prot_id, bool tx)
{
int ret, idx;
+ char name[32];
struct scmi_chan_info *cinfo;
struct idr *idr;
+ struct scmi_device *tdev = NULL;
/* Transmit channel is first entry i.e. index 0 */
idx = tx ? 0 : 1;
idr = tx ? &info->tx_idr : &info->rx_idr;
- /* check if already allocated, used for multiple device per protocol */
- cinfo = idr_find(idr, prot_id);
- if (cinfo)
- return 0;
-
- if (!info->desc->ops->chan_available(dev, idx)) {
+ if (!info->desc->ops->chan_available(of_node, idx)) {
cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
return -EINVAL;
goto idr_alloc;
}
- cinfo = kzalloc(sizeof(*cinfo), GFP_KERNEL);
+ cinfo = devm_kzalloc(info->dev, sizeof(*cinfo), GFP_KERNEL);
if (!cinfo)
return -ENOMEM;
- cinfo->dev = dev;
+ cinfo->rx_timeout_ms = info->desc->max_rx_timeout_ms;
+
+ /* Create a unique name for this transport device */
+ snprintf(name, 32, "__scmi_transport_device_%s_%02X",
+ idx ? "rx" : "tx", prot_id);
+ /* Create a uniquely named, dedicated transport device for this chan */
+ tdev = scmi_device_create(of_node, info->dev, prot_id, name);
+ if (!tdev) {
+ dev_err(info->dev,
+ "failed to create transport device (%s)\n", name);
+ devm_kfree(info->dev, cinfo);
+ return -EINVAL;
+ }
+ of_node_get(of_node);
+ cinfo->id = prot_id;
+ cinfo->dev = &tdev->dev;
ret = info->desc->ops->chan_setup(cinfo, info->dev, tx);
- if (ret)
+ if (ret) {
+ of_node_put(of_node);
+ scmi_device_destroy(info->dev, prot_id, name);
+ devm_kfree(info->dev, cinfo);
return ret;
+ }
+
+ if (tx && is_polling_required(cinfo, info->desc)) {
+ if (is_transport_polling_capable(info->desc))
+ dev_dbg(&tdev->dev,
+ "Enabled polling mode TX channel - prot_id:%d\n",
+ prot_id);
+ else
+ dev_warn(&tdev->dev,
+ "Polling mode NOT supported by transport.\n");
+ }
idr_alloc:
ret = idr_alloc_one(idr, cinfo, prot_id);
if (ret != prot_id) {
- dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret);
+ dev_err(info->dev,
+ "unable to allocate SCMI idr slot err %d\n", ret);
+ /* Destroy channel and device only if created by this call. */
+ if (tdev) {
+ of_node_put(of_node);
+ scmi_device_destroy(info->dev, prot_id, name);
+ devm_kfree(info->dev, cinfo);
+ }
return ret;
}
@@ -864,311 +1462,160 @@ idr_alloc:
}
static inline int
-scmi_txrx_setup(struct scmi_info *info, struct device_d *dev, int prot_id)
+scmi_txrx_setup(struct scmi_info *info, struct device_node *of_node,
+ int prot_id)
{
- int ret = scmi_chan_setup(info, dev, prot_id, true);
+ int ret = scmi_chan_setup(info, of_node, prot_id, true);
- if (!ret) /* Rx is optional, hence no error check */
- scmi_chan_setup(info, dev, prot_id, false);
+ if (!ret) {
+ /* Rx is optional, report only memory errors */
+ ret = scmi_chan_setup(info, of_node, prot_id, false);
+ if (ret && ret != -ENOMEM)
+ ret = 0;
+ }
return ret;
}
/**
- * scmi_get_protocol_device - Helper to get/create an SCMI device.
- *
- * @np: A device node representing a valid active protocols for the referred
- * SCMI instance.
- * @info: The referred SCMI instance for which we are getting/creating this
- * device.
- * @prot_id: The protocol ID.
- * @name: The device name.
- *
- * Referring to the specific SCMI instance identified by @info, this helper
- * takes care to return a properly initialized device matching the requested
- * @proto_id and @name: if device was still not existent it is created as a
- * child of the specified SCMI instance @info and its transport properly
- * initialized as usual.
+ * scmi_channels_setup - Helper to initialize all required channels
+ *
+ * @info: The SCMI instance descriptor.
+ *
+ * Initialize all the channels found described in the DT against the underlying
+ * configured transport using custom defined dedicated devices instead of
+ * borrowing devices from the SCMI drivers; this way channels are initialized
+ * upfront during core SCMI stack probing and are no more coupled with SCMI
+ * devices used by SCMI drivers.
+ *
+ * Note that, even though a pair of TX/RX channels is associated to each
+ * protocol defined in the DT, a distinct freshly initialized channel is
+ * created only if the DT node for the protocol at hand describes a dedicated
+ * channel: in all the other cases the common BASE protocol channel is reused.
+ *
+ * Return: 0 on Success
*/
-static inline struct scmi_device *
-scmi_get_protocol_device(struct device_node *np, struct scmi_info *info,
- int prot_id, const char *name)
+static int scmi_channels_setup(struct scmi_info *info)
{
- struct scmi_device *sdev;
+ int ret;
+ struct device_node *child, *top_np = info->dev->of_node;
- /* Already created for this parent SCMI instance ? */
- sdev = scmi_child_dev_find(info->dev, prot_id, name);
- if (sdev)
- return sdev;
+ /* Initialize a common generic channel at first */
+ ret = scmi_txrx_setup(info, top_np, SCMI_PROTOCOL_BASE);
+ if (ret)
+ return ret;
- pr_debug("Creating SCMI device (%s) for protocol %x\n", name, prot_id);
+ for_each_available_child_of_node(top_np, child) {
+ u32 prot_id;
- sdev = scmi_device_alloc(np, info->dev, prot_id, name);
- if (!sdev) {
- dev_err(info->dev, "failed to create %d protocol device\n",
- prot_id);
- return NULL;
- }
+ if (of_property_read_u32(child, "reg", &prot_id))
+ continue;
- if (scmi_txrx_setup(info, &sdev->dev, prot_id)) {
- dev_err(&sdev->dev, "failed to setup transport\n");
- scmi_device_destroy(sdev);
- return NULL;
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
+ dev_err(info->dev,
+ "Out of range protocol %d\n", prot_id);
+
+ ret = scmi_txrx_setup(info, child, prot_id);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
}
- return sdev;
+ return 0;
}
-static inline void
-scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
- int prot_id, const char *name)
+static int scmi_chan_destroy(int id, void *p, void *idr)
{
- struct scmi_device *sdev;
+ struct scmi_chan_info *cinfo = p;
- sdev = scmi_get_protocol_device(np, info, prot_id, name);
- if (!sdev)
- return;
+ if (cinfo->dev) {
+ struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
+ struct scmi_device *sdev = to_scmi_dev(cinfo->dev);
- /* setup handle now as the transport is ready */
- scmi_set_handle(sdev);
+ of_node_put(cinfo->dev->of_node);
+ scmi_device_destroy(info->dev, id, sdev->name);
+ cinfo->dev = NULL;
+ }
- /* Register if not done yet */
- if (sdev->dev.id == DEVICE_ID_DYNAMIC)
- register_device(&sdev->dev);
+ idr_remove(idr, id);
+
+ return 0;
}
-/**
- * scmi_create_protocol_devices - Create devices for all pending requests for
- * this SCMI instance.
- *
- * @np: The device node describing the protocol
- * @info: The SCMI instance descriptor
- * @prot_id: The protocol ID
- *
- * All devices previously requested for this instance (if any) are found and
- * created by scanning the proper @&scmi_requested_devices entry.
- */
-static void scmi_create_protocol_devices(struct device_node *np,
- struct scmi_info *info, int prot_id)
+static void scmi_cleanup_channels(struct scmi_info *info, struct idr *idr)
{
- struct list_head *phead;
+ /* At first free all channels at the transport layer ... */
+ idr_for_each(idr, info->desc->ops->chan_free, idr);
- phead = idr_find(&scmi_requested_devices, prot_id);
- if (phead) {
- struct scmi_requested_dev *rdev;
+ /* ...then destroy all underlying devices */
+ idr_for_each(idr, scmi_chan_destroy, idr);
- list_for_each_entry(rdev, phead, node)
- scmi_create_protocol_device(np, info, prot_id,
- rdev->id_table->name);
- }
+ idr_destroy(idr);
}
-/**
- * scmi_protocol_device_request - Helper to request a device
- *
- * @id_table: A protocol/name pair descriptor for the device to be created.
- *
- * This helper let an SCMI driver request specific devices identified by the
- * @id_table to be created for each active SCMI instance.
- *
- * The requested device name MUST NOT be already existent for any protocol;
- * at first the freshly requested @id_table is annotated in the IDR table
- * @scmi_requested_devices, then a matching device is created for each already
- * active SCMI instance. (if any)
- *
- * This way the requested device is created straight-away for all the already
- * initialized(probed) SCMI instances (handles) and it remains also annotated
- * as pending creation if the requesting SCMI driver was loaded before some
- * SCMI instance and related transports were available: when such late instance
- * is probed, its probe will take care to scan the list of pending requested
- * devices and create those on its own (see @scmi_create_protocol_devices and
- * its enclosing loop)
- *
- * Return: 0 on Success
- */
-int scmi_protocol_device_request(const struct scmi_device_id *id_table)
+static void scmi_cleanup_txrx_channels(struct scmi_info *info)
{
- int ret = 0;
- struct list_head *phead = NULL;
- struct scmi_requested_dev *rdev;
- struct scmi_info *info;
- struct idr *idr;
-
- pr_debug("Requesting SCMI device (%s) for protocol 0x%x\n",
- id_table->name, id_table->protocol_id);
+ scmi_cleanup_channels(info, &info->tx_idr);
- /*
- * Search for the matching protocol rdev list and then search
- * of any existent equally named device...fails if any duplicate found.
- */
- __idr_for_each_entry(&scmi_requested_devices, idr) {
- struct list_head *head = idr->ptr;
- if (!phead) {
- /* A list found registered in the IDR is never empty */
- rdev = list_first_entry(head, struct scmi_requested_dev,
- node);
- if (rdev->id_table->protocol_id ==
- id_table->protocol_id)
- phead = head;
- }
- list_for_each_entry(rdev, head, node) {
- if (!strcmp(rdev->id_table->name, id_table->name)) {
- pr_err("Ignoring duplicate request [%d] %s\n",
- rdev->id_table->protocol_id,
- rdev->id_table->name);
- ret = -EINVAL;
- goto out;
- }
- }
- }
-
- /*
- * No duplicate found for requested id_table, so let's create a new
- * requested device entry for this new valid request.
- */
- rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
- if (!rdev) {
- ret = -ENOMEM;
- goto out;
- }
- rdev->id_table = id_table;
-
- /*
- * Append the new requested device table descriptor to the head of the
- * related protocol list, eventually creating such head if not already
- * there.
- */
- if (!phead) {
- phead = kzalloc(sizeof(*phead), GFP_KERNEL);
- if (!phead) {
- kfree(rdev);
- ret = -ENOMEM;
- goto out;
- }
- INIT_LIST_HEAD(phead);
-
- ret = idr_alloc_one(&scmi_requested_devices, (void *)phead,
- id_table->protocol_id);
- if (ret != id_table->protocol_id) {
- pr_err("Failed to save SCMI device - ret:%d\n", ret);
- kfree(rdev);
- kfree(phead);
- ret = -EINVAL;
- goto out;
- }
- ret = 0;
- }
- list_add(&rdev->node, phead);
-
- /*
- * Now effectively create and initialize the requested device for every
- * already initialized SCMI instance which has registered the requested
- * protocol as a valid active one: i.e. defined in DT and supported by
- * current platform FW.
- */
- list_for_each_entry(info, &scmi_list, node) {
- struct device_node *child;
-
- child = idr_find(&info->active_protocols,
- id_table->protocol_id);
- if (child) {
- struct scmi_device *sdev;
-
- sdev = scmi_get_protocol_device(child, info,
- id_table->protocol_id,
- id_table->name);
- /* Set handle if not already set: device existed */
- if (sdev && !sdev->handle)
- sdev->handle =
- scmi_handle_get_from_info_unlocked(info);
- } else {
- dev_err(info->dev,
- "Failed. SCMI protocol %d not active.\n",
- id_table->protocol_id);
- }
- }
-
-out:
- return ret;
+ scmi_cleanup_channels(info, &info->rx_idr);
}
-/**
- * scmi_protocol_device_unrequest - Helper to unrequest a device
- *
- * @id_table: A protocol/name pair descriptor for the device to be unrequested.
- *
- * An helper to let an SCMI driver release its request about devices; note that
- * devices are created and initialized once the first SCMI driver request them
- * but they destroyed only on SCMI core unloading/unbinding.
- *
- * The current SCMI transport layer uses such devices as internal references and
- * as such they could be shared as same transport between multiple drivers so
- * that cannot be safely destroyed till the whole SCMI stack is removed.
- * (unless adding further burden of refcounting.)
- */
-void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
+static int scmi_device_request_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
{
- struct list_head *phead;
-
- pr_debug("Unrequesting SCMI device (%s) for protocol %x\n",
- id_table->name, id_table->protocol_id);
-
- phead = idr_find(&scmi_requested_devices, id_table->protocol_id);
- if (phead) {
- struct scmi_requested_dev *victim, *tmp;
-
- list_for_each_entry_safe(victim, tmp, phead, node) {
- if (!strcmp(victim->id_table->name, id_table->name)) {
- list_del(&victim->node);
- kfree(victim);
- break;
- }
- }
-
- if (list_empty(phead)) {
- idr_remove(&scmi_requested_devices,
- id_table->protocol_id);
- kfree(phead);
- }
+ struct device_node *np;
+ struct scmi_device_id *id_table = data;
+ struct scmi_info *info = req_nb_to_scmi_info(nb);
+
+ np = idr_find(&info->active_protocols, id_table->protocol_id);
+ if (!np)
+ return NOTIFY_DONE;
+
+ dev_dbg(info->dev, "%sRequested device (%s) for protocol 0x%x\n",
+ action == SCMI_BUS_NOTIFY_DEVICE_REQUEST ? "" : "UN-",
+ id_table->name, id_table->protocol_id);
+
+ switch (action) {
+ case SCMI_BUS_NOTIFY_DEVICE_REQUEST:
+ scmi_create_protocol_devices(np, info, id_table->protocol_id,
+ id_table->name);
+ break;
+ case SCMI_BUS_NOTIFY_DEVICE_UNREQUEST:
+ scmi_destroy_protocol_devices(info, id_table->protocol_id,
+ id_table->name);
+ break;
+ default:
+ return NOTIFY_DONE;
}
-}
-static void version_info(struct device_d *dev)
-{
- struct scmi_info *info = dev->priv;
-
- printf("SCMI information:\n"
- " version: %u.%u\n"
- " firmware version: 0x%x\n"
- " vendor: %s (sub: %s)\n",
- info->version.minor_ver,
- info->version.major_ver,
- info->version.impl_ver,
- info->version.vendor_id,
- info->version.sub_vendor_id);
+ return NOTIFY_OK;
}
-static int scmi_probe(struct device_d *dev)
+static int scmi_probe(struct device *dev)
{
int ret;
struct scmi_handle *handle;
const struct scmi_desc *desc;
struct scmi_info *info;
- struct device_node *child, *np = dev->device_node;
+ struct device_node *child, *np = dev->of_node;
desc = of_device_get_match_data(dev);
if (!desc)
return -EINVAL;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->dev = dev;
info->desc = desc;
+ info->dev_req_nb.notifier_call = scmi_device_request_notifier;
INIT_LIST_HEAD(&info->node);
idr_init(&info->protocols);
+ mutex_init(&info->protocols_mtx);
idr_init(&info->active_protocols);
+ mutex_init(&info->devreq_mtx);
dev->priv = info;
idr_init(&info->tx_idr);
@@ -1177,28 +1624,55 @@ static int scmi_probe(struct device_d *dev)
handle = &info->handle;
handle->dev = info->dev;
handle->version = &info->version;
- handle->protocol_get = scmi_dev_protocol_get;
+ handle->dev_protocol_acquire = scmi_dev_protocol_acquire;
+ handle->dev_protocol_get = scmi_dev_protocol_get;
+ handle->dev_protocol_put = scmi_dev_protocol_put;
+
+ /* System wide atomic threshold for atomic ops .. if any */
+ if (!of_property_read_u32(np, "atomic-threshold-us",
+ &info->atomic_threshold))
+ dev_info(dev,
+ "SCMI System wide atomic threshold set to %d us\n",
+ info->atomic_threshold);
+ handle->is_transport_atomic = scmi_is_transport_atomic;
+
+ if (desc->ops->link_supplier) {
+ ret = desc->ops->link_supplier(dev);
+ if (ret)
+ goto clear_ida;
+ }
- ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
+ /* Setup all channels described in the DT at first */
+ ret = scmi_channels_setup(info);
if (ret)
- return ret;
+ goto clear_ida;
+
+ ret = blocking_notifier_chain_register(&scmi_requested_devices_nh,
+ &info->dev_req_nb);
+ if (ret)
+ goto clear_txrx_setup;
ret = scmi_xfer_info_init(info);
if (ret)
- return ret;
+ goto clear_dev_req_notifier;
+
+ if (!is_transport_polling_capable(info->desc))
+ dev_err(dev,
+ "Transport is not polling capable. Atomic mode not supported.\n");
/*
* Trigger SCMI Base protocol initialization.
- * It's mandatory and won't be ever released/deinit until the
- * SCMI stack is shutdown/unloaded as a whole.
+ * It's mandatory and won't be ever released/deinit.
*/
ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
if (ret) {
dev_err(dev, "unable to communicate with SCMI\n");
- return ret;
+ goto notification_exit;
}
+ mutex_lock(&scmi_list_mutex);
list_add_tail(&info->node, &scmi_list);
+ mutex_unlock(&scmi_list_mutex);
for_each_available_child_of_node(np, child) {
u32 prot_id;
@@ -1226,49 +1700,68 @@ static int scmi_probe(struct device_d *dev)
continue;
}
- scmi_create_protocol_devices(child, info, prot_id);
+ of_node_get(child);
+ scmi_create_protocol_devices(child, info, prot_id, NULL);
}
- dev->info = version_info;
-
return 0;
-}
-void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id)
-{
- idr_remove(idr, id);
+notification_exit:
+clear_dev_req_notifier:
+ blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
+ &info->dev_req_nb);
+clear_txrx_setup:
+ scmi_cleanup_txrx_channels(info);
+clear_ida:
+ return ret;
}
/* Each compatible listed below must have descriptor associated with it */
static const struct of_device_id scmi_of_match[] = {
-#ifdef CONFIG_ARM_SMCCC
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE
+ { .compatible = "linaro,scmi-optee", .data = &scmi_optee_desc },
+#endif
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
+ { .compatible = "arm,scmi-smc-param", .data = &scmi_smc_desc},
#endif
{ /* Sentinel */ },
};
-static struct driver_d arm_scmi_driver = {
+MODULE_DEVICE_TABLE(of, scmi_of_match);
+
+static struct driver arm_scmi_driver = {
.name = "arm-scmi",
- .of_compatible = scmi_of_match,
+ .of_match_table = scmi_of_match,
.probe = scmi_probe,
};
core_platform_driver(arm_scmi_driver);
static int __init scmi_bus_driver_init(void)
{
- scmi_bus_init();
+ int ret;
+
+ ret = scmi_bus_init();
+ if (ret)
+ return ret;
+
+ /* Bail out if no SCMI transport was configured */
+ if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT)))
+ return -EINVAL;
scmi_base_register();
- scmi_reset_register();
scmi_clock_register();
+ scmi_power_register();
+ scmi_reset_register();
+ scmi_sensors_register();
scmi_voltage_register();
return 0;
}
pure_initcall(scmi_bus_driver_init);
-MODULE_ALIAS("platform: arm-scmi");
+MODULE_ALIAS("platform:arm-scmi");
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCMI protocol driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/arm_scmi/msg.c b/drivers/firmware/arm_scmi/msg.c
new file mode 100644
index 0000000000..6e621223af
--- /dev/null
+++ b/drivers/firmware/arm_scmi/msg.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * For transports using message passing.
+ *
+ * Derived from shm.c.
+ *
+ * Copyright (C) 2019-2021 ARM Ltd.
+ * Copyright (C) 2020-2021 OpenSynergy GmbH
+ */
+
+#include <linux/types.h>
+
+#include "common.h"
+
+/*
+ * struct scmi_msg_payld - Transport SDU layout
+ *
+ * The SCMI specification requires all parameters, message headers, return
+ * arguments or any protocol data to be expressed in little endian format only.
+ */
+struct scmi_msg_payld {
+ __le32 msg_header;
+ __le32 msg_payload[];
+};
+
+/**
+ * msg_command_size() - Actual size of transport SDU for command.
+ *
+ * @xfer: message which core has prepared for sending
+ *
+ * Return: transport SDU size.
+ */
+size_t msg_command_size(struct scmi_xfer *xfer)
+{
+ return sizeof(struct scmi_msg_payld) + xfer->tx.len;
+}
+
+/**
+ * msg_response_size() - Maximum size of transport SDU for response.
+ *
+ * @xfer: message which core has prepared for sending
+ *
+ * Return: transport SDU size.
+ */
+size_t msg_response_size(struct scmi_xfer *xfer)
+{
+ return sizeof(struct scmi_msg_payld) + sizeof(__le32) + xfer->rx.len;
+}
+
+/**
+ * msg_tx_prepare() - Set up transport SDU for command.
+ *
+ * @msg: transport SDU for command
+ * @xfer: message which is being sent
+ */
+void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer)
+{
+ msg->msg_header = cpu_to_le32(pack_scmi_header(&xfer->hdr));
+ if (xfer->tx.buf)
+ memcpy(msg->msg_payload, xfer->tx.buf, xfer->tx.len);
+}
+
+/**
+ * msg_read_header() - Read SCMI header from transport SDU.
+ *
+ * @msg: transport SDU
+ *
+ * Return: SCMI header
+ */
+u32 msg_read_header(struct scmi_msg_payld *msg)
+{
+ return le32_to_cpu(msg->msg_header);
+}
+
+/**
+ * msg_fetch_response() - Fetch response SCMI payload from transport SDU.
+ *
+ * @msg: transport SDU with response
+ * @len: transport SDU size
+ * @xfer: message being responded to
+ */
+void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
+ struct scmi_xfer *xfer)
+{
+ size_t prefix_len = sizeof(*msg) + sizeof(msg->msg_payload[0]);
+
+ xfer->hdr.status = le32_to_cpu(msg->msg_payload[0]);
+ xfer->rx.len = min_t(size_t, xfer->rx.len,
+ len >= prefix_len ? len - prefix_len : 0);
+
+ /* Take a copy to the rx buffer.. */
+ memcpy(xfer->rx.buf, &msg->msg_payload[1], xfer->rx.len);
+}
diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c
new file mode 100644
index 0000000000..1eff819af5
--- /dev/null
+++ b/drivers/firmware/arm_scmi/optee.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2021 Linaro Ltd.
+ */
+
+#include <linux/io.h>
+#include <of.h>
+#include <of_address.h>
+#include <linux/kernel.h>
+#include <module.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+#include <uapi/linux/tee.h>
+
+#include "common.h"
+
+#define SCMI_OPTEE_MAX_MSG_SIZE 128
+
+enum scmi_optee_pta_cmd {
+ /*
+ * PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities
+ *
+ * [out] value[0].a: Capability bit mask (enum pta_scmi_caps)
+ * [out] value[0].b: Extended capabilities or 0
+ */
+ PTA_SCMI_CMD_CAPABILITIES = 0,
+
+ /*
+ * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer
+ *
+ * [in] value[0].a: Channel handle
+ *
+ * Shared memory used for SCMI message/response exhange is expected
+ * already identified and bound to channel handle in both SCMI agent
+ * and SCMI server (OP-TEE) parts.
+ * The memory uses SMT header to carry SCMI meta-data (protocol ID and
+ * protocol message ID).
+ */
+ PTA_SCMI_CMD_PROCESS_SMT_CHANNEL = 1,
+
+ /*
+ * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message
+ *
+ * [in] value[0].a: Channel handle
+ * [in/out] memref[1]: Message/response buffer (SMT and SCMI payload)
+ *
+ * Shared memory used for SCMI message/response is a SMT buffer
+ * referenced by param[1]. It shall be 128 bytes large to fit response
+ * payload whatever message playload size.
+ * The memory uses SMT header to carry SCMI meta-data (protocol ID and
+ * protocol message ID).
+ */
+ PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE = 2,
+
+ /*
+ * PTA_SCMI_CMD_GET_CHANNEL - Get channel handle
+ *
+ * SCMI shm information are 0 if agent expects to use OP-TEE regular SHM
+ *
+ * [in] value[0].a: Channel identifier
+ * [out] value[0].a: Returned channel handle
+ * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps)
+ */
+ PTA_SCMI_CMD_GET_CHANNEL = 3,
+
+ /*
+ * PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG
+ * buffer pointed by memref parameters
+ *
+ * [in] value[0].a: Channel handle
+ * [in] memref[1]: Message buffer (MSG and SCMI payload)
+ * [out] memref[2]: Response buffer (MSG and SCMI payload)
+ *
+ * Shared memories used for SCMI message/response are MSG buffers
+ * referenced by param[1] and param[2]. MSG transport protocol
+ * uses a 32bit header to carry SCMI meta-data (protocol ID and
+ * protocol message ID) followed by the effective SCMI message
+ * payload.
+ */
+ PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4,
+};
+
+/*
+ * OP-TEE SCMI service capabilities bit flags (32bit)
+ *
+ * PTA_SCMI_CAPS_SMT_HEADER
+ * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in
+ * shared memory buffers to carry SCMI protocol synchronisation information.
+ *
+ * PTA_SCMI_CAPS_MSG_HEADER
+ * When set, OP-TEE supports command using MSG header protocol in an OP-TEE
+ * shared memory to carry SCMI protocol synchronisation information and SCMI
+ * message payload.
+ */
+#define PTA_SCMI_CAPS_NONE 0
+#define PTA_SCMI_CAPS_SMT_HEADER BIT(0)
+#define PTA_SCMI_CAPS_MSG_HEADER BIT(1)
+#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \
+ PTA_SCMI_CAPS_MSG_HEADER)
+
+/**
+ * struct scmi_optee_channel - Description of an OP-TEE SCMI channel
+ *
+ * @channel_id: OP-TEE channel ID used for this transport
+ * @tee_session: TEE session identifier
+ * @caps: OP-TEE SCMI channel capabilities
+ * @rx_len: Response size
+ * @cinfo: SCMI channel information
+ * @shmem: Virtual base address of the shared memory
+ * @req: Shared memory protocol handle for SCMI request and synchronous response
+ * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem
+ * @link: Reference in agent's channel list
+ */
+struct scmi_optee_channel {
+ u32 channel_id;
+ u32 tee_session;
+ u32 caps;
+ u32 rx_len;
+ struct scmi_chan_info *cinfo;
+ union {
+ struct scmi_shared_mem __iomem *shmem;
+ struct scmi_msg_payld *msg;
+ } req;
+ struct tee_shm *tee_shm;
+ struct list_head link;
+};
+
+/**
+ * struct scmi_optee_agent - OP-TEE transport private data
+ *
+ * @dev: Device used for communication with TEE
+ * @tee_ctx: TEE context used for communication
+ * @caps: Supported channel capabilities
+ * @channel_list: List of all created channels for the agent
+ */
+struct scmi_optee_agent {
+ struct device *dev;
+ struct tee_context *tee_ctx;
+ u32 caps;
+ struct list_head channel_list;
+};
+
+/* There can be only 1 SCMI service in OP-TEE we connect to */
+static struct scmi_optee_agent *scmi_optee_private;
+
+/* Forward reference to scmi_optee transport initialization */
+static int scmi_optee_init(void);
+
+/* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */
+static int open_session(struct scmi_optee_agent *agent, u32 *tee_session)
+{
+ struct device *dev = agent->dev;
+ struct tee_client_device *scmi_pta = to_tee_client_device(dev);
+ struct tee_ioctl_open_session_arg arg = { };
+ int ret;
+
+ memcpy(arg.uuid, scmi_pta->id.uuid.b, TEE_IOCTL_UUID_LEN);
+ arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
+
+ ret = tee_client_open_session(agent->tee_ctx, &arg, NULL);
+ if (ret < 0 || arg.ret) {
+ dev_err(dev, "Can't open tee session: %d / %#x\n", ret, arg.ret);
+ return -EOPNOTSUPP;
+ }
+
+ *tee_session = arg.session;
+
+ return 0;
+}
+
+static void close_session(struct scmi_optee_agent *agent, u32 tee_session)
+{
+ tee_client_close_session(agent->tee_ctx, tee_session);
+}
+
+static int get_capabilities(struct scmi_optee_agent *agent)
+{
+ struct tee_ioctl_invoke_arg arg = { };
+ struct tee_param param[1] = { };
+ u32 caps;
+ u32 tee_session;
+ int ret;
+
+ ret = open_session(agent, &tee_session);
+ if (ret)
+ return ret;
+
+ arg.func = PTA_SCMI_CMD_CAPABILITIES;
+ arg.session = tee_session;
+ arg.num_params = 1;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+
+ ret = tee_client_invoke_func(agent->tee_ctx, &arg, param);
+
+ close_session(agent, tee_session);
+
+ if (ret < 0 || arg.ret) {
+ dev_err(agent->dev, "Can't get capabilities: %d / %#x\n", ret, arg.ret);
+ return -EOPNOTSUPP;
+ }
+
+ caps = param[0].u.value.a;
+
+ if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) {
+ dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n");
+ return -EOPNOTSUPP;
+ }
+
+ agent->caps = caps;
+
+ return 0;
+}
+
+static int get_channel(struct scmi_optee_channel *channel)
+{
+ struct device *dev = scmi_optee_private->dev;
+ struct tee_ioctl_invoke_arg arg = { };
+ struct tee_param param[1] = { };
+ unsigned int caps = 0;
+ int ret;
+
+ if (channel->tee_shm)
+ caps = PTA_SCMI_CAPS_MSG_HEADER;
+ else
+ caps = PTA_SCMI_CAPS_SMT_HEADER;
+
+ arg.func = PTA_SCMI_CMD_GET_CHANNEL;
+ arg.session = channel->tee_session;
+ arg.num_params = 1;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
+ param[0].u.value.a = channel->channel_id;
+ param[0].u.value.b = caps;
+
+ ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
+
+ if (ret || arg.ret) {
+ dev_err(dev, "Can't get channel with caps %#x: %d / %#x\n", caps, ret, arg.ret);
+ return -EOPNOTSUPP;
+ }
+
+ /* From now on use channel identifer provided by OP-TEE SCMI service */
+ channel->channel_id = param[0].u.value.a;
+ channel->caps = caps;
+
+ return 0;
+}
+
+static int invoke_process_smt_channel(struct scmi_optee_channel *channel)
+{
+ struct tee_ioctl_invoke_arg arg = {
+ .func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL,
+ .session = channel->tee_session,
+ .num_params = 1,
+ };
+ struct tee_param param[1] = { };
+ int ret;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+ param[0].u.value.a = channel->channel_id;
+
+ ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret) {
+ dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",
+ channel->channel_id, ret, arg.ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size)
+{
+ struct tee_ioctl_invoke_arg arg = {
+ .func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL,
+ .session = channel->tee_session,
+ .num_params = 3,
+ };
+ struct tee_param param[3] = { };
+ int ret;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+ param[0].u.value.a = channel->channel_id;
+
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ param[1].u.memref.shm = channel->tee_shm;
+ param[1].u.memref.size = msg_size;
+
+ param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param[2].u.memref.shm = channel->tee_shm;
+ param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE;
+
+ ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret) {
+ dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",
+ channel->channel_id, ret, arg.ret);
+ return -EIO;
+ }
+
+ /* Save response size */
+ channel->rx_len = param[2].u.memref.size;
+
+ return 0;
+}
+
+static int scmi_optee_link_supplier(struct device *dev)
+{
+ if (!scmi_optee_private) {
+ scmi_optee_init();
+ of_devices_ensure_probed_by_compatible("linaro,optee-tz");
+ }
+
+ return scmi_optee_private ? 0 : -EPROBE_DEFER;
+}
+
+static bool scmi_optee_chan_available(struct device_node *of_node, int idx)
+{
+ u32 channel_id;
+
+ return !of_property_read_u32_index(of_node, "linaro,optee-channel-id",
+ idx, &channel_id);
+}
+
+static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo)
+{
+ struct scmi_optee_channel *channel = cinfo->transport_info;
+
+ if (!channel->tee_shm)
+ shmem_clear_channel(channel->req.shmem);
+}
+
+static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel)
+{
+ const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE;
+ void *shbuf;
+
+ channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size);
+ if (IS_ERR(channel->tee_shm)) {
+ dev_err(channel->cinfo->dev, "shmem allocation failed\n");
+ return -ENOMEM;
+ }
+
+ shbuf = tee_shm_get_va(channel->tee_shm, 0);
+ memset(shbuf, 0, msg_size);
+ channel->req.msg = shbuf;
+ channel->rx_len = msg_size;
+
+ return 0;
+}
+
+static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,
+ struct scmi_optee_channel *channel)
+{
+ struct device_node *np;
+ resource_size_t size;
+ struct resource res;
+ int ret;
+
+ np = of_parse_phandle(cinfo->dev->of_node, "shmem", 0);
+ if (!of_device_is_compatible(np, "arm,scmi-shmem")) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret) {
+ dev_err(dev, "Failed to get SCMI Tx shared memory\n");
+ goto out;
+ }
+
+ size = resource_size(&res);
+
+ channel->req.shmem = devm_ioremap(dev, res.start, size);
+ if (!channel->req.shmem) {
+ dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n");
+ ret = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ of_node_put(np);
+
+ return ret;
+}
+
+static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo,
+ struct scmi_optee_channel *channel)
+{
+ if (of_property_present(cinfo->dev->of_node, "shmem"))
+ return setup_static_shmem(dev, cinfo, channel);
+ else
+ return setup_dynamic_shmem(dev, channel);
+}
+
+static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx)
+{
+ struct scmi_optee_channel *channel;
+ uint32_t channel_id;
+ int ret;
+
+ if (!tx)
+ return -ENODEV;
+
+ channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL);
+ if (!channel)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_index(cinfo->dev->of_node, "linaro,optee-channel-id",
+ 0, &channel_id);
+ if (ret)
+ return ret;
+
+ cinfo->transport_info = channel;
+ channel->cinfo = cinfo;
+ channel->channel_id = channel_id;
+
+ ret = setup_shmem(dev, cinfo, channel);
+ if (ret)
+ return ret;
+
+ ret = open_session(scmi_optee_private, &channel->tee_session);
+ if (ret)
+ goto err_free_shm;
+
+ ret = get_channel(channel);
+ if (ret)
+ goto err_close_sess;
+
+ list_add(&channel->link, &scmi_optee_private->channel_list);
+
+ return 0;
+
+err_close_sess:
+ close_session(scmi_optee_private, channel->tee_session);
+err_free_shm:
+ if (channel->tee_shm)
+ tee_shm_free(channel->tee_shm);
+
+ return ret;
+}
+
+static int scmi_optee_chan_free(int id, void *p, void *data)
+{
+ struct scmi_chan_info *cinfo = p;
+ struct scmi_optee_channel *channel = cinfo->transport_info;
+
+ /*
+ * chan_setup and chan_free can be unbalanced if a single OP-TEE
+ * channel is used. Catch this and early exit
+ */
+ if (!channel)
+ return 0;
+
+ list_del(&channel->link);
+
+ close_session(scmi_optee_private, channel->tee_session);
+
+ if (channel->tee_shm) {
+ tee_shm_free(channel->tee_shm);
+ channel->tee_shm = NULL;
+ }
+
+ cinfo->transport_info = NULL;
+ channel->cinfo = NULL;
+
+ return 0;
+}
+
+static int scmi_optee_send_message(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer)
+{
+ struct scmi_optee_channel *channel = cinfo->transport_info;
+ int ret;
+
+ if (channel->tee_shm) {
+ msg_tx_prepare(channel->req.msg, xfer);
+ ret = invoke_process_msg_channel(channel, msg_command_size(xfer));
+ } else {
+ shmem_tx_prepare(channel->req.shmem, xfer, cinfo);
+ ret = invoke_process_smt_channel(channel);
+ }
+
+ return ret;
+}
+
+static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer)
+{
+ struct scmi_optee_channel *channel = cinfo->transport_info;
+
+ if (channel->tee_shm)
+ msg_fetch_response(channel->req.msg, channel->rx_len, xfer);
+ else
+ shmem_fetch_response(channel->req.shmem, xfer);
+}
+
+static struct scmi_transport_ops scmi_optee_ops = {
+ .link_supplier = scmi_optee_link_supplier,
+ .chan_available = scmi_optee_chan_available,
+ .chan_setup = scmi_optee_chan_setup,
+ .chan_free = scmi_optee_chan_free,
+ .send_message = scmi_optee_send_message,
+ .fetch_response = scmi_optee_fetch_response,
+ .clear_channel = scmi_optee_clear_channel,
+};
+
+static int scmi_optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ return ver->impl_id == TEE_IMPL_ID_OPTEE;
+}
+
+static int scmi_optee_service_probe(struct device *dev)
+{
+ struct scmi_optee_agent *agent;
+ struct tee_context *tee_ctx;
+ int ret;
+
+ /* Only one SCMI OP-TEE device allowed */
+ if (scmi_optee_private) {
+ dev_err(dev, "An SCMI OP-TEE device was already initialized: only one allowed\n");
+ return -EBUSY;
+ }
+
+ tee_ctx = tee_client_open_context(NULL, scmi_optee_ctx_match, NULL, NULL);
+ if (IS_ERR(tee_ctx))
+ return -ENODEV;
+
+ agent = devm_kzalloc(dev, sizeof(*agent), GFP_KERNEL);
+ if (!agent) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ agent->dev = dev;
+ agent->tee_ctx = tee_ctx;
+ INIT_LIST_HEAD(&agent->channel_list);
+
+ ret = get_capabilities(agent);
+ if (ret)
+ goto err;
+
+ scmi_optee_private = agent;
+
+ return 0;
+
+err:
+ tee_client_close_context(tee_ctx);
+
+ return ret;
+}
+
+/*
+ * Deep probe puts us in an unfortunate position here:
+ * Currently, devices are removed in the inverse order their probe was
+ * entered. If SCMI driver core probes first and then probes OP-TEE,
+ * the OP-TEE dependency will be removed before SCMI.
+ *
+ * If we change the ordering to sort by probe exit, we trade one
+ * breakage for another: OP-TEE will be freed after SCMI, but OP-TEE
+ * probe registers devices for the OP-TEE TAs, which will probe
+ * immediately and thus be removed _after_ their OP-TEE parent.
+ *
+ * TODO: One way to workaround this is to disallow recursive probe, except
+ * for deep probe. For this particular driver, we are in luck as nearly
+ * all components just free memory and we can ignore that as Linux will
+ * reclaim all memory anyway. The only component that needs actual shutdown
+ * is the OP-TEE context used to do SCMI communication, so we just move
+ * that into a later exitcall.
+ */
+static void scmi_optee_service_remove(void)
+{
+ if (!scmi_optee_private)
+ return;
+
+ /* This will also iterate over all sessions and close them */
+ tee_client_close_context(scmi_optee_private->tee_ctx);
+}
+postdevshutdown_exitcall(scmi_optee_service_remove);
+
+static const struct tee_client_device_id scmi_optee_service_id[] = {
+ {
+ UUID_INIT(0xa8cfe406, 0xd4f5, 0x4a2e,
+ 0x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99)
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(tee, scmi_optee_service_id);
+
+static struct tee_client_driver scmi_optee_driver = {
+ .id_table = scmi_optee_service_id,
+ .driver = {
+ .name = "scmi-optee",
+ .bus = &tee_bus_type,
+ .probe = scmi_optee_service_probe,
+ },
+};
+
+static int scmi_optee_init(void)
+{
+ return driver_register(&scmi_optee_driver.driver);
+}
+
+const struct scmi_desc scmi_optee_desc = {
+ .ops = &scmi_optee_ops,
+ .max_rx_timeout_ms = 30,
+ .max_msg = 20,
+ .max_msg_size = SCMI_OPTEE_MAX_MSG_SIZE,
+ .sync_cmds_completed_on_ret = true,
+};
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
new file mode 100644
index 0000000000..5c4fc73335
--- /dev/null
+++ b/drivers/firmware/arm_scmi/power.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Power Protocol
+ *
+ * Copyright (C) 2018-2022 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
+
+#include <module.h>
+#include <linux/scmi_protocol.h>
+
+#include "protocols.h"
+
+enum scmi_power_protocol_cmd {
+ POWER_DOMAIN_ATTRIBUTES = 0x3,
+ POWER_STATE_SET = 0x4,
+ POWER_STATE_GET = 0x5,
+ POWER_DOMAIN_NAME_GET = 0x8,
+};
+
+struct scmi_msg_resp_power_attributes {
+ __le16 num_domains;
+ __le16 reserved;
+ __le32 stats_addr_low;
+ __le32 stats_addr_high;
+ __le32 stats_size;
+};
+
+struct scmi_msg_resp_power_domain_attributes {
+ __le32 flags;
+#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(27))
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+};
+
+struct scmi_power_set_state {
+ __le32 flags;
+ __le32 domain;
+ __le32 state;
+};
+
+struct power_dom_info {
+ char name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_power_info {
+ u32 version;
+ int num_domains;
+ u64 stats_addr;
+ u32 stats_size;
+ struct power_dom_info *dom_info;
+};
+
+static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
+ struct scmi_power_info *pi)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_power_attributes *attr;
+
+ ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+ 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ pi->num_domains = le16_to_cpu(attr->num_domains);
+ pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+ (u64)le32_to_cpu(attr->stats_addr_high) << 32;
+ pi->stats_size = le32_to_cpu(attr->stats_size);
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int
+scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
+ u32 domain, struct power_dom_info *dom_info,
+ u32 version)
+{
+ int ret;
+ u32 flags;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_power_domain_attributes *attr;
+
+ ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES,
+ sizeof(domain), sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(domain, t->tx.buf);
+ attr = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ flags = le32_to_cpu(attr->flags);
+
+ strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
+ }
+ ph->xops->xfer_put(ph, t);
+
+ /*
+ * If supported overwrite short name with the extended one;
+ * on error just carry on and use already provided short name.
+ */
+ if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ SUPPORTS_EXTENDED_NAMES(flags)) {
+ ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
+ domain, dom_info->name,
+ SCMI_MAX_STR_SIZE);
+ }
+
+ return ret;
+}
+
+static int scmi_power_state_set(const struct scmi_protocol_handle *ph,
+ u32 domain, u32 state)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_power_set_state *st;
+
+ ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t);
+ if (ret)
+ return ret;
+
+ st = t->tx.buf;
+ st->flags = cpu_to_le32(0);
+ st->domain = cpu_to_le32(domain);
+ st->state = cpu_to_le32(state);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_power_state_get(const struct scmi_protocol_handle *ph,
+ u32 domain, u32 *state)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(domain, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *state = get_unaligned_le32(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
+{
+ struct scmi_power_info *pi = ph->get_priv(ph);
+
+ return pi->num_domains;
+}
+
+static const char *
+scmi_power_name_get(const struct scmi_protocol_handle *ph,
+ u32 domain)
+{
+ struct scmi_power_info *pi = ph->get_priv(ph);
+ struct power_dom_info *dom = pi->dom_info + domain;
+
+ return dom->name;
+}
+
+static const struct scmi_power_proto_ops power_proto_ops = {
+ .num_domains_get = scmi_power_num_domains_get,
+ .name_get = scmi_power_name_get,
+ .state_set = scmi_power_state_set,
+ .state_get = scmi_power_state_get,
+};
+
+static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
+{
+ int domain, ret;
+ u32 version;
+ struct scmi_power_info *pinfo;
+
+ ret = ph->xops->version_get(ph, &version);
+ if (ret)
+ return ret;
+
+ dev_dbg(ph->dev, "Power Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ ret = scmi_power_attributes_get(ph, pinfo);
+ if (ret)
+ return ret;
+
+ pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
+ sizeof(*pinfo->dom_info), GFP_KERNEL);
+ if (!pinfo->dom_info)
+ return -ENOMEM;
+
+ for (domain = 0; domain < pinfo->num_domains; domain++) {
+ struct power_dom_info *dom = pinfo->dom_info + domain;
+
+ scmi_power_domain_attributes_get(ph, domain, dom, version);
+ }
+
+ pinfo->version = version;
+
+ return ph->set_priv(ph, pinfo);
+}
+
+static const struct scmi_protocol scmi_power = {
+ .id = SCMI_PROTOCOL_POWER,
+ .instance_init = &scmi_power_protocol_init,
+ .ops = &power_proto_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER(power, scmi_power)
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
new file mode 100644
index 0000000000..cb0c2eb841
--- /dev/null
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * System Control and Management Interface (SCMI) Message Protocol
+ * protocols common header file containing some definitions, structures
+ * and function prototypes used in all the different SCMI protocols.
+ *
+ * Copyright (C) 2022 ARM Ltd.
+ */
+#ifndef _SCMI_PROTOCOLS_H
+#define _SCMI_PROTOCOLS_H
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <module.h>
+#include <linux/refcount.h>
+#include <linux/scmi_protocol.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/unaligned.h>
+
+#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
+#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
+#define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))))
+#define PROTOCOL_REV_MINOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))))
+
+enum scmi_common_cmd {
+ PROTOCOL_VERSION = 0x0,
+ PROTOCOL_ATTRIBUTES = 0x1,
+ PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+};
+
+/**
+ * struct scmi_msg_resp_prot_version - Response for a message
+ *
+ * @minor_version: Minor version of the ABI that firmware supports
+ * @major_version: Major version of the ABI that firmware supports
+ *
+ * In general, ABI version changes follow the rule that minor version increments
+ * are backward compatible. Major revision changes in ABI may not be
+ * backward compatible.
+ *
+ * Response to a generic message with message type SCMI_MSG_VERSION
+ */
+struct scmi_msg_resp_prot_version {
+ __le16 minor_version;
+ __le16 major_version;
+};
+
+/**
+ * struct scmi_msg - Message(Tx/Rx) structure
+ *
+ * @buf: Buffer pointer
+ * @len: Length of data in the Buffer
+ */
+struct scmi_msg {
+ void *buf;
+ size_t len;
+};
+
+/**
+ * struct scmi_msg_hdr - Message(Tx/Rx) header
+ *
+ * @id: The identifier of the message being sent
+ * @protocol_id: The identifier of the protocol used to send @id message
+ * @type: The SCMI type for this message
+ * @seq: The token to identify the message. When a message returns, the
+ * platform returns the whole message header unmodified including the
+ * token
+ * @status: Status of the transfer once it's complete
+ */
+struct scmi_msg_hdr {
+ u8 id;
+ u8 protocol_id;
+ u8 type;
+ u16 seq;
+ u32 status;
+};
+
+/**
+ * struct scmi_xfer - Structure representing a message flow
+ *
+ * @transfer_id: Unique ID for debug & profiling purpose
+ * @hdr: Transmit message header
+ * @tx: Transmit message
+ * @rx: Receive message, the buffer should be pre-allocated to store
+ * message. If request-ACK protocol is used, we can reuse the same
+ * buffer for the rx path as we use for the tx path.
+ * @pending: True for xfers removed from the free list @free_xfers
+ * @node: An hlist_node reference used to store this xfer on
+ * the free list @free_xfers
+ * @users: A refcount to track the active users for this xfer.
+ * This is meant to protect against the possibility that, when a command
+ * transaction times out concurrently with the reception of a valid
+ * response message, the xfer could be finally put on the TX path, and
+ * so vanish, while on the RX path scmi_rx_callback() is still
+ * processing it: in such a case this refcounting will ensure that, even
+ * though the timed-out transaction will anyway cause the command
+ * request to be reported as failed by time-out, the underlying xfer
+ * cannot be discarded and possibly reused until the last one user on
+ * the RX path has released it.
+ * @busy: An atomic flag to ensure exclusive write access to this xfer
+ * @state: The current state of this transfer, with states transitions deemed
+ * valid being:
+ * - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
+ * - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
+ * (Missing synchronous response is assumed OK and ignored)
+ * @flags: Optional flags associated to this xfer.
+ * @lock: A spinlock to protect state and busy fields.
+ * @priv: A pointer for transport private usage.
+ */
+struct scmi_xfer {
+ int transfer_id;
+ struct scmi_msg_hdr hdr;
+ struct scmi_msg tx;
+ struct scmi_msg rx;
+ bool pending;
+ struct hlist_node node;
+ refcount_t users;
+#define SCMI_XFER_FREE 0
+#define SCMI_XFER_BUSY 1
+ atomic_t busy;
+#define SCMI_XFER_SENT_OK 0
+#define SCMI_XFER_RESP_OK 1
+#define SCMI_XFER_DRESP_OK 2
+ int state;
+#define SCMI_XFER_FLAG_IS_RAW BIT(0)
+#define SCMI_XFER_IS_RAW(x) ((x)->flags & SCMI_XFER_FLAG_IS_RAW)
+#define SCMI_XFER_FLAG_CHAN_SET BIT(1)
+#define SCMI_XFER_IS_CHAN_SET(x) \
+ ((x)->flags & SCMI_XFER_FLAG_CHAN_SET)
+ int flags;
+ /* A lock to protect state and busy fields */
+ spinlock_t lock;
+ void *priv;
+};
+
+struct scmi_xfer_ops;
+struct scmi_proto_helpers_ops;
+
+/**
+ * struct scmi_protocol_handle - Reference to an initialized protocol instance
+ *
+ * @dev: A reference to the associated SCMI instance device (handle->dev).
+ * @xops: A reference to a struct holding refs to the core xfer operations that
+ * can be used by the protocol implementation to generate SCMI messages.
+ * @set_priv: A method to set protocol private data for this instance.
+ * @get_priv: A method to get protocol private data previously set.
+ *
+ * This structure represents a protocol initialized against specific SCMI
+ * instance and it will be used as follows:
+ * - as a parameter fed from the core to the protocol initialization code so
+ * that it can access the core xfer operations to build and generate SCMI
+ * messages exclusively for the specific underlying protocol instance.
+ * - as an opaque handle fed by an SCMI driver user when it tries to access
+ * this protocol through its own protocol operations.
+ * In this case this handle will be returned as an opaque object together
+ * with the related protocol operations when the SCMI driver tries to access
+ * the protocol.
+ */
+struct scmi_protocol_handle {
+ struct device *dev;
+ const struct scmi_xfer_ops *xops;
+ const struct scmi_proto_helpers_ops *hops;
+ int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
+ void *(*get_priv)(const struct scmi_protocol_handle *ph);
+};
+
+/**
+ * struct scmi_iterator_state - Iterator current state descriptor
+ * @desc_index: Starting index for the current mulit-part request.
+ * @num_returned: Number of returned items in the last multi-part reply.
+ * @num_remaining: Number of remaining items in the multi-part message.
+ * @max_resources: Maximum acceptable number of items, configured by the caller
+ * depending on the underlying resources that it is querying.
+ * @loop_idx: The iterator loop index in the current multi-part reply.
+ * @rx_len: Size in bytes of the currenly processed message; it can be used by
+ * the user of the iterator to verify a reply size.
+ * @priv: Optional pointer to some additional state-related private data setup
+ * by the caller during the iterations.
+ */
+struct scmi_iterator_state {
+ unsigned int desc_index;
+ unsigned int num_returned;
+ unsigned int num_remaining;
+ unsigned int max_resources;
+ unsigned int loop_idx;
+ size_t rx_len;
+ void *priv;
+};
+
+/**
+ * struct scmi_iterator_ops - Custom iterator operations
+ * @prepare_message: An operation to provide the custom logic to fill in the
+ * SCMI command request pointed by @message. @desc_index is
+ * a reference to the next index to use in the multi-part
+ * request.
+ * @update_state: An operation to provide the custom logic to update the
+ * iterator state from the actual message response.
+ * @process_response: An operation to provide the custom logic needed to process
+ * each chunk of the multi-part message.
+ */
+struct scmi_iterator_ops {
+ void (*prepare_message)(void *message, unsigned int desc_index,
+ const void *priv);
+ int (*update_state)(struct scmi_iterator_state *st,
+ const void *response, void *priv);
+ int (*process_response)(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv);
+};
+
+struct scmi_fc_db_info {
+ int width;
+ u64 set;
+ u64 mask;
+ void __iomem *addr;
+};
+
+struct scmi_fc_info {
+ void __iomem *set_addr;
+ void __iomem *get_addr;
+ struct scmi_fc_db_info *set_db;
+};
+
+/**
+ * struct scmi_proto_helpers_ops - References to common protocol helpers
+ * @extended_name_get: A common helper function to retrieve extended naming
+ * for the specified resource using the specified command.
+ * Result is returned as a NULL terminated string in the
+ * pre-allocated area pointed to by @name with maximum
+ * capacity of @len bytes.
+ * @iter_response_init: A common helper to initialize a generic iterator to
+ * parse multi-message responses: when run the iterator
+ * will take care to send the initial command request as
+ * specified by @msg_id and @tx_size and then to parse the
+ * multi-part responses using the custom operations
+ * provided in @ops.
+ * @iter_response_run: A common helper to trigger the run of a previously
+ * initialized iterator.
+ * @fastchannel_init: A common helper used to initialize FC descriptors by
+ * gathering FC descriptions from the SCMI platform server.
+ * @fastchannel_db_ring: A common helper to ring a FC doorbell.
+ */
+struct scmi_proto_helpers_ops {
+ int (*extended_name_get)(const struct scmi_protocol_handle *ph,
+ u8 cmd_id, u32 res_id, char *name, size_t len);
+ void *(*iter_response_init)(const struct scmi_protocol_handle *ph,
+ struct scmi_iterator_ops *ops,
+ unsigned int max_resources, u8 msg_id,
+ size_t tx_size, void *priv);
+ int (*iter_response_run)(void *iter);
+ void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
+ u8 describe_id, u32 message_id,
+ u32 valid_size, u32 domain,
+ void __iomem **p_addr,
+ struct scmi_fc_db_info **p_db);
+ void (*fastchannel_db_ring)(struct scmi_fc_db_info *db);
+};
+
+/**
+ * struct scmi_xfer_ops - References to the core SCMI xfer operations.
+ * @version_get: Get this version protocol.
+ * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
+ * @reset_rx_to_maxsz: Reset rx size to max transport size.
+ * @do_xfer: Do the SCMI transfer.
+ * @xfer_put: Free the xfer slot.
+ *
+ * Note that all this operations expect a protocol handle as first parameter;
+ * they then internally use it to infer the underlying protocol number: this
+ * way is not possible for a protocol implementation to forge messages for
+ * another protocol.
+ */
+struct scmi_xfer_ops {
+ int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
+ int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
+ size_t tx_size, size_t rx_size,
+ struct scmi_xfer **p);
+ void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+ int (*do_xfer)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+ void (*xfer_put)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+};
+
+typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
+
+/**
+ * struct scmi_protocol - Protocol descriptor
+ * @id: Protocol ID.
+ * @instance_init: Mandatory protocol initialization function.
+ * @instance_deinit: Optional protocol de-initialization function.
+ * @ops: Optional reference to the operations provided by the protocol and
+ * exposed in scmi_protocol.h.
+ */
+struct scmi_protocol {
+ const u8 id;
+ const scmi_prot_init_ph_fn_t instance_init;
+ const scmi_prot_init_ph_fn_t instance_deinit;
+ const void *ops;
+};
+
+#define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \
+static const struct scmi_protocol *__this_proto = &(proto); \
+ \
+int __init scmi_##name##_register(void) \
+{ \
+ return scmi_protocol_register(__this_proto); \
+} \
+
+#define DECLARE_SCMI_REGISTER(func) \
+ int __init scmi_##func##_register(void);
+DECLARE_SCMI_REGISTER(base);
+DECLARE_SCMI_REGISTER(clock);
+DECLARE_SCMI_REGISTER(power);
+DECLARE_SCMI_REGISTER(reset);
+DECLARE_SCMI_REGISTER(sensors);
+DECLARE_SCMI_REGISTER(voltage);
+
+#endif /* _SCMI_PROTOCOLS_H */
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index 94baab99e1..98a7dd0afa 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -2,30 +2,29 @@
/*
* System Control and Management Interface (SCMI) Reset Protocol
*
- * Copyright (C) 2019-2021 ARM Ltd.
+ * Copyright (C) 2019-2022 ARM Ltd.
*/
-#define pr_fmt(fmt) "SCMI RESET - " fmt
+#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
#include <common.h>
#include <linux/scmi_protocol.h>
-#include "common.h"
+#include "protocols.h"
enum scmi_reset_protocol_cmd {
RESET_DOMAIN_ATTRIBUTES = 0x3,
RESET = 0x4,
- RESET_NOTIFY = 0x5,
+ RESET_DOMAIN_NAME_GET = 0x6,
};
#define NUM_RESET_DOMAIN_MASK 0xffff
struct scmi_msg_resp_reset_domain_attributes {
__le32 attributes;
-#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
-#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
+#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
__le32 latency;
- u8 name[SCMI_MAX_STR_SIZE];
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
};
struct scmi_msg_reset_domain_reset {
@@ -33,13 +32,11 @@ struct scmi_msg_reset_domain_reset {
__le32 flags;
#define AUTONOMOUS_RESET BIT(0)
#define EXPLICIT_RESET_ASSERT BIT(1)
-#define ASYNCHRONOUS_RESET BIT(2)
__le32 reset_state;
#define ARCH_COLD_RESET 0
};
struct reset_dom_info {
- bool async_reset;
u32 latency_us;
char name[SCMI_MAX_STR_SIZE];
};
@@ -74,9 +71,11 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
static int
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
- u32 domain, struct reset_dom_info *dom_info)
+ u32 domain, struct reset_dom_info *dom_info,
+ u32 version)
{
int ret;
+ u32 attributes;
struct scmi_xfer *t;
struct scmi_msg_resp_reset_domain_attributes *attr;
@@ -90,16 +89,25 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
- u32 attributes = le32_to_cpu(attr->attributes);
+ attributes = le32_to_cpu(attr->attributes);
- dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
dom_info->latency_us = le32_to_cpu(attr->latency);
if (dom_info->latency_us == U32_MAX)
dom_info->latency_us = 0;
- strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+ strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
}
ph->xops->xfer_put(ph, t);
+
+ /*
+ * If supported overwrite short name with the extended one;
+ * on error just carry on and use already provided short name.
+ */
+ if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ SUPPORTS_EXTENDED_NAMES(attributes))
+ ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
+ dom_info->name, SCMI_MAX_STR_SIZE);
+
return ret;
}
@@ -110,8 +118,8 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
return pi->num_domains;
}
-static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph,
- u32 domain)
+static const char *
+scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)
{
struct scmi_reset_info *pi = ph->get_priv(ph);
@@ -136,10 +144,12 @@ static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
struct scmi_xfer *t;
struct scmi_msg_reset_domain_reset *dom;
struct scmi_reset_info *pi = ph->get_priv(ph);
- struct reset_dom_info *rdom = pi->dom_info + domain;
+ struct reset_dom_info *rdom;
- if (rdom->async_reset)
- flags |= ASYNCHRONOUS_RESET;
+ if (domain >= pi->num_domains)
+ return -EINVAL;
+
+ rdom = pi->dom_info + domain;
ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);
if (ret)
@@ -150,10 +160,7 @@ static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
dom->flags = cpu_to_le32(flags);
dom->reset_state = cpu_to_le32(state);
- if (rdom->async_reset)
- ret = ph->xops->do_xfer_with_response(ph, t);
- else
- ret = ph->xops->do_xfer(ph, t);
+ ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
return ret;
@@ -190,22 +197,26 @@ static const struct scmi_reset_proto_ops reset_proto_ops = {
static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
{
- int domain;
+ int domain, ret;
u32 version;
struct scmi_reset_info *pinfo;
- ph->xops->version_get(ph, &version);
+ ret = ph->xops->version_get(ph, &version);
+ if (ret)
+ return ret;
dev_dbg(ph->dev, "Reset Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
- pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+ pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
- scmi_reset_attributes_get(ph, pinfo);
+ ret = scmi_reset_attributes_get(ph, pinfo);
+ if (ret)
+ return ret;
- pinfo->dom_info = kcalloc(pinfo->num_domains,
+ pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
sizeof(*pinfo->dom_info), GFP_KERNEL);
if (!pinfo->dom_info)
return -ENOMEM;
@@ -213,7 +224,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
for (domain = 0; domain < pinfo->num_domains; domain++) {
struct reset_dom_info *dom = pinfo->dom_info + domain;
- scmi_reset_domain_attributes_get(ph, domain, dom);
+ scmi_reset_domain_attributes_get(ph, domain, dom, version);
}
pinfo->version = version;
diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
new file mode 100644
index 0000000000..4448598fd3
--- /dev/null
+++ b/drivers/firmware/arm_scmi/scmi_pm_domain.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SCMI Generic power domain support.
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <module.h>
+#include <pm_domain.h>
+#include <linux/scmi_protocol.h>
+
+static const struct scmi_power_proto_ops *power_ops;
+
+struct scmi_pm_domain {
+ struct generic_pm_domain genpd;
+ const struct scmi_protocol_handle *ph;
+ const char *name;
+ u32 domain;
+};
+
+#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
+
+static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+ int ret;
+ u32 state, ret_state;
+ struct scmi_pm_domain *pd = to_scmi_pd(domain);
+
+ if (power_on)
+ state = SCMI_POWER_STATE_GENERIC_ON;
+ else
+ state = SCMI_POWER_STATE_GENERIC_OFF;
+
+ ret = power_ops->state_set(pd->ph, pd->domain, state);
+ if (!ret)
+ ret = power_ops->state_get(pd->ph, pd->domain, &ret_state);
+ if (!ret && state != ret_state)
+ return -EIO;
+
+ return ret;
+}
+
+static int scmi_pd_power_on(struct generic_pm_domain *domain)
+{
+ return scmi_pd_power(domain, true);
+}
+
+static int scmi_pd_power_off(struct generic_pm_domain *domain)
+{
+ return scmi_pd_power(domain, false);
+}
+
+static int scmi_pm_domain_probe(struct scmi_device *sdev)
+{
+ int num_domains, i;
+ struct device *dev = &sdev->dev;
+ struct device_node *np = dev->of_node;
+ struct scmi_pm_domain *scmi_pd;
+ struct genpd_onecell_data *scmi_pd_data;
+ struct generic_pm_domain **domains;
+ const struct scmi_handle *handle = sdev->handle;
+ struct scmi_protocol_handle *ph;
+
+ if (!handle)
+ return -ENODEV;
+
+ power_ops = handle->dev_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph);
+ if (IS_ERR(power_ops))
+ return PTR_ERR(power_ops);
+
+ num_domains = power_ops->num_domains_get(ph);
+ if (num_domains < 0) {
+ dev_err(dev, "number of domains not found\n");
+ return num_domains;
+ }
+
+ scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
+ if (!scmi_pd)
+ return -ENOMEM;
+
+ scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
+ if (!scmi_pd_data)
+ return -ENOMEM;
+
+ domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ for (i = 0; i < num_domains; i++, scmi_pd++) {
+ u32 state;
+
+ if (power_ops->state_get(ph, i, &state)) {
+ dev_warn(dev, "failed to get state for domain %d\n", i);
+ continue;
+ }
+
+ scmi_pd->domain = i;
+ scmi_pd->ph = ph;
+ scmi_pd->name = power_ops->name_get(ph, i);
+ scmi_pd->genpd.name = scmi_pd->name;
+ scmi_pd->genpd.power_off = scmi_pd_power_off;
+ scmi_pd->genpd.power_on = scmi_pd_power_on;
+
+ pm_genpd_init(&scmi_pd->genpd, NULL,
+ state == SCMI_POWER_STATE_GENERIC_OFF);
+
+ domains[i] = &scmi_pd->genpd;
+ }
+
+ scmi_pd_data->domains = domains;
+ scmi_pd_data->num_domains = num_domains;
+
+ dev->priv = scmi_pd_data;
+
+ return of_genpd_add_provider_onecell(np, scmi_pd_data);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_POWER, "genpd" },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_power_domain_driver = {
+ .name = "scmi-power-domain",
+ .probe = scmi_pm_domain_probe,
+ .id_table = scmi_id_table,
+};
+core_scmi_driver(scmi_power_domain_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI power domain driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
new file mode 100644
index 0000000000..6e94ef2e6b
--- /dev/null
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -0,0 +1,936 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Sensor Protocol
+ *
+ * Copyright (C) 2018-2022 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
+
+#include <linux/bitfield.h>
+#include <module.h>
+#include <linux/scmi_protocol.h>
+
+#include "protocols.h"
+
+#define SCMI_MAX_NUM_SENSOR_AXIS 63
+#define SCMIv2_SENSOR_PROTOCOL 0x10000
+
+enum scmi_sensor_protocol_cmd {
+ SENSOR_DESCRIPTION_GET = 0x3,
+ SENSOR_TRIP_POINT_NOTIFY = 0x4,
+ SENSOR_TRIP_POINT_CONFIG = 0x5,
+ SENSOR_READING_GET = 0x6,
+ SENSOR_AXIS_DESCRIPTION_GET = 0x7,
+ SENSOR_LIST_UPDATE_INTERVALS = 0x8,
+ SENSOR_CONFIG_GET = 0x9,
+ SENSOR_CONFIG_SET = 0xA,
+ SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xB,
+ SENSOR_NAME_GET = 0xC,
+ SENSOR_AXIS_NAME_GET = 0xD,
+};
+
+struct scmi_msg_resp_sensor_attributes {
+ __le16 num_sensors;
+ u8 max_requests;
+ u8 reserved;
+ __le32 reg_addr_low;
+ __le32 reg_addr_high;
+ __le32 reg_size;
+};
+
+/* v3 attributes_low macros */
+#define SUPPORTS_UPDATE_NOTIFY(x) FIELD_GET(BIT(30), (x))
+#define SENSOR_TSTAMP_EXP(x) FIELD_GET(GENMASK(14, 10), (x))
+#define SUPPORTS_TIMESTAMP(x) FIELD_GET(BIT(9), (x))
+#define SUPPORTS_EXTEND_ATTRS(x) FIELD_GET(BIT(8), (x))
+
+/* v2 attributes_high macros */
+#define SENSOR_UPDATE_BASE(x) FIELD_GET(GENMASK(31, 27), (x))
+#define SENSOR_UPDATE_SCALE(x) FIELD_GET(GENMASK(26, 22), (x))
+
+/* v3 attributes_high macros */
+#define SENSOR_AXIS_NUMBER(x) FIELD_GET(GENMASK(21, 16), (x))
+#define SUPPORTS_AXIS(x) FIELD_GET(BIT(8), (x))
+
+/* v3 resolution macros */
+#define SENSOR_RES(x) FIELD_GET(GENMASK(26, 0), (x))
+#define SENSOR_RES_EXP(x) FIELD_GET(GENMASK(31, 27), (x))
+
+struct scmi_msg_resp_attrs {
+ __le32 min_range_low;
+ __le32 min_range_high;
+ __le32 max_range_low;
+ __le32 max_range_high;
+};
+
+struct scmi_msg_sensor_description {
+ __le32 desc_index;
+};
+
+struct scmi_msg_resp_sensor_description {
+ __le16 num_returned;
+ __le16 num_remaining;
+ struct scmi_sensor_descriptor {
+ __le32 id;
+ __le32 attributes_low;
+/* Common attributes_low macros */
+#define SUPPORTS_ASYNC_READ(x) FIELD_GET(BIT(31), (x))
+#define SUPPORTS_EXTENDED_NAMES(x) FIELD_GET(BIT(29), (x))
+#define NUM_TRIP_POINTS(x) FIELD_GET(GENMASK(7, 0), (x))
+ __le32 attributes_high;
+/* Common attributes_high macros */
+#define SENSOR_SCALE(x) FIELD_GET(GENMASK(15, 11), (x))
+#define SENSOR_SCALE_SIGN BIT(4)
+#define SENSOR_SCALE_EXTEND GENMASK(31, 5)
+#define SENSOR_TYPE(x) FIELD_GET(GENMASK(7, 0), (x))
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+ /* only for version > 2.0 */
+ __le32 power;
+ __le32 resolution;
+ struct scmi_msg_resp_attrs scalar_attrs;
+ } desc[];
+};
+
+/* Base scmi_sensor_descriptor size excluding extended attrs after name */
+#define SCMI_MSG_RESP_SENS_DESCR_BASE_SZ 28
+
+/* Sign extend to a full s32 */
+#define S32_EXT(v) \
+ ({ \
+ int __v = (v); \
+ \
+ if (__v & SENSOR_SCALE_SIGN) \
+ __v |= SENSOR_SCALE_EXTEND; \
+ __v; \
+ })
+
+struct scmi_msg_sensor_axis_description_get {
+ __le32 id;
+ __le32 axis_desc_index;
+};
+
+struct scmi_msg_resp_sensor_axis_description {
+ __le32 num_axis_flags;
+#define NUM_AXIS_RETURNED(x) FIELD_GET(GENMASK(5, 0), (x))
+#define NUM_AXIS_REMAINING(x) FIELD_GET(GENMASK(31, 26), (x))
+ struct scmi_axis_descriptor {
+ __le32 id;
+ __le32 attributes_low;
+#define SUPPORTS_EXTENDED_AXIS_NAMES(x) FIELD_GET(BIT(9), (x))
+ __le32 attributes_high;
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+ __le32 resolution;
+ struct scmi_msg_resp_attrs attrs;
+ } desc[];
+};
+
+struct scmi_msg_resp_sensor_axis_names_description {
+ __le32 num_axis_flags;
+ struct scmi_sensor_axis_name_descriptor {
+ __le32 axis_id;
+ u8 name[SCMI_MAX_STR_SIZE];
+ } desc[];
+};
+
+/* Base scmi_axis_descriptor size excluding extended attrs after name */
+#define SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ 28
+
+struct scmi_msg_sensor_list_update_intervals {
+ __le32 id;
+ __le32 index;
+};
+
+struct scmi_msg_resp_sensor_list_update_intervals {
+ __le32 num_intervals_flags;
+#define NUM_INTERVALS_RETURNED(x) FIELD_GET(GENMASK(11, 0), (x))
+#define SEGMENTED_INTVL_FORMAT(x) FIELD_GET(BIT(12), (x))
+#define NUM_INTERVALS_REMAINING(x) FIELD_GET(GENMASK(31, 16), (x))
+ __le32 intervals[];
+};
+
+struct scmi_msg_set_sensor_trip_point {
+ __le32 id;
+ __le32 event_control;
+#define SENSOR_TP_EVENT_MASK (0x3)
+#define SENSOR_TP_DISABLED 0x0
+#define SENSOR_TP_POSITIVE 0x1
+#define SENSOR_TP_NEGATIVE 0x2
+#define SENSOR_TP_BOTH 0x3
+#define SENSOR_TP_ID(x) (((x) & 0xff) << 4)
+ __le32 value_low;
+ __le32 value_high;
+};
+
+struct scmi_msg_sensor_config_set {
+ __le32 id;
+ __le32 sensor_config;
+};
+
+struct scmi_msg_sensor_reading_get {
+ __le32 id;
+ __le32 flags;
+#define SENSOR_READ_ASYNC BIT(0)
+};
+
+struct scmi_resp_sensor_reading_complete {
+ __le32 id;
+ __le32 readings_low;
+ __le32 readings_high;
+};
+
+struct scmi_sensor_reading_resp {
+ __le32 sensor_value_low;
+ __le32 sensor_value_high;
+ __le32 timestamp_low;
+ __le32 timestamp_high;
+};
+
+struct scmi_resp_sensor_reading_complete_v3 {
+ __le32 id;
+ struct scmi_sensor_reading_resp readings[];
+};
+
+struct sensors_info {
+ u32 version;
+ int num_sensors;
+ int max_requests;
+ u64 reg_addr;
+ u32 reg_size;
+ struct scmi_sensor_info *sensors;
+};
+
+static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
+ struct sensors_info *si)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_sensor_attributes *attr;
+
+ ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+ 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ si->num_sensors = le16_to_cpu(attr->num_sensors);
+ si->max_requests = attr->max_requests;
+ si->reg_addr = le32_to_cpu(attr->reg_addr_low) |
+ (u64)le32_to_cpu(attr->reg_addr_high) << 32;
+ si->reg_size = le32_to_cpu(attr->reg_size);
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static inline void scmi_parse_range_attrs(struct scmi_range_attrs *out,
+ const struct scmi_msg_resp_attrs *in)
+{
+ out->min_range = get_unaligned_le64((void *)&in->min_range_low);
+ out->max_range = get_unaligned_le64((void *)&in->max_range_low);
+}
+
+struct scmi_sens_ipriv {
+ void *priv;
+ struct device *dev;
+};
+
+static void iter_intervals_prepare_message(void *message,
+ unsigned int desc_index,
+ const void *p)
+{
+ struct scmi_msg_sensor_list_update_intervals *msg = message;
+ const struct scmi_sensor_info *s;
+
+ s = ((const struct scmi_sens_ipriv *)p)->priv;
+ /* Set the number of sensors to be skipped/already read */
+ msg->id = cpu_to_le32(s->id);
+ msg->index = cpu_to_le32(desc_index);
+}
+
+static int iter_intervals_update_state(struct scmi_iterator_state *st,
+ const void *response, void *p)
+{
+ u32 flags;
+ struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv;
+ struct device *dev = ((struct scmi_sens_ipriv *)p)->dev;
+ const struct scmi_msg_resp_sensor_list_update_intervals *r = response;
+
+ flags = le32_to_cpu(r->num_intervals_flags);
+ st->num_returned = NUM_INTERVALS_RETURNED(flags);
+ st->num_remaining = NUM_INTERVALS_REMAINING(flags);
+
+ /*
+ * Max intervals is not declared previously anywhere so we
+ * assume it's returned+remaining on first call.
+ */
+ if (!st->max_resources) {
+ s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags);
+ s->intervals.count = st->num_returned + st->num_remaining;
+ /* segmented intervals are reported in one triplet */
+ if (s->intervals.segmented &&
+ (st->num_remaining || st->num_returned != 3)) {
+ dev_err(dev,
+ "Sensor ID:%d advertises an invalid segmented interval (%d)\n",
+ s->id, s->intervals.count);
+ s->intervals.segmented = false;
+ s->intervals.count = 0;
+ return -EINVAL;
+ }
+ /* Direct allocation when exceeding pre-allocated */
+ if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) {
+ s->intervals.desc =
+ devm_kcalloc(dev,
+ s->intervals.count,
+ sizeof(*s->intervals.desc),
+ GFP_KERNEL);
+ if (!s->intervals.desc) {
+ s->intervals.segmented = false;
+ s->intervals.count = 0;
+ return -ENOMEM;
+ }
+ }
+
+ st->max_resources = s->intervals.count;
+ }
+
+ return 0;
+}
+
+static int
+iter_intervals_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *p)
+{
+ const struct scmi_msg_resp_sensor_list_update_intervals *r = response;
+ struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv;
+
+ s->intervals.desc[st->desc_index + st->loop_idx] =
+ le32_to_cpu(r->intervals[st->loop_idx]);
+
+ return 0;
+}
+
+static int scmi_sensor_update_intervals(const struct scmi_protocol_handle *ph,
+ struct scmi_sensor_info *s)
+{
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_intervals_prepare_message,
+ .update_state = iter_intervals_update_state,
+ .process_response = iter_intervals_process_response,
+ };
+ struct scmi_sens_ipriv upriv = {
+ .priv = s,
+ .dev = ph->dev,
+ };
+
+ iter = ph->hops->iter_response_init(ph, &ops, s->intervals.count,
+ SENSOR_LIST_UPDATE_INTERVALS,
+ sizeof(struct scmi_msg_sensor_list_update_intervals),
+ &upriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ return ph->hops->iter_response_run(iter);
+}
+
+struct scmi_apriv {
+ bool any_axes_support_extended_names;
+ struct scmi_sensor_info *s;
+};
+
+static void iter_axes_desc_prepare_message(void *message,
+ const unsigned int desc_index,
+ const void *priv)
+{
+ struct scmi_msg_sensor_axis_description_get *msg = message;
+ const struct scmi_apriv *apriv = priv;
+
+ /* Set the number of sensors to be skipped/already read */
+ msg->id = cpu_to_le32(apriv->s->id);
+ msg->axis_desc_index = cpu_to_le32(desc_index);
+}
+
+static int
+iter_axes_desc_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ u32 flags;
+ const struct scmi_msg_resp_sensor_axis_description *r = response;
+
+ flags = le32_to_cpu(r->num_axis_flags);
+ st->num_returned = NUM_AXIS_RETURNED(flags);
+ st->num_remaining = NUM_AXIS_REMAINING(flags);
+ st->priv = (void *)&r->desc[0];
+
+ return 0;
+}
+
+static int
+iter_axes_desc_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
+{
+ u32 attrh, attrl;
+ struct scmi_sensor_axis_info *a;
+ size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ;
+ struct scmi_apriv *apriv = priv;
+ const struct scmi_axis_descriptor *adesc = st->priv;
+
+ attrl = le32_to_cpu(adesc->attributes_low);
+ if (SUPPORTS_EXTENDED_AXIS_NAMES(attrl))
+ apriv->any_axes_support_extended_names = true;
+
+ a = &apriv->s->axis[st->desc_index + st->loop_idx];
+ a->id = le32_to_cpu(adesc->id);
+ a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
+
+ attrh = le32_to_cpu(adesc->attributes_high);
+ a->scale = S32_EXT(SENSOR_SCALE(attrh));
+ a->type = SENSOR_TYPE(attrh);
+ strscpy(a->name, adesc->name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ if (a->extended_attrs) {
+ unsigned int ares = le32_to_cpu(adesc->resolution);
+
+ a->resolution = SENSOR_RES(ares);
+ a->exponent = S32_EXT(SENSOR_RES_EXP(ares));
+ dsize += sizeof(adesc->resolution);
+
+ scmi_parse_range_attrs(&a->attrs, &adesc->attrs);
+ dsize += sizeof(adesc->attrs);
+ }
+ st->priv = ((u8 *)adesc + dsize);
+
+ return 0;
+}
+
+static int
+iter_axes_extended_name_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ u32 flags;
+ const struct scmi_msg_resp_sensor_axis_names_description *r = response;
+
+ flags = le32_to_cpu(r->num_axis_flags);
+ st->num_returned = NUM_AXIS_RETURNED(flags);
+ st->num_remaining = NUM_AXIS_REMAINING(flags);
+ st->priv = (void *)&r->desc[0];
+
+ return 0;
+}
+
+static int
+iter_axes_extended_name_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st,
+ void *priv)
+{
+ struct scmi_sensor_axis_info *a;
+ const struct scmi_apriv *apriv = priv;
+ struct scmi_sensor_axis_name_descriptor *adesc = st->priv;
+ u32 axis_id = le32_to_cpu(adesc->axis_id);
+
+ if (axis_id >= st->max_resources)
+ return -EPROTO;
+
+ /*
+ * Pick the corresponding descriptor based on the axis_id embedded
+ * in the reply since the list of axes supporting extended names
+ * can be a subset of all the axes.
+ */
+ a = &apriv->s->axis[axis_id];
+ strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE);
+ st->priv = ++adesc;
+
+ return 0;
+}
+
+static int
+scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph,
+ struct scmi_sensor_info *s)
+{
+ int ret;
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_axes_desc_prepare_message,
+ .update_state = iter_axes_extended_name_update_state,
+ .process_response = iter_axes_extended_name_process_response,
+ };
+ struct scmi_apriv apriv = {
+ .any_axes_support_extended_names = false,
+ .s = s,
+ };
+
+ iter = ph->hops->iter_response_init(ph, &ops, s->num_axis,
+ SENSOR_AXIS_NAME_GET,
+ sizeof(struct scmi_msg_sensor_axis_description_get),
+ &apriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ /*
+ * Do not cause whole protocol initialization failure when failing to
+ * get extended names for axes.
+ */
+ ret = ph->hops->iter_response_run(iter);
+ if (ret)
+ dev_warn(ph->dev,
+ "Failed to get axes extended names for %s (ret:%d).\n",
+ s->name, ret);
+
+ return 0;
+}
+
+static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
+ struct scmi_sensor_info *s,
+ u32 version)
+{
+ int ret;
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_axes_desc_prepare_message,
+ .update_state = iter_axes_desc_update_state,
+ .process_response = iter_axes_desc_process_response,
+ };
+ struct scmi_apriv apriv = {
+ .any_axes_support_extended_names = false,
+ .s = s,
+ };
+
+ s->axis = devm_kcalloc(ph->dev, s->num_axis,
+ sizeof(*s->axis), GFP_KERNEL);
+ if (!s->axis)
+ return -ENOMEM;
+
+ iter = ph->hops->iter_response_init(ph, &ops, s->num_axis,
+ SENSOR_AXIS_DESCRIPTION_GET,
+ sizeof(struct scmi_msg_sensor_axis_description_get),
+ &apriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ ret = ph->hops->iter_response_run(iter);
+ if (ret)
+ return ret;
+
+ if (PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ apriv.any_axes_support_extended_names)
+ ret = scmi_sensor_axis_extended_names_get(ph, s);
+
+ return ret;
+}
+
+static void iter_sens_descr_prepare_message(void *message,
+ unsigned int desc_index,
+ const void *priv)
+{
+ struct scmi_msg_sensor_description *msg = message;
+
+ msg->desc_index = cpu_to_le32(desc_index);
+}
+
+static int iter_sens_descr_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ const struct scmi_msg_resp_sensor_description *r = response;
+
+ st->num_returned = le16_to_cpu(r->num_returned);
+ st->num_remaining = le16_to_cpu(r->num_remaining);
+ st->priv = (void *)&r->desc[0];
+
+ return 0;
+}
+
+static int
+iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
+
+{
+ int ret = 0;
+ u32 attrh, attrl;
+ size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ;
+ struct scmi_sensor_info *s;
+ struct sensors_info *si = priv;
+ const struct scmi_sensor_descriptor *sdesc = st->priv;
+
+ s = &si->sensors[st->desc_index + st->loop_idx];
+ s->id = le32_to_cpu(sdesc->id);
+
+ attrl = le32_to_cpu(sdesc->attributes_low);
+ /* common bitfields parsing */
+ s->num_trip_points = NUM_TRIP_POINTS(attrl);
+ /**
+ * only SCMIv3.0 specific bitfield below.
+ * Such bitfields are assumed to be zeroed on non
+ * relevant fw versions...assuming fw not buggy !
+ */
+ s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
+ s->timestamped = SUPPORTS_TIMESTAMP(attrl);
+ if (s->timestamped)
+ s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
+ s->extended_scalar_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
+
+ attrh = le32_to_cpu(sdesc->attributes_high);
+ /* common bitfields parsing */
+ s->scale = S32_EXT(SENSOR_SCALE(attrh));
+ s->type = SENSOR_TYPE(attrh);
+ /* Use pre-allocated pool wherever possible */
+ s->intervals.desc = s->intervals.prealloc_pool;
+ if (si->version == SCMIv2_SENSOR_PROTOCOL) {
+ s->intervals.segmented = false;
+ s->intervals.count = 1;
+ /*
+ * Convert SCMIv2.0 update interval format to
+ * SCMIv3.0 to be used as the common exposed
+ * descriptor, accessible via common macros.
+ */
+ s->intervals.desc[0] = (SENSOR_UPDATE_BASE(attrh) << 5) |
+ SENSOR_UPDATE_SCALE(attrh);
+ } else {
+ /*
+ * From SCMIv3.0 update intervals are retrieved
+ * via a dedicated (optional) command.
+ * Since the command is optional, on error carry
+ * on without any update interval.
+ */
+ if (scmi_sensor_update_intervals(ph, s))
+ dev_dbg(ph->dev,
+ "Update Intervals not available for sensor ID:%d\n",
+ s->id);
+ }
+ /**
+ * only > SCMIv2.0 specific bitfield below.
+ * Such bitfields are assumed to be zeroed on non
+ * relevant fw versions...assuming fw not buggy !
+ */
+ s->num_axis = min_t(unsigned int,
+ SUPPORTS_AXIS(attrh) ?
+ SENSOR_AXIS_NUMBER(attrh) : 0,
+ SCMI_MAX_NUM_SENSOR_AXIS);
+ strscpy(s->name, sdesc->name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ /*
+ * If supported overwrite short name with the extended
+ * one; on error just carry on and use already provided
+ * short name.
+ */
+ if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 &&
+ SUPPORTS_EXTENDED_NAMES(attrl))
+ ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id,
+ s->name, SCMI_MAX_STR_SIZE);
+
+ if (s->extended_scalar_attrs) {
+ s->sensor_power = le32_to_cpu(sdesc->power);
+ dsize += sizeof(sdesc->power);
+
+ /* Only for sensors reporting scalar values */
+ if (s->num_axis == 0) {
+ unsigned int sres = le32_to_cpu(sdesc->resolution);
+
+ s->resolution = SENSOR_RES(sres);
+ s->exponent = S32_EXT(SENSOR_RES_EXP(sres));
+ dsize += sizeof(sdesc->resolution);
+
+ scmi_parse_range_attrs(&s->scalar_attrs,
+ &sdesc->scalar_attrs);
+ dsize += sizeof(sdesc->scalar_attrs);
+ }
+ }
+
+ if (s->num_axis > 0)
+ ret = scmi_sensor_axis_description(ph, s, si->version);
+
+ st->priv = ((u8 *)sdesc + dsize);
+
+ return ret;
+}
+
+static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph,
+ struct sensors_info *si)
+{
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_sens_descr_prepare_message,
+ .update_state = iter_sens_descr_update_state,
+ .process_response = iter_sens_descr_process_response,
+ };
+
+ iter = ph->hops->iter_response_init(ph, &ops, si->num_sensors,
+ SENSOR_DESCRIPTION_GET,
+ sizeof(__le32), si);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ return ph->hops->iter_response_run(iter);
+}
+
+static int
+scmi_sensor_trip_point_config(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u8 trip_id, u64 trip_value)
+{
+ int ret;
+ u32 evt_cntl = SENSOR_TP_BOTH;
+ struct scmi_xfer *t;
+ struct scmi_msg_set_sensor_trip_point *trip;
+
+ ret = ph->xops->xfer_get_init(ph, SENSOR_TRIP_POINT_CONFIG,
+ sizeof(*trip), 0, &t);
+ if (ret)
+ return ret;
+
+ trip = t->tx.buf;
+ trip->id = cpu_to_le32(sensor_id);
+ trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
+ trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
+ trip->value_high = cpu_to_le32(trip_value >> 32);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_sensor_config_get(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u32 *sensor_config)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct sensors_info *si = ph->get_priv(ph);
+
+ if (sensor_id >= si->num_sensors)
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, SENSOR_CONFIG_GET,
+ sizeof(__le32), sizeof(__le32), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(sensor_id, t->tx.buf);
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ struct scmi_sensor_info *s = si->sensors + sensor_id;
+
+ *sensor_config = get_unaligned_le64(t->rx.buf);
+ s->sensor_config = *sensor_config;
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_sensor_config_set(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u32 sensor_config)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_sensor_config_set *msg;
+ struct sensors_info *si = ph->get_priv(ph);
+
+ if (sensor_id >= si->num_sensors)
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, SENSOR_CONFIG_SET,
+ sizeof(*msg), 0, &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->id = cpu_to_le32(sensor_id);
+ msg->sensor_config = cpu_to_le32(sensor_config);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ struct scmi_sensor_info *s = si->sensors + sensor_id;
+
+ s->sensor_config = sensor_config;
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+/**
+ * scmi_sensor_reading_get - Read scalar sensor value
+ * @ph: Protocol handle
+ * @sensor_id: Sensor ID
+ * @value: The 64bit value sensor reading
+ *
+ * This function returns a single 64 bit reading value representing the sensor
+ * value; if the platform SCMI Protocol implementation and the sensor support
+ * multiple axis and timestamped-reads, this just returns the first axis while
+ * dropping the timestamp value.
+ * Use instead the @scmi_sensor_reading_get_timestamped to retrieve the array of
+ * timestamped multi-axis values.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_sensor_reading_get(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u64 *value)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_sensor_reading_get *sensor;
+ struct scmi_sensor_info *s;
+ struct sensors_info *si = ph->get_priv(ph);
+
+ if (sensor_id >= si->num_sensors)
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, SENSOR_READING_GET,
+ sizeof(*sensor), 0, &t);
+ if (ret)
+ return ret;
+
+ sensor = t->tx.buf;
+ sensor->id = cpu_to_le32(sensor_id);
+ s = si->sensors + sensor_id;
+
+ sensor->flags = cpu_to_le32(0);
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *value = get_unaligned_le64(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static inline void
+scmi_parse_sensor_readings(struct scmi_sensor_reading *out,
+ const struct scmi_sensor_reading_resp *in)
+{
+ out->value = get_unaligned_le64((void *)&in->sensor_value_low);
+ out->timestamp = get_unaligned_le64((void *)&in->timestamp_low);
+}
+
+/**
+ * scmi_sensor_reading_get_timestamped - Read multiple-axis timestamped values
+ * @ph: Protocol handle
+ * @sensor_id: Sensor ID
+ * @count: The length of the provided @readings array
+ * @readings: An array of elements each representing a timestamped per-axis
+ * reading of type @struct scmi_sensor_reading.
+ * Returned readings are ordered as the @axis descriptors array
+ * included in @struct scmi_sensor_info and the max number of
+ * returned elements is min(@count, @num_axis); ideally the provided
+ * array should be of length @count equal to @num_axis.
+ *
+ * Return: 0 on Success
+ */
+static int
+scmi_sensor_reading_get_timestamped(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u8 count,
+ struct scmi_sensor_reading *readings)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_sensor_reading_get *sensor;
+ struct scmi_sensor_info *s;
+ struct sensors_info *si = ph->get_priv(ph);
+
+ if (sensor_id >= si->num_sensors)
+ return -EINVAL;
+
+ s = si->sensors + sensor_id;
+ if (!count || !readings ||
+ (!s->num_axis && count > 1) || (s->num_axis && count > s->num_axis))
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, SENSOR_READING_GET,
+ sizeof(*sensor), 0, &t);
+ if (ret)
+ return ret;
+
+ sensor = t->tx.buf;
+ sensor->id = cpu_to_le32(sensor_id);
+
+ sensor->flags = cpu_to_le32(0);
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ int i;
+ struct scmi_sensor_reading_resp *resp_readings;
+
+ resp_readings = t->rx.buf;
+ for (i = 0; i < count; i++)
+ scmi_parse_sensor_readings(&readings[i],
+ &resp_readings[i]);
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static const struct scmi_sensor_info *
+scmi_sensor_info_get(const struct scmi_protocol_handle *ph, u32 sensor_id)
+{
+ struct sensors_info *si = ph->get_priv(ph);
+
+ if (sensor_id >= si->num_sensors)
+ return NULL;
+
+ return si->sensors + sensor_id;
+}
+
+static int scmi_sensor_count_get(const struct scmi_protocol_handle *ph)
+{
+ struct sensors_info *si = ph->get_priv(ph);
+
+ return si->num_sensors;
+}
+
+static const struct scmi_sensor_proto_ops sensor_proto_ops = {
+ .count_get = scmi_sensor_count_get,
+ .info_get = scmi_sensor_info_get,
+ .trip_point_config = scmi_sensor_trip_point_config,
+ .reading_get = scmi_sensor_reading_get,
+ .reading_get_timestamped = scmi_sensor_reading_get_timestamped,
+ .config_get = scmi_sensor_config_get,
+ .config_set = scmi_sensor_config_set,
+};
+
+static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
+{
+ u32 version;
+ int ret;
+ struct sensors_info *sinfo;
+
+ ret = ph->xops->version_get(ph, &version);
+ if (ret)
+ return ret;
+
+ dev_dbg(ph->dev, "Sensor Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ sinfo = devm_kzalloc(ph->dev, sizeof(*sinfo), GFP_KERNEL);
+ if (!sinfo)
+ return -ENOMEM;
+ sinfo->version = version;
+
+ ret = scmi_sensor_attributes_get(ph, sinfo);
+ if (ret)
+ return ret;
+ sinfo->sensors = devm_kcalloc(ph->dev, sinfo->num_sensors,
+ sizeof(*sinfo->sensors), GFP_KERNEL);
+ if (!sinfo->sensors)
+ return -ENOMEM;
+
+ ret = scmi_sensor_description_get(ph, sinfo);
+ if (ret)
+ return ret;
+
+ return ph->set_priv(ph, sinfo);
+}
+
+static const struct scmi_protocol scmi_sensors = {
+ .id = SCMI_PROTOCOL_SENSOR,
+ .instance_init = &scmi_sensors_protocol_init,
+ .ops = &sensor_proto_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER(sensors, scmi_sensors)
diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c
index 2dde2b6e09..38ac92031b 100644
--- a/drivers/firmware/arm_scmi/shmem.c
+++ b/drivers/firmware/arm_scmi/shmem.c
@@ -6,7 +6,7 @@
*/
#include <common.h>
-#include <io.h>
+#include <linux/io.h>
#include <linux/types.h>
#include <linux/processor.h>
@@ -31,20 +31,39 @@ struct scmi_shared_mem {
};
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
- struct scmi_xfer *xfer)
+ struct scmi_xfer *xfer, struct scmi_chan_info *cinfo)
{
+ ktime_t stop;
+
/*
* Ideally channel must be free by now unless OS timeout last
* request and platform continued to process the same, wait
* until it releases the shared memory, otherwise we may endup
- * overwriting its response with new message payload or vice-versa
+ * overwriting its response with new message payload or vice-versa.
+ * Giving up anyway after twice the expected channel timeout so as
+ * not to bail-out on intermittent issues where the platform is
+ * occasionally a bit slower to answer.
+ *
+ * Note that after a timeout is detected we bail-out and carry on but
+ * the transport functionality is probably permanently compromised:
+ * this is just to ease debugging and avoid complete hangs on boot
+ * due to a misbehaving SCMI firmware.
*/
- spin_until_cond(ioread32(&shmem->channel_status) &
- SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+ stop = ktime_add_ms(ktime_get(), 2 * cinfo->rx_timeout_ms);
+ spin_until_cond((ioread32(&shmem->channel_status) &
+ SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ||
+ ktime_after(ktime_get(), stop));
+ if (!(ioread32(&shmem->channel_status) &
+ SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
+ WARN_ON_ONCE(1);
+ dev_err(cinfo->dev,
+ "Timeout waiting for a free TX channel !\n");
+ return;
+ }
+
/* Mark channel busy + clear error */
iowrite32(0x0, &shmem->channel_status);
- iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
- &shmem->flags);
+ iowrite32(0, &shmem->flags); /* No SCMI_SHMEM_FLAG_INTR_ENABLED */
iowrite32(sizeof(shmem->msg_header) + xfer->tx.len, &shmem->length);
iowrite32(pack_scmi_header(&xfer->hdr), &shmem->msg_header);
if (xfer->tx.buf)
@@ -59,10 +78,11 @@ u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer)
{
+ size_t len = ioread32(&shmem->length);
+
xfer->hdr.status = ioread32(shmem->msg_payload);
/* Skip the length of header and status in shmem area i.e 8 bytes */
- xfer->rx.len = min_t(size_t, xfer->rx.len,
- ioread32(&shmem->length) - 8);
+ xfer->rx.len = min_t(size_t, xfer->rx.len, len > 8 ? len - 8 : 0);
/* Take a copy to the rx buffer.. */
memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
@@ -72,18 +92,3 @@ void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem)
{
iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status);
}
-
-bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
- struct scmi_xfer *xfer)
-{
- u16 xfer_id;
-
- xfer_id = MSG_XTRACT_TOKEN(ioread32(&shmem->msg_header));
-
- if (xfer->hdr.seq != xfer_id)
- return false;
-
- return ioread32(&shmem->channel_status) &
- (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
- SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
-}
diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c
index 67f19a7b43..d128820b02 100644
--- a/drivers/firmware/arm_scmi/smc.c
+++ b/drivers/firmware/arm_scmi/smc.c
@@ -8,36 +8,67 @@
#include <common.h>
#include <linux/arm-smccc.h>
-#include <driver.h>
+#include <linux/mutex.h>
+#include <linux/processor.h>
+#include <linux/sizes.h>
+#include <linux/device.h>
#include <linux/err.h>
#include <of.h>
#include <of_address.h>
#include "common.h"
+/*
+ * The shmem address is split into 4K page and offset.
+ * This is to make sure the parameters fit in 32bit arguments of the
+ * smc/hvc call to keep it uniform across smc32/smc64 conventions.
+ * This however limits the shmem address to 44 bit.
+ *
+ * These optional parameters can be used to distinguish among multiple
+ * scmi instances that are using the same smc-id.
+ * The page parameter is passed in r1/x1/w1 register and the offset parameter
+ * is passed in r2/x2/w2 register.
+ */
+
+#define SHMEM_SIZE (SZ_4K)
+#define SHMEM_SHIFT 12
+#define SHMEM_PAGE(x) (_UL((x) >> SHMEM_SHIFT))
+#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1))
+
/**
* struct scmi_smc - Structure representing a SCMI smc transport
*
* @cinfo: SCMI channel info
* @shmem: Transmit/Receive shared memory area
* @func_id: smc/hvc call function id
+ * @param_page: 4K page number of the shmem channel
+ * @param_offset: Offset within the 4K page of the shmem channel
*/
struct scmi_smc {
struct scmi_chan_info *cinfo;
struct scmi_shared_mem __iomem *shmem;
+ /* Protect access to shmem area */
+ struct mutex shmem_lock;
u32 func_id;
+ u32 param_page;
+ u32 param_offset;
};
-static bool smc_chan_available(struct device_d *dev, int idx)
+static bool smc_chan_available(struct device_node *of_node, int idx)
{
- return of_parse_phandle(dev->device_node, "shmem", 0) != NULL;
+ struct device_node *np = of_parse_phandle(of_node, "shmem", 0);
+ if (!np)
+ return false;
+
+ of_node_put(np);
+ return true;
}
-static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device_d *dev,
+static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
bool tx)
{
- struct device_d *cdev = cinfo->dev;
+ struct device *cdev = cinfo->dev;
struct scmi_smc *scmi_info;
resource_size_t size;
struct resource res;
@@ -48,24 +79,39 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device_d *dev,
if (!tx)
return -ENODEV;
- scmi_info = kzalloc(sizeof(*scmi_info), GFP_KERNEL);
+ scmi_info = devm_kzalloc(dev, sizeof(*scmi_info), GFP_KERNEL);
if (!scmi_info)
return -ENOMEM;
- np = of_parse_phandle(cdev->device_node, "shmem", 0);
+ np = of_parse_phandle(cdev->of_node, "shmem", 0);
+ if (!of_device_is_compatible(np, "arm,scmi-shmem")) {
+ of_node_put(np);
+ return -ENXIO;
+ }
+
ret = of_address_to_resource(np, 0, &res);
+ of_node_put(np);
if (ret) {
dev_err(cdev, "failed to get SCMI Tx shared memory\n");
return ret;
}
size = resource_size(&res);
- scmi_info->shmem = IOMEM(res.start);
+ scmi_info->shmem = devm_ioremap(dev, res.start, size);
+ if (!scmi_info->shmem) {
+ dev_err(dev, "failed to ioremap SCMI Tx shared memory\n");
+ return -EADDRNOTAVAIL;
+ }
- ret = of_property_read_u32(dev->device_node, "arm,smc-id", &func_id);
+ ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id);
if (ret < 0)
return ret;
+ if (of_device_is_compatible(dev->of_node, "arm,scmi-smc-param")) {
+ scmi_info->param_page = SHMEM_PAGE(res.start);
+ scmi_info->param_offset = SHMEM_OFFSET(res.start);
+ }
+
scmi_info->func_id = func_id;
scmi_info->cinfo = cinfo;
cinfo->transport_info = scmi_info;
@@ -81,8 +127,6 @@ static int smc_chan_free(int id, void *p, void *data)
cinfo->transport_info = NULL;
scmi_info->cinfo = NULL;
- scmi_free_channel(cinfo, data, id);
-
return 0;
}
@@ -91,16 +135,18 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
{
struct scmi_smc *scmi_info = cinfo->transport_info;
struct arm_smccc_res res;
+ unsigned long page = scmi_info->param_page;
+ unsigned long offset = scmi_info->param_offset;
- shmem_tx_prepare(scmi_info->shmem, xfer);
-
- arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+ shmem_tx_prepare(scmi_info->shmem, xfer, cinfo);
- scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem));
+ arm_smccc_1_1_invoke(scmi_info->func_id, page, offset, 0, 0, 0, 0, 0,
+ &res);
/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
if (res.a0)
return -EOPNOTSUPP;
+
return 0;
}
@@ -112,21 +158,12 @@ static void smc_fetch_response(struct scmi_chan_info *cinfo,
shmem_fetch_response(scmi_info->shmem, xfer);
}
-static bool
-smc_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
-{
- struct scmi_smc *scmi_info = cinfo->transport_info;
-
- return shmem_poll_done(scmi_info->shmem, xfer);
-}
-
static const struct scmi_transport_ops scmi_smc_ops = {
.chan_available = smc_chan_available,
.chan_setup = smc_chan_setup,
.chan_free = smc_chan_free,
.send_message = smc_send_message,
.fetch_response = smc_fetch_response,
- .poll_done = smc_poll_done,
};
const struct scmi_desc scmi_smc_desc = {
@@ -134,4 +171,13 @@ const struct scmi_desc scmi_smc_desc = {
.max_rx_timeout_ms = 30,
.max_msg = 20,
.max_msg_size = 128,
+ /*
+ * Setting .sync_cmds_atomic_replies to true for SMC assumes that,
+ * once the SMC instruction has completed successfully, the issued
+ * SCMI command would have been already fully processed by the SCMI
+ * platform firmware and so any possible response value expected
+ * for the issued command will be immmediately ready to be fetched
+ * from the shared memory area.
+ */
+ .sync_cmds_completed_on_ret = true,
};
diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c
index a3d78db28f..a9352fcb76 100644
--- a/drivers/firmware/arm_scmi/voltage.c
+++ b/drivers/firmware/arm_scmi/voltage.c
@@ -2,7 +2,7 @@
/*
* System Control and Management Interface (SCMI) Voltage Protocol
*
- * Copyright (C) 2020-2021 ARM Ltd.
+ * Copyright (C) 2020-2022 ARM Ltd.
*/
#include <common.h>
@@ -21,13 +21,16 @@ enum scmi_voltage_protocol_cmd {
VOLTAGE_CONFIG_GET = 0x6,
VOLTAGE_LEVEL_SET = 0x7,
VOLTAGE_LEVEL_GET = 0x8,
+ VOLTAGE_DOMAIN_NAME_GET = 0x09,
};
#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
struct scmi_msg_resp_domain_attributes {
__le32 attr;
- u8 name[SCMI_MAX_STR_SIZE];
+#define SUPPORTS_ASYNC_LEVEL_SET(x) ((x) & BIT(31))
+#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30))
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
};
struct scmi_msg_cmd_describe_levels {
@@ -54,6 +57,11 @@ struct scmi_msg_cmd_level_set {
__le32 voltage_level;
};
+struct scmi_resp_voltage_level_set_complete {
+ __le32 domain_id;
+ __le32 voltage_level;
+};
+
struct voltage_info {
unsigned int version;
unsigned int num_domains;
@@ -80,7 +88,7 @@ static int scmi_protocol_attributes_get(const struct scmi_protocol_handle *ph,
return ret;
}
-static int scmi_init_voltage_levels(struct device_d *dev,
+static int scmi_init_voltage_levels(struct device *dev,
struct scmi_voltage_info *v,
u32 num_returned, u32 num_remaining,
bool segmented)
@@ -100,7 +108,7 @@ static int scmi_init_voltage_levels(struct device_d *dev,
return -EINVAL;
}
- v->levels_uv = kcalloc(num_levels, sizeof(u32), GFP_KERNEL);
+ v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
if (!v->levels_uv)
return -ENOMEM;
@@ -110,14 +118,100 @@ static int scmi_init_voltage_levels(struct device_d *dev,
return 0;
}
+struct scmi_volt_ipriv {
+ struct device *dev;
+ struct scmi_voltage_info *v;
+};
+
+static void iter_volt_levels_prepare_message(void *message,
+ unsigned int desc_index,
+ const void *priv)
+{
+ struct scmi_msg_cmd_describe_levels *msg = message;
+ const struct scmi_volt_ipriv *p = priv;
+
+ msg->domain_id = cpu_to_le32(p->v->id);
+ msg->level_index = cpu_to_le32(desc_index);
+}
+
+static int iter_volt_levels_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ int ret = 0;
+ u32 flags;
+ const struct scmi_msg_resp_describe_levels *r = response;
+ struct scmi_volt_ipriv *p = priv;
+
+ flags = le32_to_cpu(r->flags);
+ st->num_returned = NUM_RETURNED_LEVELS(flags);
+ st->num_remaining = NUM_REMAINING_LEVELS(flags);
+
+ /* Allocate space for num_levels if not already done */
+ if (!p->v->num_levels) {
+ ret = scmi_init_voltage_levels(p->dev, p->v, st->num_returned,
+ st->num_remaining,
+ SUPPORTS_SEGMENTED_LEVELS(flags));
+ if (!ret)
+ st->max_resources = p->v->num_levels;
+ }
+
+ return ret;
+}
+
+static int
+iter_volt_levels_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
+{
+ s32 val;
+ const struct scmi_msg_resp_describe_levels *r = response;
+ struct scmi_volt_ipriv *p = priv;
+
+ val = (s32)le32_to_cpu(r->voltage[st->loop_idx]);
+ p->v->levels_uv[st->desc_index + st->loop_idx] = val;
+ if (val < 0)
+ p->v->negative_volts_allowed = true;
+
+ return 0;
+}
+
+static int scmi_voltage_levels_get(const struct scmi_protocol_handle *ph,
+ struct scmi_voltage_info *v)
+{
+ int ret;
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_volt_levels_prepare_message,
+ .update_state = iter_volt_levels_update_state,
+ .process_response = iter_volt_levels_process_response,
+ };
+ struct scmi_volt_ipriv vpriv = {
+ .dev = ph->dev,
+ .v = v,
+ };
+
+ iter = ph->hops->iter_response_init(ph, &ops, v->num_levels,
+ VOLTAGE_DESCRIBE_LEVELS,
+ sizeof(struct scmi_msg_cmd_describe_levels),
+ &vpriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ ret = ph->hops->iter_response_run(iter);
+ if (ret) {
+ v->num_levels = 0;
+ devm_kfree(ph->dev, v->levels_uv);
+ }
+
+ return ret;
+}
+
static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
struct voltage_info *vinfo)
{
int ret, dom;
- struct scmi_xfer *td, *tl;
- struct device_d *dev = ph->dev;
+ struct scmi_xfer *td;
struct scmi_msg_resp_domain_attributes *resp_dom;
- struct scmi_msg_resp_describe_levels *resp_levels;
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
sizeof(__le32), sizeof(*resp_dom), &td);
@@ -125,90 +219,37 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
return ret;
resp_dom = td->rx.buf;
- ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS,
- sizeof(__le64), 0, &tl);
- if (ret)
- goto outd;
- resp_levels = tl->rx.buf;
-
for (dom = 0; dom < vinfo->num_domains; dom++) {
- u32 desc_index = 0;
- u16 num_returned = 0, num_remaining = 0;
- struct scmi_msg_cmd_describe_levels *cmd;
+ u32 attributes;
struct scmi_voltage_info *v;
/* Retrieve domain attributes at first ... */
put_unaligned_le32(dom, td->tx.buf);
- ret = ph->xops->do_xfer(ph, td);
/* Skip domain on comms error */
- if (ret)
+ if (ph->xops->do_xfer(ph, td))
continue;
v = vinfo->domains + dom;
v->id = dom;
- v->attributes = le32_to_cpu(resp_dom->attr);
- strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
-
- cmd = tl->tx.buf;
- /* ...then retrieve domain levels descriptions */
- do {
- u32 flags;
- int cnt;
-
- cmd->domain_id = cpu_to_le32(v->id);
- cmd->level_index = desc_index;
- ret = ph->xops->do_xfer(ph, tl);
- if (ret)
- break;
-
- flags = le32_to_cpu(resp_levels->flags);
- num_returned = NUM_RETURNED_LEVELS(flags);
- num_remaining = NUM_REMAINING_LEVELS(flags);
-
- /* Allocate space for num_levels if not already done */
- if (!v->num_levels) {
- ret = scmi_init_voltage_levels(dev, v,
- num_returned,
- num_remaining,
- SUPPORTS_SEGMENTED_LEVELS(flags));
- if (ret)
- break;
- }
-
- if (desc_index + num_returned > v->num_levels) {
- dev_err(ph->dev,
- "No. of voltage levels can't exceed %d\n",
- v->num_levels);
- ret = -EINVAL;
- break;
- }
-
- for (cnt = 0; cnt < num_returned; cnt++) {
- s32 val;
-
- val =
- (s32)le32_to_cpu(resp_levels->voltage[cnt]);
- v->levels_uv[desc_index + cnt] = val;
- if (val < 0)
- v->negative_volts_allowed = true;
- }
-
- desc_index += num_returned;
-
- ph->xops->reset_rx_to_maxsz(ph, tl);
- /* check both to avoid infinite loop due to buggy fw */
- } while (num_returned && num_remaining);
-
- if (ret) {
- v->num_levels = 0;
- kfree(v->levels_uv);
+ attributes = le32_to_cpu(resp_dom->attr);
+ strscpy(v->name, resp_dom->name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ /*
+ * If supported overwrite short name with the extended one;
+ * on error just carry on and use already provided short name.
+ */
+ if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) {
+ if (SUPPORTS_EXTENDED_NAMES(attributes))
+ ph->hops->extended_name_get(ph,
+ VOLTAGE_DOMAIN_NAME_GET,
+ v->id, v->name,
+ SCMI_MAX_STR_SIZE);
}
- ph->xops->reset_rx_to_maxsz(ph, td);
+ /* Skip invalid voltage descriptors */
+ scmi_voltage_levels_get(ph, v);
}
- ph->xops->xfer_put(ph, tl);
-outd:
ph->xops->xfer_put(ph, td);
return ret;
@@ -271,12 +312,15 @@ static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph,
}
static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 flags, s32 volt_uV)
+ u32 domain_id,
+ enum scmi_voltage_level_mode mode,
+ s32 volt_uV)
{
int ret;
struct scmi_xfer *t;
struct voltage_info *vinfo = ph->get_priv(ph);
struct scmi_msg_cmd_level_set *cmd;
+ struct scmi_voltage_info *v;
if (domain_id >= vinfo->num_domains)
return -EINVAL;
@@ -286,11 +330,13 @@ static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
if (ret)
return ret;
+ v = vinfo->domains + domain_id;
+
cmd = t->tx.buf;
cmd->domain_id = cpu_to_le32(domain_id);
- cmd->flags = cpu_to_le32(flags);
cmd->voltage_level = cpu_to_le32(volt_uV);
+ cmd->flags = cpu_to_le32(0x0);
ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
@@ -345,7 +391,7 @@ static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
dev_dbg(ph->dev, "Voltage Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
- vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL);
+ vinfo = devm_kzalloc(ph->dev, sizeof(*vinfo), GFP_KERNEL);
if (!vinfo)
return -ENOMEM;
vinfo->version = version;
@@ -355,7 +401,7 @@ static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
return ret;
if (vinfo->num_domains) {
- vinfo->domains = kcalloc(vinfo->num_domains,
+ vinfo->domains = devm_kcalloc(ph->dev, vinfo->num_domains,
sizeof(*vinfo->domains),
GFP_KERNEL);
if (!vinfo->domains)
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
new file mode 100644
index 0000000000..3f129a2c1e
--- /dev/null
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * qemu_fw_cfg.c - QEMU FW CFG character device
+ *
+ * Copyright (C) 2022 Adrian Negreanu
+ * Copyright (C) 2022 Ahmad Fatoum
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <fcntl.h>
+#include <dma.h>
+#include <linux/err.h>
+#include <linux/bitfield.h>
+#include <linux/qemu_fw_cfg.h>
+#include <asm/unaligned.h>
+#include <io-64-nonatomic-lo-hi.h>
+
+/* arch-specific ctrl & data register offsets are not available in ACPI, DT */
+#ifdef CONFIG_X86
+# define FW_CFG_CTRL_OFF 0x00
+# define FW_CFG_DATA_OFF 0x01
+# define FW_CFG_DMA_OFF 0x04
+#else
+# define FW_CFG_CTRL_OFF 0x08
+# define FW_CFG_DATA_OFF 0x00
+# define FW_CFG_DMA_OFF 0x10
+#endif
+
+/* fw_cfg DMA commands */
+#define FW_CFG_DMA_CTL_ERROR 0x01
+#define FW_CFG_DMA_CTL_READ 0x02
+#define FW_CFG_DMA_CTL_SKIP 0x04
+#define FW_CFG_DMA_CTL_SELECT 0x08
+#define FW_CFG_DMA_CTL_WRITE 0x10
+
+struct fw_cfg_dma {
+ __be32 control;
+ __be32 length;
+ __be64 address;
+} __packed;
+
+/* fw_cfg device i/o register addresses */
+struct fw_cfg {
+ struct resource *iores;
+ void __iomem *reg_ctrl;
+ void __iomem *reg_data;
+ void __iomem *reg_dma;
+ struct cdev cdev;
+ loff_t next_read_offset;
+ u32 sel;
+ bool is_mmio;
+ struct fw_cfg_dma __iomem *acc_virt;
+ dma_addr_t acc_dma;
+};
+
+static struct fw_cfg *to_fw_cfg(struct cdev *cdev)
+{
+ return container_of(cdev, struct fw_cfg, cdev);
+}
+
+/* pick appropriate endianness for selector key */
+static void fw_cfg_select(struct fw_cfg *fw_cfg)
+{
+ if (fw_cfg->is_mmio)
+ iowrite16be(fw_cfg->sel, fw_cfg->reg_ctrl);
+ else
+ iowrite16(fw_cfg->sel, fw_cfg->reg_ctrl);
+}
+
+/* clean up fw_cfg device i/o */
+static void fw_cfg_io_cleanup(struct fw_cfg *fw_cfg)
+{
+ release_region(fw_cfg->iores);
+}
+
+static int fw_cfg_ioctl(struct cdev *cdev, int request, void *buf)
+{
+ struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
+ int ret = 0;
+
+ switch (request) {
+ case FW_CFG_SELECT:
+ fw_cfg->sel = *(u16 *)buf;
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+
+ return 0;
+}
+
+#define __raw_readu64 __raw_readq
+#define __raw_readu32 __raw_readl
+#define __raw_readu16 __raw_readw
+#define __raw_readu8 __raw_readb
+
+#define fw_cfg_data_read_sized(fw_cfg, remaining, address, type) do { \
+ while (*remaining >= sizeof(type)) { \
+ val = __raw_read##type((fw_cfg)->reg_data); \
+ *remaining -= sizeof(type); \
+ if (*address) { \
+ put_unaligned(val, (type *)*address); \
+ *address += sizeof(type); \
+ } \
+ } \
+} while(0)
+
+static void fw_cfg_data_read(struct fw_cfg *fw_cfg, void *address, size_t remaining,
+ unsigned rdsize)
+{
+
+ u64 val;
+
+ if (fw_cfg->is_mmio) {
+ /*
+ * This is just a preference. If we can't honour it, we
+ * fall back to byte-sized copy
+ */
+ switch(rdsize) {
+ case 8:
+#ifdef CONFIG_64BIT
+ fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u64);
+ break;
+#endif
+ case 4:
+ fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u32);
+ break;
+ case 2:
+ fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u16);
+ break;
+ }
+ }
+
+ fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u8);
+}
+
+static ssize_t fw_cfg_read(struct cdev *cdev, void *buf, size_t count,
+ loff_t pos, unsigned long flags)
+{
+ struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
+ unsigned rdsize = FIELD_GET(O_RWSIZE_MASK, flags);
+
+ if (!pos || pos != fw_cfg->next_read_offset) {
+ fw_cfg_select(fw_cfg);
+ fw_cfg->next_read_offset = 0;
+ }
+
+ if (!rdsize) {
+ if (pos % 8 == 0)
+ rdsize = 8;
+ else if (pos % 4 == 0)
+ rdsize = 4;
+ else if (pos % 2 == 0)
+ rdsize = 2;
+ else
+ rdsize = 1;
+ }
+
+ while (pos-- > fw_cfg->next_read_offset)
+ fw_cfg_data_read(fw_cfg, NULL, count, rdsize);
+
+ fw_cfg_data_read(fw_cfg, buf, count, rdsize);
+
+ fw_cfg->next_read_offset += count;
+ return count;
+}
+
+static ssize_t fw_cfg_write(struct cdev *cdev, const void *buf, size_t count,
+ loff_t pos, unsigned long flags)
+{
+ struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
+ struct device *dev = cdev->dev;
+ struct fw_cfg_dma __iomem *acc = fw_cfg->acc_virt;
+ void *dma_buf;
+ dma_addr_t mapping;
+ int ret = 0;
+
+ if (pos != 0)
+ return -EINVAL;
+
+ dma_buf = dma_alloc(count);
+ if (!dma_buf)
+ return -ENOMEM;
+
+ memcpy(dma_buf, buf, count);
+
+ mapping = dma_map_single(dev, dma_buf, count, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, mapping)) {
+ ret = -EFAULT;
+ goto free_buf;
+ }
+
+ fw_cfg->next_read_offset = 0;
+
+ acc->address = cpu_to_be64(mapping);
+ acc->length = cpu_to_be32(count);
+ acc->control = cpu_to_be32(FW_CFG_DMA_CTL_WRITE |
+ FW_CFG_DMA_CTL_SELECT | fw_cfg->sel << 16);
+
+ iowrite64be(fw_cfg->acc_dma, fw_cfg->reg_dma);
+
+ while (ioread32be(&acc->control) & ~FW_CFG_DMA_CTL_ERROR)
+ ;
+
+ dma_unmap_single(dev, mapping, count, DMA_FROM_DEVICE);
+free_buf:
+ dma_free(dma_buf);
+
+ return ret ?: count;
+}
+
+static struct cdev_operations fw_cfg_ops = {
+ .read = fw_cfg_read,
+ .write = fw_cfg_write,
+ .ioctl = fw_cfg_ioctl,
+};
+
+static int fw_cfg_param_select(struct param_d *p, void *priv)
+{
+ struct fw_cfg *fw_cfg = priv;
+
+ return fw_cfg->sel <= U16_MAX ? 0 : -EINVAL;
+}
+
+static int fw_cfg_probe(struct device *dev)
+{
+ struct device_node *np = dev_of_node(dev);
+ struct resource *parent_res, *iores;
+ char sig[FW_CFG_SIG_SIZE];
+ struct fw_cfg *fw_cfg;
+ int ret;
+
+ fw_cfg = xzalloc(sizeof(*fw_cfg));
+
+ /* acquire i/o range details */
+ fw_cfg->is_mmio = false;
+ iores = dev_get_resource(dev, IORESOURCE_IO, 0);
+ if (IS_ERR(iores)) {
+ fw_cfg->is_mmio = true;
+ iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
+ if (IS_ERR(iores))
+ return -EINVAL;
+ }
+
+ parent_res = fw_cfg->is_mmio ? &iomem_resource : &ioport_resource;
+ iores = __request_region(parent_res, iores->start, iores->end, dev_name(dev), 0);
+ if (IS_ERR(iores))
+ return -EBUSY;
+
+ /* use architecture-specific offsets */
+ fw_cfg->reg_ctrl = IOMEM(iores->start + FW_CFG_CTRL_OFF);
+ fw_cfg->reg_data = IOMEM(iores->start + FW_CFG_DATA_OFF);
+ fw_cfg->reg_dma = IOMEM(iores->start + FW_CFG_DMA_OFF);
+
+ fw_cfg->iores = iores;
+
+ /* verify fw_cfg device signature */
+ fw_cfg->sel = FW_CFG_SIGNATURE;
+ fw_cfg_read(&fw_cfg->cdev, sig, FW_CFG_SIG_SIZE, 0, 0);
+
+ if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) {
+ ret = np ? -EILSEQ : -ENODEV;
+ goto err;
+ }
+
+ fw_cfg->acc_virt = dma_alloc_coherent(sizeof(*fw_cfg->acc_virt), &fw_cfg->acc_dma);
+
+ fw_cfg->cdev.name = basprintf("fw_cfg%d", cdev_find_free_index("fw_cfg"));
+ fw_cfg->cdev.flags = DEVFS_IS_CHARACTER_DEV;
+ fw_cfg->cdev.size = 0;
+ fw_cfg->cdev.ops = &fw_cfg_ops;
+ fw_cfg->cdev.dev = dev;
+ fw_cfg->cdev.filetype = filetype_qemu_fw_cfg;
+
+ dev_set_name(dev, fw_cfg->cdev.name);
+
+ ret = devfs_create(&fw_cfg->cdev);
+ if (ret) {
+ dev_err(dev, "Failed to create corresponding cdev\n");
+ goto err;
+ }
+
+ cdev_create_default_automount(&fw_cfg->cdev);
+
+ dev_add_param_uint32(dev, "selector", fw_cfg_param_select,
+ NULL, &fw_cfg->sel, "%u", fw_cfg);
+
+ dev->priv = fw_cfg;
+
+ return 0;
+err:
+ fw_cfg_io_cleanup(fw_cfg);
+ return ret;
+}
+
+static const struct of_device_id qemu_fw_cfg_of_match[] = {
+ { .compatible = "qemu,fw-cfg-mmio", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, qemu_fw_cfg_of_match);
+
+static struct driver qemu_fw_cfg_drv = {
+ .name = "fw_cfg",
+ .probe = fw_cfg_probe,
+ .of_compatible = of_match_ptr(qemu_fw_cfg_of_match),
+};
+
+static int qemu_fw_cfg_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&qemu_fw_cfg_drv);
+ if (ret)
+ return ret;
+
+ return of_devices_ensure_probed_by_dev_id(qemu_fw_cfg_of_match);
+}
+postmmu_initcall(qemu_fw_cfg_init);
diff --git a/drivers/firmware/socfpga.c b/drivers/firmware/socfpga.c
index 98b47f6570..419bd27945 100644
--- a/drivers/firmware/socfpga.c
+++ b/drivers/firmware/socfpga.c
@@ -14,10 +14,10 @@
#include <fcntl.h>
#include <init.h>
#include <io.h>
-#include <mach/cyclone5-system-manager.h>
-#include <mach/cyclone5-reset-manager.h>
-#include <mach/cyclone5-regs.h>
-#include <mach/cyclone5-sdram.h>
+#include <mach/socfpga/cyclone5-system-manager.h>
+#include <mach/socfpga/cyclone5-reset-manager.h>
+#include <mach/socfpga/cyclone5-regs.h>
+#include <mach/socfpga/cyclone5-sdram.h>
#include <asm/fncpy.h>
#include <mmu.h>
#include <asm/cache.h>
@@ -377,12 +377,12 @@ static int programmed_get(struct param_d *p, void *priv)
return 0;
}
-static int socfpga_fpgamgr_probe(struct device_d *dev)
+static int socfpga_fpgamgr_probe(struct device *dev)
{
struct resource *iores;
struct fpgamgr *mgr;
struct firmware_handler *fh;
- const char *alias = of_alias_get(dev->device_node);
+ const char *alias = of_alias_get(dev->of_node);
const char *model = NULL;
struct param_d *p;
int ret;
@@ -414,7 +414,7 @@ static int socfpga_fpgamgr_probe(struct device_d *dev)
fh->open = socfpga_fpgamgr_program_start;
fh->write = socfpga_fpgamgr_program_write_buf;
fh->close = socfpga_fpgamgr_program_finish;
- of_property_read_string(dev->device_node, "compatible", &model);
+ of_property_read_string(dev->of_node, "compatible", &model);
if (model)
fh->model = xstrdup(model);
fh->dev = dev;
@@ -435,7 +435,7 @@ static int socfpga_fpgamgr_probe(struct device_d *dev)
}
fh->dev = &mgr->dev;
- fh->device_node = dev->device_node;
+ fh->device_node = dev->of_node;
ret = firmwaremgr_register(fh);
if (ret != 0) {
@@ -459,8 +459,9 @@ static struct of_device_id socfpga_fpgamgr_id_table[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, socfpga_fpgamgr_id_table);
-static struct driver_d socfpga_fpgamgr_driver = {
+static struct driver socfpga_fpgamgr_driver = {
.name = "socfpa-fpgamgr",
.of_compatible = DRV_OF_COMPAT(socfpga_fpgamgr_id_table),
.probe = socfpga_fpgamgr_probe,
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
new file mode 100644
index 0000000000..2e930064f4
--- /dev/null
+++ b/drivers/firmware/ti_sci.c
@@ -0,0 +1,2745 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments System Control Interface Protocol Driver
+ * Based on drivers/firmware/ti_sci.c from Linux.
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla <lokeshvutla@ti.com>
+ */
+
+#include <common.h>
+#include <mailbox.h>
+#include <restart.h>
+#include <soc/ti/k3-sec-proxy.h>
+#include <soc/ti/ti_sci_protocol.h>
+
+#include "ti_sci.h"
+
+/* List of all TI SCI devices active in system */
+static LIST_HEAD(ti_sci_list);
+
+/**
+ * struct ti_sci_xfer - Structure representing a message flow
+ * @tx_message: Transmit message
+ * @rx_len: Receive message length
+ */
+struct ti_sci_xfer {
+ struct k3_sec_proxy_msg tx_message;
+ u8 rx_len;
+};
+
+/**
+ * struct ti_sci_rm_type_map - Structure representing TISCI Resource
+ * management representation of dev_ids.
+ * @dev_id: TISCI device ID
+ * @type: Corresponding id as identified by TISCI RM.
+ *
+ * Note: This is used only as a work around for using RM range apis
+ * for AM654 SoC. For future SoCs dev_id will be used as type
+ * for RM range APIs. In order to maintain ABI backward compatibility
+ * type is not being changed for AM654 SoC.
+ */
+struct ti_sci_rm_type_map {
+ u32 dev_id;
+ u16 type;
+};
+
+/**
+ * struct ti_sci_desc - Description of SoC integration
+ * @default_host_id: Host identifier representing the compute entity
+ * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
+ * @max_msgs: Maximum number of messages that can be pending
+ * simultaneously in the system
+ * @max_msg_size: Maximum size of data per message that can be handled.
+ */
+struct ti_sci_desc {
+ u8 default_host_id;
+ int max_rx_timeout_ms;
+ int max_msgs;
+ int max_msg_size;
+};
+
+/**
+ * struct ti_sci_info - Structure representing a TI SCI instance
+ * @dev: Device pointer
+ * @desc: SoC description for this instance
+ * @handle: Instance of TI SCI handle to send to clients.
+ * @chan_tx: Transmit mailbox channel
+ * @chan_rx: Receive mailbox channel
+ * @xfer: xfer info
+ * @list: list head
+ * @is_secure: Determines if the communication is through secure threads.
+ * @host_id: Host identifier representing the compute entity
+ * @seq: Seq id used for verification for tx and rx message.
+ */
+struct ti_sci_info {
+ struct device *dev;
+ const struct ti_sci_desc *desc;
+ struct ti_sci_handle handle;
+ struct mbox_chan *chan_tx;
+ struct mbox_chan *chan_rx;
+ struct mbox_chan *chan_notify;
+ struct ti_sci_xfer xfer;
+ struct list_head list;
+ struct list_head dev_list;
+ bool is_secure;
+ u32 host_id;
+ u8 seq;
+};
+
+struct ti_sci_exclusive_dev {
+ u32 id;
+ u32 count;
+ struct list_head list;
+};
+
+#define handle_to_ti_sci_info(h) container_of(h, struct ti_sci_info, handle)
+
+/**
+ * ti_sci_setup_one_xfer() - Setup one message type
+ * @info: Pointer to SCI entity information
+ * @msg_type: Message type
+ * @msg_flags: Flag to set for the message
+ * @buf: Buffer to be send to mailbox channel
+ * @tx_message_size: transmit message size
+ * @rx_message_size: receive message size. may be set to zero for send-only
+ * transactions.
+ *
+ * Helper function which is used by various command functions that are
+ * exposed to clients of this driver for allocating a message traffic event.
+ *
+ * Return: Corresponding ti_sci_xfer pointer if all went fine,
+ * else appropriate error pointer.
+ */
+static struct ti_sci_xfer *ti_sci_setup_one_xfer(struct ti_sci_info *info,
+ u16 msg_type, u32 msg_flags,
+ u32 *buf,
+ size_t tx_message_size,
+ size_t rx_message_size)
+{
+ struct ti_sci_xfer *xfer = &info->xfer;
+ struct ti_sci_msg_hdr *hdr;
+
+ /* Ensure we have sane transfer sizes */
+ if (rx_message_size > info->desc->max_msg_size ||
+ tx_message_size > info->desc->max_msg_size ||
+ (rx_message_size > 0 && rx_message_size < sizeof(*hdr)) ||
+ tx_message_size < sizeof(*hdr)) {
+ dev_err(info->dev, "TI-SCI message transfer size not sane\n");
+ return ERR_PTR(-ERANGE);
+ }
+
+
+ info->seq = ~info->seq;
+ xfer->tx_message.buf = buf;
+ xfer->tx_message.len = tx_message_size;
+ xfer->rx_len = (u8)rx_message_size;
+
+ hdr = (struct ti_sci_msg_hdr *)buf;
+ hdr->seq = info->seq;
+ hdr->type = msg_type;
+ hdr->host = info->host_id;
+ hdr->flags = msg_flags;
+
+ return xfer;
+}
+
+/**
+ * ti_sci_get_response() - Receive response from mailbox channel
+ * @info: Pointer to SCI entity information
+ * @xfer: Transfer to initiate and wait for response
+ * @chan: Channel to receive the response
+ *
+ * Return: -ETIMEDOUT in case of no response, if transmit error,
+ * return corresponding error, else if all goes well,
+ * return 0.
+ */
+static int ti_sci_get_response(struct ti_sci_info *info,
+ struct ti_sci_xfer *xfer,
+ struct mbox_chan *chan)
+{
+ struct k3_sec_proxy_msg *msg = &xfer->tx_message;
+ struct ti_sci_secure_msg_hdr *secure_hdr;
+ struct ti_sci_msg_hdr *hdr;
+ int ret;
+
+ /* Receive the response */
+ ret = mbox_recv(chan, msg, info->desc->max_rx_timeout_ms * 1000);
+ if (ret) {
+ dev_err(info->dev, "%s: Message receive failed. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* ToDo: Verify checksum */
+ if (info->is_secure) {
+ secure_hdr = (struct ti_sci_secure_msg_hdr *)msg->buf;
+ msg->buf = (u32 *)((void *)msg->buf + sizeof(*secure_hdr));
+ }
+
+ /* msg is updated by mailbox driver */
+ hdr = (struct ti_sci_msg_hdr *)msg->buf;
+
+ /* Sanity check for message response */
+ if (hdr->seq != info->seq) {
+ dev_dbg(info->dev, "%s: Message for %d is not expected\n",
+ __func__, hdr->seq);
+ return ret;
+ }
+
+ if (msg->len > info->desc->max_msg_size) {
+ dev_err(info->dev, "%s: Unable to handle %zu xfer (max %d)\n",
+ __func__, msg->len, info->desc->max_msg_size);
+ return -EINVAL;
+ }
+
+ if (msg->len < xfer->rx_len) {
+ dev_err(info->dev, "%s: Recv xfer %zu < expected %d length\n",
+ __func__, msg->len, xfer->rx_len);
+ }
+
+ return ret;
+}
+
+/**
+ * ti_sci_is_response_ack() - Generic ACK/NACK message checkup
+ * @r: pointer to response buffer
+ *
+ * Return: true if the response was an ACK, else returns false.
+ */
+static bool ti_sci_is_response_ack(void *r)
+{
+ struct ti_sci_msg_hdr *hdr = r;
+
+ return hdr->flags & TI_SCI_FLAG_RESP_GENERIC_ACK ? true : false;
+}
+
+/**
+ * ti_sci_do_xfer() - Do one transfer
+ * @info: Pointer to SCI entity information
+ * @xfer: Transfer to initiate and wait for response
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_do_xfer(struct ti_sci_info *info,
+ struct ti_sci_xfer *xfer)
+{
+ struct k3_sec_proxy_msg *msg = &xfer->tx_message;
+ u8 secure_buf[info->desc->max_msg_size];
+ struct ti_sci_secure_msg_hdr secure_hdr;
+ int ret;
+
+ if (info->is_secure) {
+ /* ToDo: get checksum of the entire message */
+ secure_hdr.checksum = 0;
+ secure_hdr.reserved = 0;
+ memcpy(&secure_buf[sizeof(secure_hdr)], xfer->tx_message.buf,
+ xfer->tx_message.len);
+
+ xfer->tx_message.buf = (u32 *)secure_buf;
+ xfer->tx_message.len += sizeof(secure_hdr);
+
+ if (xfer->rx_len)
+ xfer->rx_len += sizeof(secure_hdr);
+ }
+
+ /* Send the message */
+ ret = mbox_send(info->chan_tx, msg);
+ if (ret) {
+ dev_err(info->dev, "%s: Message sending failed. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Get response if requested */
+ if (xfer->rx_len) {
+ ret = ti_sci_get_response(info, xfer, info->chan_rx);
+ if (!ti_sci_is_response_ack(xfer->tx_message.buf)) {
+ dev_err(info->dev, "Message not acknowledged\n");
+ ret = -ENODEV;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_revision() - command to get the revision of the SCI entity
+ * @handle: pointer to TI SCI handle
+ *
+ * Updates the SCI information in the internal data structure.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_get_revision(struct ti_sci_handle *handle)
+{
+ struct ti_sci_msg_resp_version *rev_info;
+ struct ti_sci_version_info *ver;
+ struct ti_sci_msg_hdr hdr;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_VERSION,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&hdr, sizeof(struct ti_sci_msg_hdr),
+ sizeof(*rev_info));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ rev_info = (struct ti_sci_msg_resp_version *)xfer->tx_message.buf;
+
+ ver = &handle->version;
+ ver->abi_major = rev_info->abi_major;
+ ver->abi_minor = rev_info->abi_minor;
+ ver->firmware_revision = rev_info->firmware_revision;
+ strncpy(ver->firmware_description, rev_info->firmware_description,
+ sizeof(ver->firmware_description));
+
+ return 0;
+}
+
+/**
+ * cmd_set_board_config_using_msg() - Common command to send board configuration
+ * message
+ * @handle: pointer to TI SCI handle
+ * @msg_type: One of the TISCI message types to set board configuration
+ * @addr: Address where the board config structure is located
+ * @size: Size of the board config structure
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int cmd_set_board_config_using_msg(const struct ti_sci_handle *handle,
+ u16 msg_type, u64 addr, u32 size)
+{
+ struct ti_sci_msg_board_config req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, msg_type,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.boardcfgp_high = (addr >> 32) & 0xffffffff;
+ req.boardcfgp_low = addr & 0xffffffff;
+ req.boardcfg_size = size;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_set_board_config() - Command to send board configuration message
+ * @handle: pointer to TI SCI handle
+ * @addr: Address where the board config structure is located
+ * @size: Size of the board config structure
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_set_board_config(const struct ti_sci_handle *handle,
+ u64 addr, u32 size)
+{
+ return cmd_set_board_config_using_msg(handle,
+ TI_SCI_MSG_BOARD_CONFIG,
+ addr, size);
+}
+
+/**
+ * ti_sci_cmd_set_board_config_rm() - Command to send board resource
+ * management configuration
+ * @handle: pointer to TI SCI handle
+ * @addr: Address where the board RM config structure is located
+ * @size: Size of the RM config structure
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static
+int ti_sci_cmd_set_board_config_rm(const struct ti_sci_handle *handle,
+ u64 addr, u32 size)
+{
+ return cmd_set_board_config_using_msg(handle,
+ TI_SCI_MSG_BOARD_CONFIG_RM,
+ addr, size);
+}
+
+/**
+ * ti_sci_cmd_set_board_config_security() - Command to send board security
+ * configuration message
+ * @handle: pointer to TI SCI handle
+ * @addr: Address where the board security config structure is located
+ * @size: Size of the security config structure
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static
+int ti_sci_cmd_set_board_config_security(const struct ti_sci_handle *handle,
+ u64 addr, u32 size)
+{
+ return cmd_set_board_config_using_msg(handle,
+ TI_SCI_MSG_BOARD_CONFIG_SECURITY,
+ addr, size);
+}
+
+/**
+ * ti_sci_cmd_set_board_config_pm() - Command to send board power and clock
+ * configuration message
+ * @handle: pointer to TI SCI handle
+ * @addr: Address where the board PM config structure is located
+ * @size: Size of the PM config structure
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_set_board_config_pm(const struct ti_sci_handle *handle,
+ u64 addr, u32 size)
+{
+ return cmd_set_board_config_using_msg(handle,
+ TI_SCI_MSG_BOARD_CONFIG_PM,
+ addr, size);
+}
+
+static struct ti_sci_exclusive_dev
+*ti_sci_get_exclusive_dev(struct list_head *dev_list, u32 id)
+{
+ struct ti_sci_exclusive_dev *dev;
+
+ list_for_each_entry(dev, dev_list, list)
+ if (dev->id == id)
+ return dev;
+
+ return NULL;
+}
+
+static void ti_sci_add_exclusive_dev(struct ti_sci_info *info, u32 id)
+{
+ struct ti_sci_exclusive_dev *dev;
+
+ dev = ti_sci_get_exclusive_dev(&info->dev_list, id);
+ if (dev) {
+ dev->count++;
+ return;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev->id = id;
+ dev->count = 1;
+ INIT_LIST_HEAD(&dev->list);
+ list_add_tail(&dev->list, &info->dev_list);
+}
+
+static void ti_sci_delete_exclusive_dev(struct ti_sci_info *info, u32 id)
+{
+ struct ti_sci_exclusive_dev *dev;
+
+ dev = ti_sci_get_exclusive_dev(&info->dev_list, id);
+ if (!dev)
+ return;
+
+ if (dev->count > 0)
+ dev->count--;
+}
+
+/**
+ * ti_sci_set_device_state() - Set device state helper
+ * @handle: pointer to TI SCI handle
+ * @id: Device identifier
+ * @flags: flags to setup for the device
+ * @state: State to move the device to
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_set_device_state(const struct ti_sci_handle *handle,
+ u32 id, u32 flags, u8 state)
+{
+ struct ti_sci_msg_req_set_device_state req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_DEVICE_STATE,
+ flags | TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.id = id;
+ req.state = state;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ if (state == MSG_DEVICE_SW_STATE_AUTO_OFF)
+ ti_sci_delete_exclusive_dev(info, id);
+ else if (flags & MSG_FLAG_DEVICE_EXCLUSIVE)
+ ti_sci_add_exclusive_dev(info, id);
+
+ return ret;
+}
+
+/**
+ * ti_sci_set_device_state_no_wait() - Set device state helper without
+ * requesting or waiting for a response.
+ * @handle: pointer to TI SCI handle
+ * @id: Device identifier
+ * @flags: flags to setup for the device
+ * @state: State to move the device to
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_set_device_state_no_wait(const struct ti_sci_handle *handle,
+ u32 id, u32 flags, u8 state)
+{
+ struct ti_sci_msg_req_set_device_state req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_DEVICE_STATE,
+ flags | TI_SCI_FLAG_REQ_GENERIC_NORESPONSE,
+ (u32 *)&req, sizeof(req), 0);
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.id = id;
+ req.state = state;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_get_device_state() - Get device state helper
+ * @handle: Handle to the device
+ * @id: Device Identifier
+ * @clcnt: Pointer to Context Loss Count
+ * @resets: pointer to resets
+ * @p_state: pointer to p_state
+ * @c_state: pointer to c_state
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_get_device_state(const struct ti_sci_handle *handle,
+ u32 id, u32 *clcnt, u32 *resets,
+ u8 *p_state, u8 *c_state)
+{
+ struct ti_sci_msg_resp_get_device_state *resp;
+ struct ti_sci_msg_req_get_device_state req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ if (!clcnt && !resets && !p_state && !c_state)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_DEVICE_STATE,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.id = id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_get_device_state *)xfer->tx_message.buf;
+
+ if (clcnt)
+ *clcnt = resp->context_loss_count;
+ if (resets)
+ *resets = resp->resets;
+ if (p_state)
+ *p_state = resp->programmed_state;
+ if (c_state)
+ *c_state = resp->current_state;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_device() - command to request for device managed by TISCI
+ * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
+ * @id: Device Identifier
+ *
+ * Request for the device - NOTE: the client MUST maintain integrity of
+ * usage count by balancing get_device with put_device. No refcounting is
+ * managed by driver for that purpose.
+ *
+ * NOTE: The request is for exclusive access for the processor.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id)
+{
+ return ti_sci_set_device_state(handle, id, 0,
+ MSG_DEVICE_SW_STATE_ON);
+}
+
+static int ti_sci_cmd_get_device_exclusive(const struct ti_sci_handle *handle,
+ u32 id)
+{
+ return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE,
+ MSG_DEVICE_SW_STATE_ON);
+}
+
+/**
+ * ti_sci_cmd_idle_device() - Command to idle a device managed by TISCI
+ * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
+ * @id: Device Identifier
+ *
+ * Request for the device - NOTE: the client MUST maintain integrity of
+ * usage count by balancing get_device with put_device. No refcounting is
+ * managed by driver for that purpose.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_idle_device(const struct ti_sci_handle *handle, u32 id)
+{
+ return ti_sci_set_device_state(handle, id,
+ 0,
+ MSG_DEVICE_SW_STATE_RETENTION);
+}
+
+static int ti_sci_cmd_idle_device_exclusive(const struct ti_sci_handle *handle,
+ u32 id)
+{
+ return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE,
+ MSG_DEVICE_SW_STATE_RETENTION);
+}
+
+/**
+ * ti_sci_cmd_put_device() - command to release a device managed by TISCI
+ * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
+ * @id: Device Identifier
+ *
+ * Request for the device - NOTE: the client MUST maintain integrity of
+ * usage count by balancing get_device with put_device. No refcounting is
+ * managed by driver for that purpose.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_put_device(const struct ti_sci_handle *handle, u32 id)
+{
+ return ti_sci_set_device_state(handle, id, 0,
+ MSG_DEVICE_SW_STATE_AUTO_OFF);
+}
+
+static
+int ti_sci_cmd_release_exclusive_devices(const struct ti_sci_handle *handle)
+{
+ struct ti_sci_exclusive_dev *dev, *tmp;
+ struct ti_sci_info *info;
+ int i, cnt;
+
+ info = handle_to_ti_sci_info(handle);
+
+ list_for_each_entry_safe(dev, tmp, &info->dev_list, list) {
+ cnt = dev->count;
+ debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt);
+ for (i = 0; i < cnt; i++)
+ ti_sci_cmd_put_device(handle, dev->id);
+ }
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_dev_is_valid() - Is the device valid
+ * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
+ * @id: Device Identifier
+ *
+ * Return: 0 if all went fine and the device ID is valid, else return
+ * appropriate error.
+ */
+static int ti_sci_cmd_dev_is_valid(const struct ti_sci_handle *handle, u32 id)
+{
+ u8 unused;
+
+ /* check the device state which will also tell us if the ID is valid */
+ return ti_sci_get_device_state(handle, id, NULL, NULL, NULL, &unused);
+}
+
+/**
+ * ti_sci_cmd_dev_get_clcnt() - Get context loss counter
+ * @handle: Pointer to TISCI handle
+ * @id: Device Identifier
+ * @count: Pointer to Context Loss counter to populate
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_dev_get_clcnt(const struct ti_sci_handle *handle, u32 id,
+ u32 *count)
+{
+ return ti_sci_get_device_state(handle, id, count, NULL, NULL, NULL);
+}
+
+/**
+ * ti_sci_cmd_dev_is_idle() - Check if the device is requested to be idle
+ * @handle: Pointer to TISCI handle
+ * @id: Device Identifier
+ * @r_state: true if requested to be idle
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_dev_is_idle(const struct ti_sci_handle *handle, u32 id,
+ bool *r_state)
+{
+ int ret;
+ u8 state;
+
+ if (!r_state)
+ return -EINVAL;
+
+ ret = ti_sci_get_device_state(handle, id, NULL, NULL, &state, NULL);
+ if (ret)
+ return ret;
+
+ *r_state = (state == MSG_DEVICE_SW_STATE_RETENTION);
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_dev_is_stop() - Check if the device is requested to be stopped
+ * @handle: Pointer to TISCI handle
+ * @id: Device Identifier
+ * @r_state: true if requested to be stopped
+ * @curr_state: true if currently stopped.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_dev_is_stop(const struct ti_sci_handle *handle, u32 id,
+ bool *r_state, bool *curr_state)
+{
+ int ret;
+ u8 p_state, c_state;
+
+ if (!r_state && !curr_state)
+ return -EINVAL;
+
+ ret =
+ ti_sci_get_device_state(handle, id, NULL, NULL, &p_state, &c_state);
+ if (ret)
+ return ret;
+
+ if (r_state)
+ *r_state = (p_state == MSG_DEVICE_SW_STATE_AUTO_OFF);
+ if (curr_state)
+ *curr_state = (c_state == MSG_DEVICE_HW_STATE_OFF);
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_dev_is_on() - Check if the device is requested to be ON
+ * @handle: Pointer to TISCI handle
+ * @id: Device Identifier
+ * @r_state: true if requested to be ON
+ * @curr_state: true if currently ON and active
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_dev_is_on(const struct ti_sci_handle *handle, u32 id,
+ bool *r_state, bool *curr_state)
+{
+ int ret;
+ u8 p_state, c_state;
+
+ if (!r_state && !curr_state)
+ return -EINVAL;
+
+ ret =
+ ti_sci_get_device_state(handle, id, NULL, NULL, &p_state, &c_state);
+ if (ret)
+ return ret;
+
+ if (r_state)
+ *r_state = (p_state == MSG_DEVICE_SW_STATE_ON);
+ if (curr_state)
+ *curr_state = (c_state == MSG_DEVICE_HW_STATE_ON);
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_dev_is_trans() - Check if the device is currently transitioning
+ * @handle: Pointer to TISCI handle
+ * @id: Device Identifier
+ * @curr_state: true if currently transitioning.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_dev_is_trans(const struct ti_sci_handle *handle, u32 id,
+ bool *curr_state)
+{
+ int ret;
+ u8 state;
+
+ if (!curr_state)
+ return -EINVAL;
+
+ ret = ti_sci_get_device_state(handle, id, NULL, NULL, NULL, &state);
+ if (ret)
+ return ret;
+
+ *curr_state = (state == MSG_DEVICE_HW_STATE_TRANS);
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_set_device_resets() - command to set resets for device managed
+ * by TISCI
+ * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
+ * @id: Device Identifier
+ * @reset_state: Device specific reset bit field
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_set_device_resets(const struct ti_sci_handle *handle,
+ u32 id, u32 reset_state)
+{
+ struct ti_sci_msg_req_set_device_resets req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_DEVICE_RESETS,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.id = id;
+ req.resets = reset_state;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_device_resets() - Get reset state for device managed
+ * by TISCI
+ * @handle: Pointer to TISCI handle
+ * @id: Device Identifier
+ * @reset_state: Pointer to reset state to populate
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_get_device_resets(const struct ti_sci_handle *handle,
+ u32 id, u32 *reset_state)
+{
+ return ti_sci_get_device_state(handle, id, NULL, reset_state, NULL,
+ NULL);
+}
+
+/**
+ * ti_sci_set_clock_state() - Set clock state helper
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @flags: Header flags as needed
+ * @state: State to request for the clock.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_set_clock_state(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id,
+ u32 flags, u8 state)
+{
+ struct ti_sci_msg_req_set_clock_state req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_CLOCK_STATE,
+ flags | TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+ req.request_state = state;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_clock_state() - Get clock state helper
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @programmed_state: State requested for clock to move to
+ * @current_state: State that the clock is currently in
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_get_clock_state(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id,
+ u8 *programmed_state, u8 *current_state)
+{
+ struct ti_sci_msg_resp_get_clock_state *resp;
+ struct ti_sci_msg_req_get_clock_state req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ if (!programmed_state && !current_state)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_CLOCK_STATE,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_get_clock_state *)xfer->tx_message.buf;
+
+ if (programmed_state)
+ *programmed_state = resp->programmed_state;
+ if (current_state)
+ *current_state = resp->current_state;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_clock() - Get control of a clock from TI SCI
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @needs_ssc: 'true' if Spread Spectrum clock is desired, else 'false'
+ * @can_change_freq: 'true' if frequency change is desired, else 'false'
+ * @enable_input_term: 'true' if input termination is desired, else 'false'
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_get_clock(const struct ti_sci_handle *handle, u32 dev_id,
+ u8 clk_id, bool needs_ssc, bool can_change_freq,
+ bool enable_input_term)
+{
+ u32 flags = 0;
+
+ flags |= needs_ssc ? MSG_FLAG_CLOCK_ALLOW_SSC : 0;
+ flags |= can_change_freq ? MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE : 0;
+ flags |= enable_input_term ? MSG_FLAG_CLOCK_INPUT_TERM : 0;
+
+ return ti_sci_set_clock_state(handle, dev_id, clk_id, flags,
+ MSG_CLOCK_SW_STATE_REQ);
+}
+
+/**
+ * ti_sci_cmd_idle_clock() - Idle a clock which is in our control
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ *
+ * NOTE: This clock must have been requested by get_clock previously.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_idle_clock(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id)
+{
+ return ti_sci_set_clock_state(handle, dev_id, clk_id, 0,
+ MSG_CLOCK_SW_STATE_UNREQ);
+}
+
+/**
+ * ti_sci_cmd_put_clock() - Release a clock from our control back to TISCI
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ *
+ * NOTE: This clock must have been requested by get_clock previously.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_put_clock(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id)
+{
+ return ti_sci_set_clock_state(handle, dev_id, clk_id, 0,
+ MSG_CLOCK_SW_STATE_AUTO);
+}
+
+/**
+ * ti_sci_cmd_clk_is_auto() - Is the clock being auto managed
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @req_state: state indicating if the clock is auto managed
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_is_auto(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id, bool *req_state)
+{
+ u8 state = 0;
+ int ret;
+
+ if (!req_state)
+ return -EINVAL;
+
+ ret = ti_sci_cmd_get_clock_state(handle, dev_id, clk_id, &state, NULL);
+ if (ret)
+ return ret;
+
+ *req_state = (state == MSG_CLOCK_SW_STATE_AUTO);
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_clk_is_on() - Is the clock ON
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @req_state: state indicating if the clock is managed by us and enabled
+ * @curr_state: state indicating if the clock is ready for operation
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_is_on(const struct ti_sci_handle *handle, u32 dev_id,
+ u8 clk_id, bool *req_state, bool *curr_state)
+{
+ u8 c_state = 0, r_state = 0;
+ int ret;
+
+ if (!req_state && !curr_state)
+ return -EINVAL;
+
+ ret = ti_sci_cmd_get_clock_state(handle, dev_id, clk_id,
+ &r_state, &c_state);
+ if (ret)
+ return ret;
+
+ if (req_state)
+ *req_state = (r_state == MSG_CLOCK_SW_STATE_REQ);
+ if (curr_state)
+ *curr_state = (c_state == MSG_CLOCK_HW_STATE_READY);
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_clk_is_off() - Is the clock OFF
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @req_state: state indicating if the clock is managed by us and disabled
+ * @curr_state: state indicating if the clock is NOT ready for operation
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_is_off(const struct ti_sci_handle *handle, u32 dev_id,
+ u8 clk_id, bool *req_state, bool *curr_state)
+{
+ u8 c_state = 0, r_state = 0;
+ int ret;
+
+ if (!req_state && !curr_state)
+ return -EINVAL;
+
+ ret = ti_sci_cmd_get_clock_state(handle, dev_id, clk_id,
+ &r_state, &c_state);
+ if (ret)
+ return ret;
+
+ if (req_state)
+ *req_state = (r_state == MSG_CLOCK_SW_STATE_UNREQ);
+ if (curr_state)
+ *curr_state = (c_state == MSG_CLOCK_HW_STATE_NOT_READY);
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_clk_set_parent() - Set the clock source of a specific device clock
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @parent_id: Parent clock identifier to set
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_set_parent(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id, u8 parent_id)
+{
+ struct ti_sci_msg_req_set_clock_parent req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_CLOCK_PARENT,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+ req.parent_id = parent_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_clk_get_parent() - Get current parent clock source
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @parent_id: Current clock parent
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_get_parent(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id, u8 *parent_id)
+{
+ struct ti_sci_msg_resp_get_clock_parent *resp;
+ struct ti_sci_msg_req_get_clock_parent req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle || !parent_id)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_CLOCK_PARENT,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_get_clock_parent *)xfer->tx_message.buf;
+
+ *parent_id = resp->parent_id;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_clk_get_num_parents() - Get num parents of the current clk source
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @num_parents: Returns he number of parents to the current clock.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_get_num_parents(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id,
+ u8 *num_parents)
+{
+ struct ti_sci_msg_resp_get_clock_num_parents *resp;
+ struct ti_sci_msg_req_get_clock_num_parents req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle || !num_parents)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_NUM_CLOCK_PARENTS,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_get_clock_num_parents *)
+ xfer->tx_message.buf;
+
+ *num_parents = resp->num_parents;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_clk_get_match_freq() - Find a good match for frequency
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @min_freq: The minimum allowable frequency in Hz. This is the minimum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @target_freq: The target clock frequency in Hz. A frequency will be
+ * processed as close to this target frequency as possible.
+ * @max_freq: The maximum allowable frequency in Hz. This is the maximum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @match_freq: Frequency match in Hz response.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_get_match_freq(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id, u64 min_freq,
+ u64 target_freq, u64 max_freq,
+ u64 *match_freq)
+{
+ struct ti_sci_msg_resp_query_clock_freq *resp;
+ struct ti_sci_msg_req_query_clock_freq req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle || !match_freq)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_QUERY_CLOCK_FREQ,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+ req.min_freq_hz = min_freq;
+ req.target_freq_hz = target_freq;
+ req.max_freq_hz = max_freq;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_query_clock_freq *)xfer->tx_message.buf;
+
+ *match_freq = resp->freq_hz;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_clk_set_freq() - Set a frequency for clock
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @min_freq: The minimum allowable frequency in Hz. This is the minimum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @target_freq: The target clock frequency in Hz. A frequency will be
+ * processed as close to this target frequency as possible.
+ * @max_freq: The maximum allowable frequency in Hz. This is the maximum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_set_freq(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id, u64 min_freq,
+ u64 target_freq, u64 max_freq)
+{
+ struct ti_sci_msg_req_set_clock_freq req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_CLOCK_FREQ,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+ req.min_freq_hz = min_freq;
+ req.target_freq_hz = target_freq;
+ req.max_freq_hz = max_freq;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_clk_get_freq() - Get current frequency
+ * @handle: pointer to TI SCI handle
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @freq: Currently frequency in Hz
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_clk_get_freq(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 clk_id, u64 *freq)
+{
+ struct ti_sci_msg_resp_get_clock_freq *resp;
+ struct ti_sci_msg_req_get_clock_freq req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle || !freq)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_CLOCK_FREQ,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.dev_id = dev_id;
+ req.clk_id = clk_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_get_clock_freq *)xfer->tx_message.buf;
+
+ *freq = resp->freq_hz;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_core_reboot() - Command to request system reset
+ * @handle: pointer to TI SCI handle
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_core_reboot(const struct ti_sci_handle *handle)
+{
+ struct ti_sci_msg_req_reboot req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SYS_RESET,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.domain = 0;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_get_resource_range - Helper to get a range of resources assigned
+ * to a host. Resource is uniquely identified by
+ * type and subtype.
+ * @handle: Pointer to TISCI handle.
+ * @dev_id: TISCI device ID.
+ * @subtype: Resource assignment subtype that is being requested
+ * from the given device.
+ * @s_host: Host processor ID to which the resources are allocated
+ * @range_start: Start index of the resource range
+ * @range_num: Number of resources in the range
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_get_resource_range(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 subtype, u8 s_host,
+ u16 *range_start, u16 *range_num)
+{
+ struct ti_sci_msg_resp_get_resource_range *resp;
+ struct ti_sci_msg_req_get_resource_range req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_GET_RESOURCE_RANGE,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ req.secondary_host = s_host;
+ req.type = dev_id & MSG_RM_RESOURCE_TYPE_MASK;
+ req.subtype = subtype & MSG_RM_RESOURCE_SUBTYPE_MASK;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+ resp = (struct ti_sci_msg_resp_get_resource_range *)xfer->tx_message.buf;
+ if (!resp->range_start && !resp->range_num) {
+ ret = -ENODEV;
+ } else {
+ *range_start = resp->range_start;
+ *range_num = resp->range_num;
+ };
+
+fail:
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_resource_range - Get a range of resources assigned to host
+ * that is same as ti sci interface host.
+ * @handle: Pointer to TISCI handle.
+ * @dev_id: TISCI device ID.
+ * @subtype: Resource assignment subtype that is being requested
+ * from the given device.
+ * @range_start: Start index of the resource range
+ * @range_num: Number of resources in the range
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 subtype,
+ u16 *range_start, u16 *range_num)
+{
+ return ti_sci_get_resource_range(handle, dev_id, subtype,
+ TI_SCI_IRQ_SECONDARY_HOST_INVALID,
+ range_start, range_num);
+}
+
+/**
+ * ti_sci_cmd_get_resource_range_from_shost - Get a range of resources
+ * assigned to a specified host.
+ * @handle: Pointer to TISCI handle.
+ * @dev_id: TISCI device ID.
+ * @subtype: Resource assignment subtype that is being requested
+ * from the given device.
+ * @s_host: Host processor ID to which the resources are allocated
+ * @range_start: Start index of the resource range
+ * @range_num: Number of resources in the range
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static
+int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle,
+ u32 dev_id, u8 subtype, u8 s_host,
+ u16 *range_start, u16 *range_num)
+{
+ return ti_sci_get_resource_range(handle, dev_id, subtype, s_host,
+ range_start, range_num);
+}
+
+/**
+ * ti_sci_cmd_query_msmc() - Command to query currently available msmc memory
+ * @handle: pointer to TI SCI handle
+ * @msms_start: MSMC start as returned by tisci
+ * @msmc_end: MSMC end as returned by tisci
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_query_msmc(const struct ti_sci_handle *handle,
+ u64 *msmc_start, u64 *msmc_end)
+{
+ struct ti_sci_msg_resp_query_msmc *resp;
+ struct ti_sci_msg_hdr req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_QUERY_MSMC,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_query_msmc *)xfer->tx_message.buf;
+
+ *msmc_start = ((u64)resp->msmc_start_high << TISCI_ADDR_HIGH_SHIFT) |
+ resp->msmc_start_low;
+ *msmc_end = ((u64)resp->msmc_end_high << TISCI_ADDR_HIGH_SHIFT) |
+ resp->msmc_end_low;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_proc_request() - Command to request a physical processor control
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_proc_request(const struct ti_sci_handle *handle,
+ u8 proc_id)
+{
+ struct ti_sci_msg_req_proc_request req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_PROC_REQUEST,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_proc_release() - Command to release a physical processor control
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_proc_release(const struct ti_sci_handle *handle,
+ u8 proc_id)
+{
+ struct ti_sci_msg_req_proc_release req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_PROC_RELEASE,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_proc_handover() - Command to handover a physical processor
+ * control to a host in the processor's access
+ * control list.
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ * @host_id: Host ID to get the control of the processor
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_proc_handover(const struct ti_sci_handle *handle,
+ u8 proc_id, u8 host_id)
+{
+ struct ti_sci_msg_req_proc_handover req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_PROC_HANDOVER,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+ req.host_id = host_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_set_proc_boot_cfg() - Command to set the processor boot
+ * configuration flags
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ * @config_flags_set: Configuration flags to be set
+ * @config_flags_clear: Configuration flags to be cleared.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_set_proc_boot_cfg(const struct ti_sci_handle *handle,
+ u8 proc_id, u64 bootvector,
+ u32 config_flags_set,
+ u32 config_flags_clear)
+{
+ struct ti_sci_msg_req_set_proc_boot_config req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_SET_PROC_BOOT_CONFIG,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+ req.bootvector_low = bootvector & TISCI_ADDR_LOW_MASK;
+ req.bootvector_high = (bootvector & TISCI_ADDR_HIGH_MASK) >>
+ TISCI_ADDR_HIGH_SHIFT;
+ req.config_flags_set = config_flags_set;
+ req.config_flags_clear = config_flags_clear;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_set_proc_boot_ctrl() - Command to set the processor boot
+ * control flags
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ * @control_flags_set: Control flags to be set
+ * @control_flags_clear: Control flags to be cleared
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_set_proc_boot_ctrl(const struct ti_sci_handle *handle,
+ u8 proc_id, u32 control_flags_set,
+ u32 control_flags_clear)
+{
+ struct ti_sci_msg_req_set_proc_boot_ctrl req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_SET_PROC_BOOT_CTRL,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+ req.control_flags_set = control_flags_set;
+ req.control_flags_clear = control_flags_clear;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_proc_auth_boot_image() - Command to authenticate and load the
+ * image and then set the processor configuration flags.
+ * @handle: Pointer to TI SCI handle
+ * @image_addr: Memory address at which payload image and certificate is
+ * located in memory, this is updated if the image data is
+ * moved during authentication.
+ * @image_size: This is updated with the final size of the image after
+ * authentication.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_proc_auth_boot_image(const struct ti_sci_handle *handle,
+ u64 *image_addr, u32 *image_size)
+{
+ struct ti_sci_msg_req_proc_auth_boot_image req;
+ struct ti_sci_msg_resp_proc_auth_boot_image *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_PROC_AUTH_BOOT_IMAGE,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.cert_addr_low = *image_addr & TISCI_ADDR_LOW_MASK;
+ req.cert_addr_high = (*image_addr & TISCI_ADDR_HIGH_MASK) >>
+ TISCI_ADDR_HIGH_SHIFT;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_proc_auth_boot_image *)xfer->tx_message.buf;
+
+ *image_addr = (resp->image_addr_low & TISCI_ADDR_LOW_MASK) |
+ (((u64)resp->image_addr_high <<
+ TISCI_ADDR_HIGH_SHIFT) & TISCI_ADDR_HIGH_MASK);
+ *image_size = resp->image_size;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_get_proc_boot_status() - Command to get the processor boot status
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_get_proc_boot_status(const struct ti_sci_handle *handle,
+ u8 proc_id, u64 *bv, u32 *cfg_flags,
+ u32 *ctrl_flags, u32 *sts_flags)
+{
+ struct ti_sci_msg_resp_get_proc_boot_status *resp;
+ struct ti_sci_msg_req_get_proc_boot_status req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_GET_PROC_BOOT_STATUS,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_resp_get_proc_boot_status *)
+ xfer->tx_message.buf;
+
+ *bv = (resp->bootvector_low & TISCI_ADDR_LOW_MASK) |
+ (((u64)resp->bootvector_high <<
+ TISCI_ADDR_HIGH_SHIFT) & TISCI_ADDR_HIGH_MASK);
+ *cfg_flags = resp->config_flags;
+ *ctrl_flags = resp->control_flags;
+ *sts_flags = resp->status_flags;
+
+ return ret;
+}
+
+/**
+ * ti_sci_proc_wait_boot_status_no_wait() - Helper function to wait for a
+ * processor boot status without requesting or
+ * waiting for a response.
+ * @proc_id: Processor ID this request is for
+ * @num_wait_iterations: Total number of iterations we will check before
+ * we will timeout and give up
+ * @num_match_iterations: How many iterations should we have continued
+ * status to account for status bits glitching.
+ * This is to make sure that match occurs for
+ * consecutive checks. This implies that the
+ * worst case should consider that the stable
+ * time should at the worst be num_wait_iterations
+ * num_match_iterations to prevent timeout.
+ * @delay_per_iteration_us: Specifies how long to wait (in micro seconds)
+ * between each status checks. This is the minimum
+ * duration, and overhead of register reads and
+ * checks are on top of this and can vary based on
+ * varied conditions.
+ * @delay_before_iterations_us: Specifies how long to wait (in micro seconds)
+ * before the very first check in the first
+ * iteration of status check loop. This is the
+ * minimum duration, and overhead of register
+ * reads and checks are.
+ * @status_flags_1_set_all_wait:If non-zero, Specifies that all bits of the
+ * status matching this field requested MUST be 1.
+ * @status_flags_1_set_any_wait:If non-zero, Specifies that at least one of the
+ * bits matching this field requested MUST be 1.
+ * @status_flags_1_clr_all_wait:If non-zero, Specifies that all bits of the
+ * status matching this field requested MUST be 0.
+ * @status_flags_1_clr_any_wait:If non-zero, Specifies that at least one of the
+ * bits matching this field requested MUST be 0.
+ *
+ * Return: 0 if all goes well, else appropriate error message
+ */
+static int
+ti_sci_proc_wait_boot_status_no_wait(const struct ti_sci_handle *handle,
+ u8 proc_id,
+ u8 num_wait_iterations,
+ u8 num_match_iterations,
+ u8 delay_per_iteration_us,
+ u8 delay_before_iterations_us,
+ u32 status_flags_1_set_all_wait,
+ u32 status_flags_1_set_any_wait,
+ u32 status_flags_1_clr_all_wait,
+ u32 status_flags_1_clr_any_wait)
+{
+ struct ti_sci_msg_req_wait_proc_boot_status req;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_WAIT_PROC_BOOT_STATUS,
+ TI_SCI_FLAG_REQ_GENERIC_NORESPONSE,
+ (u32 *)&req, sizeof(req), 0);
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.processor_id = proc_id;
+ req.num_wait_iterations = num_wait_iterations;
+ req.num_match_iterations = num_match_iterations;
+ req.delay_per_iteration_us = delay_per_iteration_us;
+ req.delay_before_iterations_us = delay_before_iterations_us;
+ req.status_flags_1_set_all_wait = status_flags_1_set_all_wait;
+ req.status_flags_1_set_any_wait = status_flags_1_set_any_wait;
+ req.status_flags_1_clr_all_wait = status_flags_1_clr_all_wait;
+ req.status_flags_1_clr_any_wait = status_flags_1_clr_any_wait;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_proc_shutdown_no_wait() - Command to shutdown a core without
+ * requesting or waiting for a response. Note that this API call
+ * should be followed by placing the respective processor into
+ * either WFE or WFI mode.
+ * @handle: Pointer to TI SCI handle
+ * @proc_id: Processor ID this request is for
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_proc_shutdown_no_wait(const struct ti_sci_handle *handle,
+ u8 proc_id)
+{
+ int ret;
+ struct ti_sci_info *info;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ /*
+ * Send the core boot status wait message waiting for either WFE or
+ * WFI without requesting or waiting for a TISCI response with the
+ * maximum wait time to give us the best chance to get to the WFE/WFI
+ * command that should follow the invocation of this API before the
+ * DMSC-internal processing of this command times out. Note that
+ * waiting for the R5 WFE/WFI flags will also work on an ARMV8 type
+ * core as the related flag bit positions are the same.
+ */
+ ret = ti_sci_proc_wait_boot_status_no_wait(handle, proc_id,
+ U8_MAX, 100, U8_MAX, U8_MAX,
+ 0, PROC_BOOT_STATUS_FLAG_R5_WFE | PROC_BOOT_STATUS_FLAG_R5_WFI,
+ 0, 0);
+ if (ret) {
+ dev_err(info->dev, "Sending core %u wait message fail %d\n",
+ proc_id, ret);
+ return ret;
+ }
+
+ /*
+ * Release a processor managed by TISCI without requesting or waiting
+ * for a response.
+ */
+ ret = ti_sci_set_device_state_no_wait(handle, proc_id, 0,
+ MSG_DEVICE_SW_STATE_AUTO_OFF);
+ if (ret)
+ dev_err(info->dev, "Sending core %u shutdown message fail %d\n",
+ proc_id, ret);
+
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_ring_config() - configure RA ring
+ * @handle: pointer to TI SCI handle
+ * @valid_params: Bitfield defining validity of ring configuration parameters.
+ * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated
+ * @index: Ring index.
+ * @addr_lo: The ring base address lo 32 bits
+ * @addr_hi: The ring base address hi 32 bits
+ * @count: Number of ring elements.
+ * @mode: The mode of the ring
+ * @size: The ring element size.
+ * @order_id: Specifies the ring's bus order ID.
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ *
+ * See @ti_sci_msg_rm_ring_cfg_req for more info.
+ */
+static int ti_sci_cmd_ring_config(const struct ti_sci_handle *handle,
+ u32 valid_params, u16 nav_id, u16 index,
+ u32 addr_lo, u32 addr_hi, u32 count,
+ u8 mode, u8 size, u8 order_id)
+{
+ struct ti_sci_msg_rm_ring_cfg_resp *resp;
+ struct ti_sci_msg_rm_ring_cfg_req req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_RM_RING_CFG,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.valid_params = valid_params;
+ req.nav_id = nav_id;
+ req.index = index;
+ req.addr_lo = addr_lo;
+ req.addr_hi = addr_hi;
+ req.count = count;
+ req.mode = mode;
+ req.size = size;
+ req.order_id = order_id;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+fail:
+ dev_dbg(info->dev, "RM_RA:config ring %u ret:%d\n", index, ret);
+ return ret;
+}
+
+static int ti_sci_cmd_rm_psil_pair(const struct ti_sci_handle *handle,
+ u32 nav_id, u32 src_thread, u32 dst_thread)
+{
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_msg_psil_pair req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_RM_PSIL_PAIR,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.nav_id = nav_id;
+ req.src_thread = src_thread;
+ req.dst_thread = dst_thread;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+fail:
+ dev_dbg(info->dev, "RM_PSIL: nav: %u link pair %u->%u ret:%u\n",
+ nav_id, src_thread, dst_thread, ret);
+ return ret;
+}
+
+static int ti_sci_cmd_rm_psil_unpair(const struct ti_sci_handle *handle,
+ u32 nav_id, u32 src_thread, u32 dst_thread)
+{
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_msg_psil_unpair req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_RM_PSIL_UNPAIR,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.nav_id = nav_id;
+ req.src_thread = src_thread;
+ req.dst_thread = dst_thread;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+fail:
+ dev_dbg(info->dev, "RM_PSIL: link unpair %u->%u ret:%u\n",
+ src_thread, dst_thread, ret);
+ return ret;
+}
+
+static int ti_sci_cmd_rm_udmap_tx_ch_cfg(
+ const struct ti_sci_handle *handle,
+ const struct ti_sci_msg_rm_udmap_tx_ch_cfg *params)
+{
+ struct ti_sci_msg_rm_udmap_tx_ch_cfg_resp *resp;
+ struct ti_sci_msg_rm_udmap_tx_ch_cfg_req req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_RM_UDMAP_TX_CH_CFG,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+ req.valid_params = params->valid_params;
+ req.nav_id = params->nav_id;
+ req.index = params->index;
+ req.tx_pause_on_err = params->tx_pause_on_err;
+ req.tx_filt_einfo = params->tx_filt_einfo;
+ req.tx_filt_pswords = params->tx_filt_pswords;
+ req.tx_atype = params->tx_atype;
+ req.tx_chan_type = params->tx_chan_type;
+ req.tx_supr_tdpkt = params->tx_supr_tdpkt;
+ req.tx_fetch_size = params->tx_fetch_size;
+ req.tx_credit_count = params->tx_credit_count;
+ req.txcq_qnum = params->txcq_qnum;
+ req.tx_priority = params->tx_priority;
+ req.tx_qos = params->tx_qos;
+ req.tx_orderid = params->tx_orderid;
+ req.fdepth = params->fdepth;
+ req.tx_sched_priority = params->tx_sched_priority;
+ req.tx_burst_size = params->tx_burst_size;
+ req.tx_tdtype = params->tx_tdtype;
+ req.extended_ch_type = params->extended_ch_type;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+fail:
+ dev_dbg(info->dev, "TX_CH_CFG: chn %u ret:%u\n", params->index, ret);
+ return ret;
+}
+
+static int ti_sci_cmd_rm_udmap_rx_ch_cfg(
+ const struct ti_sci_handle *handle,
+ const struct ti_sci_msg_rm_udmap_rx_ch_cfg *params)
+{
+ struct ti_sci_msg_rm_udmap_rx_ch_cfg_resp *resp;
+ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_RM_UDMAP_RX_CH_CFG,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ req.valid_params = params->valid_params;
+ req.nav_id = params->nav_id;
+ req.index = params->index;
+ req.rx_fetch_size = params->rx_fetch_size;
+ req.rxcq_qnum = params->rxcq_qnum;
+ req.rx_priority = params->rx_priority;
+ req.rx_qos = params->rx_qos;
+ req.rx_orderid = params->rx_orderid;
+ req.rx_sched_priority = params->rx_sched_priority;
+ req.flowid_start = params->flowid_start;
+ req.flowid_cnt = params->flowid_cnt;
+ req.rx_pause_on_err = params->rx_pause_on_err;
+ req.rx_atype = params->rx_atype;
+ req.rx_chan_type = params->rx_chan_type;
+ req.rx_ignore_short = params->rx_ignore_short;
+ req.rx_ignore_long = params->rx_ignore_long;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+fail:
+ dev_dbg(info->dev, "RX_CH_CFG: chn %u ret:%d\n", params->index, ret);
+ return ret;
+}
+
+static int ti_sci_cmd_rm_udmap_rx_flow_cfg(
+ const struct ti_sci_handle *handle,
+ const struct ti_sci_msg_rm_udmap_flow_cfg *params)
+{
+ struct ti_sci_msg_rm_udmap_flow_cfg_resp *resp;
+ struct ti_sci_msg_rm_udmap_flow_cfg_req req;
+ struct ti_sci_xfer *xfer;
+ struct ti_sci_info *info;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_RM_UDMAP_FLOW_CFG,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ req.valid_params = params->valid_params;
+ req.nav_id = params->nav_id;
+ req.flow_index = params->flow_index;
+ req.rx_einfo_present = params->rx_einfo_present;
+ req.rx_psinfo_present = params->rx_psinfo_present;
+ req.rx_error_handling = params->rx_error_handling;
+ req.rx_desc_type = params->rx_desc_type;
+ req.rx_sop_offset = params->rx_sop_offset;
+ req.rx_dest_qnum = params->rx_dest_qnum;
+ req.rx_src_tag_hi = params->rx_src_tag_hi;
+ req.rx_src_tag_lo = params->rx_src_tag_lo;
+ req.rx_dest_tag_hi = params->rx_dest_tag_hi;
+ req.rx_dest_tag_lo = params->rx_dest_tag_lo;
+ req.rx_src_tag_hi_sel = params->rx_src_tag_hi_sel;
+ req.rx_src_tag_lo_sel = params->rx_src_tag_lo_sel;
+ req.rx_dest_tag_hi_sel = params->rx_dest_tag_hi_sel;
+ req.rx_dest_tag_lo_sel = params->rx_dest_tag_lo_sel;
+ req.rx_fdq0_sz0_qnum = params->rx_fdq0_sz0_qnum;
+ req.rx_fdq1_qnum = params->rx_fdq1_qnum;
+ req.rx_fdq2_qnum = params->rx_fdq2_qnum;
+ req.rx_fdq3_qnum = params->rx_fdq3_qnum;
+ req.rx_ps_location = params->rx_ps_location;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ goto fail;
+
+fail:
+ dev_dbg(info->dev, "RX_FL_CFG: %u ret:%d\n", params->flow_index, ret);
+ return ret;
+}
+
+/**
+ * ti_sci_cmd_set_fwl_region() - Request for configuring a firewall region
+ * @handle: pointer to TI SCI handle
+ * @region: region configuration parameters
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_set_fwl_region(const struct ti_sci_handle *handle,
+ const struct ti_sci_msg_fwl_region *region)
+{
+ struct ti_sci_msg_fwl_set_firewall_region_req req;
+ struct ti_sci_msg_hdr *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_FWL_SET,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ req.fwl_id = region->fwl_id;
+ req.region = region->region;
+ req.n_permission_regs = region->n_permission_regs;
+ req.control = region->control;
+ req.permissions[0] = region->permissions[0];
+ req.permissions[1] = region->permissions[1];
+ req.permissions[2] = region->permissions[2];
+ req.start_address = region->start_address;
+ req.end_address = region->end_address;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_get_fwl_region() - Request for getting a firewall region
+ * @handle: pointer to TI SCI handle
+ * @region: region configuration parameters
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_get_fwl_region(const struct ti_sci_handle *handle,
+ struct ti_sci_msg_fwl_region *region)
+{
+ struct ti_sci_msg_fwl_get_firewall_region_req req;
+ struct ti_sci_msg_fwl_get_firewall_region_resp *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_FWL_GET,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ req.fwl_id = region->fwl_id;
+ req.region = region->region;
+ req.n_permission_regs = region->n_permission_regs;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_fwl_get_firewall_region_resp *)xfer->tx_message.buf;
+
+ region->fwl_id = resp->fwl_id;
+ region->region = resp->region;
+ region->n_permission_regs = resp->n_permission_regs;
+ region->control = resp->control;
+ region->permissions[0] = resp->permissions[0];
+ region->permissions[1] = resp->permissions[1];
+ region->permissions[2] = resp->permissions[2];
+ region->start_address = resp->start_address;
+ region->end_address = resp->end_address;
+
+ return 0;
+}
+
+/**
+ * ti_sci_cmd_change_fwl_owner() - Request for changing a firewall owner
+ * @handle: pointer to TI SCI handle
+ * @region: region configuration parameters
+ *
+ * Return: 0 if all went well, else returns appropriate error value.
+ */
+static int ti_sci_cmd_change_fwl_owner(const struct ti_sci_handle *handle,
+ struct ti_sci_msg_fwl_owner *owner)
+{
+ struct ti_sci_msg_fwl_change_owner_info_req req;
+ struct ti_sci_msg_fwl_change_owner_info_resp *resp;
+ struct ti_sci_info *info;
+ struct ti_sci_xfer *xfer;
+ int ret = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_ti_sci_info(handle);
+
+ xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_FWL_CHANGE_OWNER,
+ TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
+ (u32 *)&req, sizeof(req), sizeof(*resp));
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ return ret;
+ }
+
+ req.fwl_id = owner->fwl_id;
+ req.region = owner->region;
+ req.owner_index = owner->owner_index;
+
+ ret = ti_sci_do_xfer(info, xfer);
+ if (ret)
+ return ret;
+
+ resp = (struct ti_sci_msg_fwl_change_owner_info_resp *)xfer->tx_message.buf;
+
+ owner->fwl_id = resp->fwl_id;
+ owner->region = resp->region;
+ owner->owner_index = resp->owner_index;
+ owner->owner_privid = resp->owner_privid;
+ owner->owner_permission_bits = resp->owner_permission_bits;
+
+ return ret;
+}
+
+static struct ti_sci_handle *g_handle;
+
+const struct ti_sci_handle *ti_sci_get_handle(struct device *dev)
+{
+ return g_handle;
+}
+
+/*
+ * ti_sci_setup_ops() - Setup the operations structures
+ * @info: pointer to TISCI pointer
+ */
+static void ti_sci_setup_ops(struct ti_sci_info *info)
+{
+ struct ti_sci_ops *ops = &info->handle.ops;
+ struct ti_sci_board_ops *bops = &ops->board_ops;
+ struct ti_sci_dev_ops *dops = &ops->dev_ops;
+ struct ti_sci_clk_ops *cops = &ops->clk_ops;
+ struct ti_sci_core_ops *core_ops = &ops->core_ops;
+ struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops;
+ struct ti_sci_proc_ops *pops = &ops->proc_ops;
+ struct ti_sci_rm_ringacc_ops *rops = &ops->rm_ring_ops;
+ struct ti_sci_rm_psil_ops *psilops = &ops->rm_psil_ops;
+ struct ti_sci_rm_udmap_ops *udmap_ops = &ops->rm_udmap_ops;
+ struct ti_sci_fwl_ops *fwl_ops = &ops->fwl_ops;
+
+ bops->board_config = ti_sci_cmd_set_board_config;
+ bops->board_config_rm = ti_sci_cmd_set_board_config_rm;
+ bops->board_config_security = ti_sci_cmd_set_board_config_security;
+ bops->board_config_pm = ti_sci_cmd_set_board_config_pm;
+
+ dops->get_device = ti_sci_cmd_get_device;
+ dops->get_device_exclusive = ti_sci_cmd_get_device_exclusive;
+ dops->idle_device = ti_sci_cmd_idle_device;
+ dops->idle_device_exclusive = ti_sci_cmd_idle_device_exclusive;
+ dops->put_device = ti_sci_cmd_put_device;
+ dops->is_valid = ti_sci_cmd_dev_is_valid;
+ dops->get_context_loss_count = ti_sci_cmd_dev_get_clcnt;
+ dops->is_idle = ti_sci_cmd_dev_is_idle;
+ dops->is_stop = ti_sci_cmd_dev_is_stop;
+ dops->is_on = ti_sci_cmd_dev_is_on;
+ dops->is_transitioning = ti_sci_cmd_dev_is_trans;
+ dops->set_device_resets = ti_sci_cmd_set_device_resets;
+ dops->get_device_resets = ti_sci_cmd_get_device_resets;
+ dops->release_exclusive_devices = ti_sci_cmd_release_exclusive_devices;
+
+ cops->get_clock = ti_sci_cmd_get_clock;
+ cops->idle_clock = ti_sci_cmd_idle_clock;
+ cops->put_clock = ti_sci_cmd_put_clock;
+ cops->is_auto = ti_sci_cmd_clk_is_auto;
+ cops->is_on = ti_sci_cmd_clk_is_on;
+ cops->is_off = ti_sci_cmd_clk_is_off;
+
+ cops->set_parent = ti_sci_cmd_clk_set_parent;
+ cops->get_parent = ti_sci_cmd_clk_get_parent;
+ cops->get_num_parents = ti_sci_cmd_clk_get_num_parents;
+
+ cops->get_best_match_freq = ti_sci_cmd_clk_get_match_freq;
+ cops->set_freq = ti_sci_cmd_clk_set_freq;
+ cops->get_freq = ti_sci_cmd_clk_get_freq;
+
+ core_ops->reboot_device = ti_sci_cmd_core_reboot;
+ core_ops->query_msmc = ti_sci_cmd_query_msmc;
+
+ rm_core_ops->get_range = ti_sci_cmd_get_resource_range;
+ rm_core_ops->get_range_from_shost =
+ ti_sci_cmd_get_resource_range_from_shost;
+
+ pops->proc_request = ti_sci_cmd_proc_request;
+ pops->proc_release = ti_sci_cmd_proc_release;
+ pops->proc_handover = ti_sci_cmd_proc_handover;
+ pops->set_proc_boot_cfg = ti_sci_cmd_set_proc_boot_cfg;
+ pops->set_proc_boot_ctrl = ti_sci_cmd_set_proc_boot_ctrl;
+ pops->proc_auth_boot_image = ti_sci_cmd_proc_auth_boot_image;
+ pops->get_proc_boot_status = ti_sci_cmd_get_proc_boot_status;
+ pops->proc_shutdown_no_wait = ti_sci_cmd_proc_shutdown_no_wait;
+
+ rops->config = ti_sci_cmd_ring_config;
+
+ psilops->pair = ti_sci_cmd_rm_psil_pair;
+ psilops->unpair = ti_sci_cmd_rm_psil_unpair;
+
+ udmap_ops->tx_ch_cfg = ti_sci_cmd_rm_udmap_tx_ch_cfg;
+ udmap_ops->rx_ch_cfg = ti_sci_cmd_rm_udmap_rx_ch_cfg;
+ udmap_ops->rx_flow_cfg = ti_sci_cmd_rm_udmap_rx_flow_cfg;
+
+ fwl_ops->set_fwl_region = ti_sci_cmd_set_fwl_region;
+ fwl_ops->get_fwl_region = ti_sci_cmd_get_fwl_region;
+ fwl_ops->change_fwl_owner = ti_sci_cmd_change_fwl_owner;
+}
+
+static void ti_sci_reset(struct restart_handler *unused)
+{
+ ti_sci_cmd_core_reboot(g_handle);
+}
+
+static int ti_sci_probe(struct device *dev)
+{
+ struct ti_sci_info *info;
+ const void *data;
+ int ret;
+
+ if (g_handle)
+ return 0;
+
+ ret = dev_get_drvdata(dev, &data);
+ if (ret)
+ return ret;
+
+ info = xzalloc(sizeof(*info));
+
+ info->chan_rx = mbox_request_channel_byname(dev, "rx");
+ if (IS_ERR(info->chan_rx))
+ return PTR_ERR(info->chan_rx);
+
+ info->chan_tx = mbox_request_channel_byname(dev, "tx");
+ if (IS_ERR(info->chan_tx))
+ return PTR_ERR(info->chan_tx);
+
+ info->desc = data;
+ info->host_id = info->desc->default_host_id;
+ of_property_read_u32(dev->of_node, "ti,host-id", &info->host_id);
+
+ info->is_secure = of_property_read_bool(dev->of_node, "ti,secure-host");
+
+ info->dev = dev;
+ info->seq = 0xA;
+ INIT_LIST_HEAD(&info->dev_list);
+
+ ti_sci_setup_ops(info);
+
+ ret = ti_sci_cmd_get_revision(&info->handle);
+ if (ret)
+ return ret;
+
+ g_handle = &info->handle;
+
+ of_platform_populate(dev->of_node, NULL, NULL);
+
+ restart_handler_register_fn("ti-sci", ti_sci_reset);
+
+ return 0;
+}
+
+/* Description for K2G */
+static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
+ .default_host_id = 2,
+ /* Conservative duration */
+ .max_rx_timeout_ms = 10000,
+ /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */
+ .max_msgs = 20,
+ .max_msg_size = 64,
+};
+
+/* Description for AM654 */
+static const struct ti_sci_desc ti_sci_pmmc_am654_desc = {
+ .default_host_id = 12,
+ /* Conservative duration */
+ .max_rx_timeout_ms = 10000,
+ /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */
+ .max_msgs = 20,
+ .max_msg_size = 60,
+};
+
+static const struct of_device_id ti_sci_of_match[] = {
+ {
+ .compatible = "ti,k2g-sci",
+ .data = &ti_sci_pmmc_k2g_desc
+ }, {
+ .compatible = "ti,am654-sci",
+ .data = &ti_sci_pmmc_am654_desc
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, ti_sci_of_match);
+
+static struct driver ti_sci_driver = {
+ .name = "ti-sci",
+ .probe = ti_sci_probe,
+ .of_compatible = DRV_OF_COMPAT(ti_sci_of_match),
+};
+core_platform_driver(ti_sci_driver);
diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h
new file mode 100644
index 0000000000..101210eb21
--- /dev/null
+++ b/drivers/firmware/ti_sci.h
@@ -0,0 +1,1533 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Texas Instruments System Control Interface (TISCI) Protocol
+ *
+ * Communication protocol with TI SCI hardware
+ * The system works in a message response protocol
+ * See: http://processors.wiki.ti.com/index.php/TISCI for details
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Based on drivers/firmware/ti_sci.h from Linux.
+ *
+ */
+
+#ifndef __TI_SCI_H
+#define __TI_SCI_H
+
+/* Generic Messages */
+#include <linux/bitops.h>
+#define TI_SCI_MSG_ENABLE_WDT 0x0000
+#define TI_SCI_MSG_WAKE_RESET 0x0001
+#define TI_SCI_MSG_VERSION 0x0002
+#define TI_SCI_MSG_WAKE_REASON 0x0003
+#define TI_SCI_MSG_GOODBYE 0x0004
+#define TI_SCI_MSG_SYS_RESET 0x0005
+#define TI_SCI_MSG_BOARD_CONFIG 0x000b
+#define TI_SCI_MSG_BOARD_CONFIG_RM 0x000c
+#define TI_SCI_MSG_BOARD_CONFIG_SECURITY 0x000d
+#define TI_SCI_MSG_BOARD_CONFIG_PM 0x000e
+#define TISCI_MSG_QUERY_MSMC 0x0020
+
+/* Device requests */
+#define TI_SCI_MSG_SET_DEVICE_STATE 0x0200
+#define TI_SCI_MSG_GET_DEVICE_STATE 0x0201
+#define TI_SCI_MSG_SET_DEVICE_RESETS 0x0202
+
+/* Clock requests */
+#define TI_SCI_MSG_SET_CLOCK_STATE 0x0100
+#define TI_SCI_MSG_GET_CLOCK_STATE 0x0101
+#define TI_SCI_MSG_SET_CLOCK_PARENT 0x0102
+#define TI_SCI_MSG_GET_CLOCK_PARENT 0x0103
+#define TI_SCI_MSG_GET_NUM_CLOCK_PARENTS 0x0104
+#define TI_SCI_MSG_SET_CLOCK_FREQ 0x010c
+#define TI_SCI_MSG_QUERY_CLOCK_FREQ 0x010d
+#define TI_SCI_MSG_GET_CLOCK_FREQ 0x010e
+
+/* Processor Control Messages */
+#define TISCI_MSG_PROC_REQUEST 0xc000
+#define TISCI_MSG_PROC_RELEASE 0xc001
+#define TISCI_MSG_PROC_HANDOVER 0xc005
+#define TISCI_MSG_SET_PROC_BOOT_CONFIG 0xc100
+#define TISCI_MSG_SET_PROC_BOOT_CTRL 0xc101
+#define TISCI_MSG_PROC_AUTH_BOOT_IMAGE 0xc120
+#define TISCI_MSG_GET_PROC_BOOT_STATUS 0xc400
+#define TISCI_MSG_WAIT_PROC_BOOT_STATUS 0xc401
+
+/* Resource Management Requests */
+#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500
+
+/* NAVSS resource management */
+/* Ringacc requests */
+#define TI_SCI_MSG_RM_RING_CFG 0x1110
+
+/* PSI-L requests */
+#define TI_SCI_MSG_RM_PSIL_PAIR 0x1280
+#define TI_SCI_MSG_RM_PSIL_UNPAIR 0x1281
+
+#define TI_SCI_MSG_RM_UDMAP_TX_ALLOC 0x1200
+#define TI_SCI_MSG_RM_UDMAP_TX_FREE 0x1201
+#define TI_SCI_MSG_RM_UDMAP_RX_ALLOC 0x1210
+#define TI_SCI_MSG_RM_UDMAP_RX_FREE 0x1211
+#define TI_SCI_MSG_RM_UDMAP_FLOW_CFG 0x1220
+#define TI_SCI_MSG_RM_UDMAP_OPT_FLOW_CFG 0x1221
+
+#define TISCI_MSG_RM_UDMAP_TX_CH_CFG 0x1205
+#define TISCI_MSG_RM_UDMAP_RX_CH_CFG 0x1215
+#define TISCI_MSG_RM_UDMAP_FLOW_CFG 0x1230
+#define TISCI_MSG_RM_UDMAP_FLOW_SIZE_THRESH_CFG 0x1231
+
+#define TISCI_MSG_FWL_SET 0x9000
+#define TISCI_MSG_FWL_GET 0x9001
+#define TISCI_MSG_FWL_CHANGE_OWNER 0x9002
+
+/**
+ * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses
+ * @type: Type of messages: One of TI_SCI_MSG* values
+ * @host: Host of the message
+ * @seq: Message identifier indicating a transfer sequence
+ * @flags: Flag for the message
+ */
+struct ti_sci_msg_hdr {
+ u16 type;
+ u8 host;
+ u8 seq;
+#define TI_SCI_MSG_FLAG(val) (1 << (val))
+#define TI_SCI_FLAG_REQ_GENERIC_NORESPONSE 0x0
+#define TI_SCI_FLAG_REQ_ACK_ON_RECEIVED TI_SCI_MSG_FLAG(0)
+#define TI_SCI_FLAG_REQ_ACK_ON_PROCESSED TI_SCI_MSG_FLAG(1)
+#define TI_SCI_FLAG_RESP_GENERIC_NACK 0x0
+#define TI_SCI_FLAG_RESP_GENERIC_ACK TI_SCI_MSG_FLAG(1)
+ /* Additional Flags */
+ u32 flags;
+} __packed;
+
+/**
+ * struct ti_sci_secure_msg_hdr - Header that prefixes all TISCI messages sent
+ * via secure transport.
+ * @checksum: crc16 checksum for the entire message
+ * @reserved: Reserved for future use.
+ */
+struct ti_sci_secure_msg_hdr {
+ u16 checksum;
+ u16 reserved;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_version - Response for a message
+ * @hdr: Generic header
+ * @firmware_description: String describing the firmware
+ * @firmware_revision: Firmware revision
+ * @abi_major: Major version of the ABI that firmware supports
+ * @abi_minor: Minor version of the ABI that firmware supports
+ *
+ * In general, ABI version changes follow the rule that minor version increments
+ * are backward compatible. Major revision changes in ABI may not be
+ * backward compatible.
+ *
+ * Response to a generic message with message type TI_SCI_MSG_VERSION
+ */
+struct ti_sci_msg_resp_version {
+ struct ti_sci_msg_hdr hdr;
+ char firmware_description[32];
+ u16 firmware_revision;
+ u8 abi_major;
+ u8 abi_minor;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_reboot - Reboot the SoC
+ * @hdr: Generic Header
+ * @domain: Domain to be reset, 0 for full SoC reboot.
+ *
+ * Request type is TI_SCI_MSG_SYS_RESET, responded with a generic
+ * ACK/NACK message.
+ */
+struct ti_sci_msg_req_reboot {
+ struct ti_sci_msg_hdr hdr;
+ u8 domain;
+} __packed;
+
+/**
+ * struct ti_sci_msg_board_config - Board configuration message
+ * @hdr: Generic Header
+ * @boardcfgp_low: Lower 32 bit of the pointer pointing to the board
+ * configuration data
+ * @boardcfgp_high: Upper 32 bit of the pointer pointing to the board
+ * configuration data
+ * @boardcfg_size: Size of board configuration data object
+ * Request type is TI_SCI_MSG_BOARD_CONFIG, responded with a generic
+ * ACK/NACK message.
+ */
+struct ti_sci_msg_board_config {
+ struct ti_sci_msg_hdr hdr;
+ u32 boardcfgp_low;
+ u32 boardcfgp_high;
+ u16 boardcfg_size;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_query_msmc - Query msmc message response structure
+ * @hdr: Generic Header
+ * @msmc_start_low: Lower 32 bit of msmc start
+ * @msmc_start_high: Upper 32 bit of msmc start
+ * @msmc_end_low: Lower 32 bit of msmc end
+ * @msmc_end_high: Upper 32 bit of msmc end
+ *
+ * Response to a generic message with message type TISCI_MSG_QUERY_MSMC
+ */
+struct ti_sci_msg_resp_query_msmc {
+ struct ti_sci_msg_hdr hdr;
+ u32 msmc_start_low;
+ u32 msmc_start_high;
+ u32 msmc_end_low;
+ u32 msmc_end_high;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_set_device_state - Set the desired state of the device
+ * @hdr: Generic header
+ * @id: Indicates which device to modify
+ * @reserved: Reserved space in message, must be 0 for backward compatibility
+ * @state: The desired state of the device.
+ *
+ * Certain flags can also be set to alter the device state:
+ * + MSG_FLAG_DEVICE_WAKE_ENABLED - Configure the device to be a wake source.
+ * The meaning of this flag will vary slightly from device to device and from
+ * SoC to SoC but it generally allows the device to wake the SoC out of deep
+ * suspend states.
+ * + MSG_FLAG_DEVICE_RESET_ISO - Enable reset isolation for this device.
+ * + MSG_FLAG_DEVICE_EXCLUSIVE - Claim this device exclusively. When passed
+ * with STATE_RETENTION or STATE_ON, it will claim the device exclusively.
+ * If another host already has this device set to STATE_RETENTION or STATE_ON,
+ * the message will fail. Once successful, other hosts attempting to set
+ * STATE_RETENTION or STATE_ON will fail.
+ *
+ * Request type is TI_SCI_MSG_SET_DEVICE_STATE, responded with a generic
+ * ACK/NACK message.
+ */
+struct ti_sci_msg_req_set_device_state {
+ /* Additional hdr->flags options */
+#define MSG_FLAG_DEVICE_WAKE_ENABLED TI_SCI_MSG_FLAG(8)
+#define MSG_FLAG_DEVICE_RESET_ISO TI_SCI_MSG_FLAG(9)
+#define MSG_FLAG_DEVICE_EXCLUSIVE TI_SCI_MSG_FLAG(10)
+ struct ti_sci_msg_hdr hdr;
+ u32 id;
+ u32 reserved;
+
+#define MSG_DEVICE_SW_STATE_AUTO_OFF 0
+#define MSG_DEVICE_SW_STATE_RETENTION 1
+#define MSG_DEVICE_SW_STATE_ON 2
+ u8 state;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_get_device_state - Request to get device.
+ * @hdr: Generic header
+ * @id: Device Identifier
+ *
+ * Request type is TI_SCI_MSG_GET_DEVICE_STATE, responded device state
+ * information
+ */
+struct ti_sci_msg_req_get_device_state {
+ struct ti_sci_msg_hdr hdr;
+ u32 id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_get_device_state - Response to get device request.
+ * @hdr: Generic header
+ * @context_loss_count: Indicates how many times the device has lost context. A
+ * driver can use this monotonic counter to determine if the device has
+ * lost context since the last time this message was exchanged.
+ * @resets: Programmed state of the reset lines.
+ * @programmed_state: The state as programmed by set_device.
+ * - Uses the MSG_DEVICE_SW_* macros
+ * @current_state: The actual state of the hardware.
+ *
+ * Response to request TI_SCI_MSG_GET_DEVICE_STATE.
+ */
+struct ti_sci_msg_resp_get_device_state {
+ struct ti_sci_msg_hdr hdr;
+ u32 context_loss_count;
+ u32 resets;
+ u8 programmed_state;
+#define MSG_DEVICE_HW_STATE_OFF 0
+#define MSG_DEVICE_HW_STATE_ON 1
+#define MSG_DEVICE_HW_STATE_TRANS 2
+ u8 current_state;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_set_device_resets - Set the desired resets
+ * configuration of the device
+ * @hdr: Generic header
+ * @id: Indicates which device to modify
+ * @resets: A bit field of resets for the device. The meaning, behavior,
+ * and usage of the reset flags are device specific. 0 for a bit
+ * indicates releasing the reset represented by that bit while 1
+ * indicates keeping it held.
+ *
+ * Request type is TI_SCI_MSG_SET_DEVICE_RESETS, responded with a generic
+ * ACK/NACK message.
+ */
+struct ti_sci_msg_req_set_device_resets {
+ struct ti_sci_msg_hdr hdr;
+ u32 id;
+ u32 resets;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_set_clock_state - Request to setup a Clock state
+ * @hdr: Generic Header, Certain flags can be set specific to the clocks:
+ * MSG_FLAG_CLOCK_ALLOW_SSC: Allow this clock to be modified
+ * via spread spectrum clocking.
+ * MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE: Allow this clock's
+ * frequency to be changed while it is running so long as it
+ * is within the min/max limits.
+ * MSG_FLAG_CLOCK_INPUT_TERM: Enable input termination, this
+ * is only applicable to clock inputs on the SoC pseudo-device.
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @request_state: Request the state for the clock to be set to.
+ * MSG_CLOCK_SW_STATE_UNREQ: The IP does not require this clock,
+ * it can be disabled, regardless of the state of the device
+ * MSG_CLOCK_SW_STATE_AUTO: Allow the System Controller to
+ * automatically manage the state of this clock. If the device
+ * is enabled, then the clock is enabled. If the device is set
+ * to off or retention, then the clock is internally set as not
+ * being required by the device.(default)
+ * MSG_CLOCK_SW_STATE_REQ: Configure the clock to be enabled,
+ * regardless of the state of the device.
+ *
+ * Normally, all required clocks are managed by TISCI entity, this is used
+ * only for specific control *IF* required. Auto managed state is
+ * MSG_CLOCK_SW_STATE_AUTO, in other states, TISCI entity assume remote
+ * will explicitly control.
+ *
+ * Request type is TI_SCI_MSG_SET_CLOCK_STATE, response is a generic
+ * ACK or NACK message.
+ */
+struct ti_sci_msg_req_set_clock_state {
+ /* Additional hdr->flags options */
+#define MSG_FLAG_CLOCK_ALLOW_SSC TI_SCI_MSG_FLAG(8)
+#define MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE TI_SCI_MSG_FLAG(9)
+#define MSG_FLAG_CLOCK_INPUT_TERM TI_SCI_MSG_FLAG(10)
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u8 clk_id;
+#define MSG_CLOCK_SW_STATE_UNREQ 0
+#define MSG_CLOCK_SW_STATE_AUTO 1
+#define MSG_CLOCK_SW_STATE_REQ 2
+ u8 request_state;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_get_clock_state - Request for clock state
+ * @hdr: Generic Header
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to get state of.
+ *
+ * Request type is TI_SCI_MSG_GET_CLOCK_STATE, response is state
+ * of the clock
+ */
+struct ti_sci_msg_req_get_clock_state {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u8 clk_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_get_clock_state - Response to get clock state
+ * @hdr: Generic Header
+ * @programmed_state: Any programmed state of the clock. This is one of
+ * MSG_CLOCK_SW_STATE* values.
+ * @current_state: Current state of the clock. This is one of:
+ * MSG_CLOCK_HW_STATE_NOT_READY: Clock is not ready
+ * MSG_CLOCK_HW_STATE_READY: Clock is ready
+ *
+ * Response to TI_SCI_MSG_GET_CLOCK_STATE.
+ */
+struct ti_sci_msg_resp_get_clock_state {
+ struct ti_sci_msg_hdr hdr;
+ u8 programmed_state;
+#define MSG_CLOCK_HW_STATE_NOT_READY 0
+#define MSG_CLOCK_HW_STATE_READY 1
+ u8 current_state;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_set_clock_parent - Set the clock parent
+ * @hdr: Generic Header
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to modify.
+ * @parent_id: The new clock parent is selectable by an index via this
+ * parameter.
+ *
+ * Request type is TI_SCI_MSG_SET_CLOCK_PARENT, response is generic
+ * ACK / NACK message.
+ */
+struct ti_sci_msg_req_set_clock_parent {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u8 clk_id;
+ u8 parent_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_get_clock_parent - Get the clock parent
+ * @hdr: Generic Header
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ * Each device has it's own set of clock inputs. This indexes
+ * which clock input to get the parent for.
+ *
+ * Request type is TI_SCI_MSG_GET_CLOCK_PARENT, response is parent information
+ */
+struct ti_sci_msg_req_get_clock_parent {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u8 clk_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_get_clock_parent - Response with clock parent
+ * @hdr: Generic Header
+ * @parent_id: The current clock parent
+ *
+ * Response to TI_SCI_MSG_GET_CLOCK_PARENT.
+ */
+struct ti_sci_msg_resp_get_clock_parent {
+ struct ti_sci_msg_hdr hdr;
+ u8 parent_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_get_clock_num_parents - Request to get clock parents
+ * @hdr: Generic header
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ *
+ * This request provides information about how many clock parent options
+ * are available for a given clock to a device. This is typically used
+ * for input clocks.
+ *
+ * Request type is TI_SCI_MSG_GET_NUM_CLOCK_PARENTS, response is appropriate
+ * message, or NACK in case of inability to satisfy request.
+ */
+struct ti_sci_msg_req_get_clock_num_parents {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u8 clk_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_get_clock_num_parents - Response for get clk parents
+ * @hdr: Generic header
+ * @num_parents: Number of clock parents
+ *
+ * Response to TI_SCI_MSG_GET_NUM_CLOCK_PARENTS
+ */
+struct ti_sci_msg_resp_get_clock_num_parents {
+ struct ti_sci_msg_hdr hdr;
+ u8 num_parents;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_query_clock_freq - Request to query a frequency
+ * @hdr: Generic Header
+ * @dev_id: Device identifier this request is for
+ * @min_freq_hz: The minimum allowable frequency in Hz. This is the minimum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @target_freq_hz: The target clock frequency. A frequency will be found
+ * as close to this target frequency as possible.
+ * @max_freq_hz: The maximum allowable frequency in Hz. This is the maximum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @clk_id: Clock identifier for the device for this request.
+ *
+ * NOTE: Normally clock frequency management is automatically done by TISCI
+ * entity. In case of specific requests, TISCI evaluates capability to achieve
+ * requested frequency within provided range and responds with
+ * result message.
+ *
+ * Request type is TI_SCI_MSG_QUERY_CLOCK_FREQ, response is appropriate message,
+ * or NACK in case of inability to satisfy request.
+ */
+struct ti_sci_msg_req_query_clock_freq {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u64 min_freq_hz;
+ u64 target_freq_hz;
+ u64 max_freq_hz;
+ u8 clk_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_query_clock_freq - Response to a clock frequency query
+ * @hdr: Generic Header
+ * @freq_hz: Frequency that is the best match in Hz.
+ *
+ * Response to request type TI_SCI_MSG_QUERY_CLOCK_FREQ. NOTE: if the request
+ * cannot be satisfied, the message will be of type NACK.
+ */
+struct ti_sci_msg_resp_query_clock_freq {
+ struct ti_sci_msg_hdr hdr;
+ u64 freq_hz;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_set_clock_freq - Request to setup a clock frequency
+ * @hdr: Generic Header
+ * @dev_id: Device identifier this request is for
+ * @min_freq_hz: The minimum allowable frequency in Hz. This is the minimum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @target_freq_hz: The target clock frequency. The clock will be programmed
+ * at a rate as close to this target frequency as possible.
+ * @max_freq_hz: The maximum allowable frequency in Hz. This is the maximum
+ * allowable programmed frequency and does not account for clock
+ * tolerances and jitter.
+ * @clk_id: Clock identifier for the device for this request.
+ *
+ * NOTE: Normally clock frequency management is automatically done by TISCI
+ * entity. In case of specific requests, TISCI evaluates capability to achieve
+ * requested range and responds with success/failure message.
+ *
+ * This sets the desired frequency for a clock within an allowable
+ * range. This message will fail on an enabled clock unless
+ * MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE is set for the clock. Additionally,
+ * if other clocks have their frequency modified due to this message,
+ * they also must have the MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE or be disabled.
+ *
+ * Calling set frequency on a clock input to the SoC pseudo-device will
+ * inform the PMMC of that clock's frequency. Setting a frequency of
+ * zero will indicate the clock is disabled.
+ *
+ * Calling set frequency on clock outputs from the SoC pseudo-device will
+ * function similarly to setting the clock frequency on a device.
+ *
+ * Request type is TI_SCI_MSG_SET_CLOCK_FREQ, response is a generic ACK/NACK
+ * message.
+ */
+struct ti_sci_msg_req_set_clock_freq {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u64 min_freq_hz;
+ u64 target_freq_hz;
+ u64 max_freq_hz;
+ u8 clk_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_get_clock_freq - Request to get the clock frequency
+ * @hdr: Generic Header
+ * @dev_id: Device identifier this request is for
+ * @clk_id: Clock identifier for the device for this request.
+ *
+ * NOTE: Normally clock frequency management is automatically done by TISCI
+ * entity. In some cases, clock frequencies are configured by host.
+ *
+ * Request type is TI_SCI_MSG_GET_CLOCK_FREQ, responded with clock frequency
+ * that the clock is currently at.
+ */
+struct ti_sci_msg_req_get_clock_freq {
+ struct ti_sci_msg_hdr hdr;
+ u32 dev_id;
+ u8 clk_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_get_clock_freq - Response of clock frequency request
+ * @hdr: Generic Header
+ * @freq_hz: Frequency that the clock is currently on, in Hz.
+ *
+ * Response to request type TI_SCI_MSG_GET_CLOCK_FREQ.
+ */
+struct ti_sci_msg_resp_get_clock_freq {
+ struct ti_sci_msg_hdr hdr;
+ u64 freq_hz;
+} __packed;
+
+#define TI_SCI_IRQ_SECONDARY_HOST_INVALID 0xff
+
+/**
+ * struct ti_sci_msg_req_get_resource_range - Request to get a host's assigned
+ * range of resources.
+ * @hdr: Generic Header
+ * @type: Unique resource assignment type
+ * @subtype: Resource assignment subtype within the resource type.
+ * @secondary_host: Host processing entity to which the resources are
+ * allocated. This is required only when the destination
+ * host id id different from ti sci interface host id,
+ * else TI_SCI_IRQ_SECONDARY_HOST_INVALID can be passed.
+ *
+ * Request type is TI_SCI_MSG_GET_RESOURCE_RANGE. Responded with requested
+ * resource range which is of type TI_SCI_MSG_GET_RESOURCE_RANGE.
+ */
+struct ti_sci_msg_req_get_resource_range {
+ struct ti_sci_msg_hdr hdr;
+#define MSG_RM_RESOURCE_TYPE_MASK GENMASK(9, 0)
+#define MSG_RM_RESOURCE_SUBTYPE_MASK GENMASK(5, 0)
+ u16 type;
+ u8 subtype;
+ u8 secondary_host;
+} __packed;
+
+/**
+ * struct ti_sci_msg_resp_get_resource_range - Response to resource get range.
+ * @hdr: Generic Header
+ * @range_start: Start index of the resource range.
+ * @range_num: Number of resources in the range.
+ *
+ * Response to request TI_SCI_MSG_GET_RESOURCE_RANGE.
+ */
+struct ti_sci_msg_resp_get_resource_range {
+ struct ti_sci_msg_hdr hdr;
+ u16 range_start;
+ u16 range_num;
+} __packed;
+
+#define TISCI_ADDR_LOW_MASK GENMASK_ULL(31, 0)
+#define TISCI_ADDR_HIGH_MASK GENMASK_ULL(63, 32)
+#define TISCI_ADDR_HIGH_SHIFT 32
+
+/**
+ * struct ti_sci_msg_req_proc_request - Request a processor
+ *
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ *
+ * Request type is TISCI_MSG_PROC_REQUEST, response is a generic ACK/NACK
+ * message.
+ */
+struct ti_sci_msg_req_proc_request {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_proc_release - Release a processor
+ *
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ *
+ * Request type is TISCI_MSG_PROC_RELEASE, response is a generic ACK/NACK
+ * message.
+ */
+struct ti_sci_msg_req_proc_release {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_proc_handover - Handover a processor to a host
+ *
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ * @host_id: New Host we want to give control to
+ *
+ * Request type is TISCI_MSG_PROC_HANDOVER, response is a generic ACK/NACK
+ * message.
+ */
+struct ti_sci_msg_req_proc_handover {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+ u8 host_id;
+} __packed;
+
+/* A53 Config Flags */
+#define PROC_BOOT_CFG_FLAG_ARMV8_DBG_EN 0x00000001
+#define PROC_BOOT_CFG_FLAG_ARMV8_DBG_NIDEN 0x00000002
+#define PROC_BOOT_CFG_FLAG_ARMV8_DBG_SPIDEN 0x00000004
+#define PROC_BOOT_CFG_FLAG_ARMV8_DBG_SPNIDEN 0x00000008
+#define PROC_BOOT_CFG_FLAG_ARMV8_AARCH32 0x00000100
+
+/* R5 Config Flags */
+#define PROC_BOOT_CFG_FLAG_R5_DBG_EN 0x00000001
+#define PROC_BOOT_CFG_FLAG_R5_DBG_NIDEN 0x00000002
+#define PROC_BOOT_CFG_FLAG_R5_LOCKSTEP 0x00000100
+#define PROC_BOOT_CFG_FLAG_R5_TEINIT 0x00000200
+#define PROC_BOOT_CFG_FLAG_R5_NMFI_EN 0x00000400
+#define PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE 0x00000800
+#define PROC_BOOT_CFG_FLAG_R5_BTCM_EN 0x00001000
+#define PROC_BOOT_CFG_FLAG_R5_ATCM_EN 0x00002000
+
+/**
+ * struct ti_sci_msg_req_set_proc_boot_config - Set Processor boot configuration
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ * @bootvector_low: Lower 32bit (Little Endian) of boot vector
+ * @bootvector_high: Higher 32bit (Little Endian) of boot vector
+ * @config_flags_set: Optional Processor specific Config Flags to set.
+ * Setting a bit here implies required bit sets to 1.
+ * @config_flags_clear: Optional Processor specific Config Flags to clear.
+ * Setting a bit here implies required bit gets cleared.
+ *
+ * Request type is TISCI_MSG_SET_PROC_BOOT_CONFIG, response is a generic
+ * ACK/NACK message.
+ */
+struct ti_sci_msg_req_set_proc_boot_config {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+ u32 bootvector_low;
+ u32 bootvector_high;
+ u32 config_flags_set;
+ u32 config_flags_clear;
+} __packed;
+
+/* R5 Control Flags */
+#define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT 0x00000001
+
+/**
+ * struct ti_sci_msg_req_set_proc_boot_ctrl - Set Processor boot control flags
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ * @control_flags_set: Optional Processor specific Control Flags to set.
+ * Setting a bit here implies required bit sets to 1.
+ * @control_flags_clear:Optional Processor specific Control Flags to clear.
+ * Setting a bit here implies required bit gets cleared.
+ *
+ * Request type is TISCI_MSG_SET_PROC_BOOT_CTRL, response is a generic ACK/NACK
+ * message.
+ */
+struct ti_sci_msg_req_set_proc_boot_ctrl {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+ u32 control_flags_set;
+ u32 control_flags_clear;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_proc_auth_start_image - Authenticate and start image
+ * @hdr: Generic Header
+ * @cert_addr_low: Lower 32bit (Little Endian) of certificate
+ * @cert_addr_high: Higher 32bit (Little Endian) of certificate
+ *
+ * Request type is TISCI_MSG_PROC_AUTH_BOOT_IMAGE, response is a generic
+ * ACK/NACK message.
+ */
+struct ti_sci_msg_req_proc_auth_boot_image {
+ struct ti_sci_msg_hdr hdr;
+ u32 cert_addr_low;
+ u32 cert_addr_high;
+} __packed;
+
+struct ti_sci_msg_resp_proc_auth_boot_image {
+ struct ti_sci_msg_hdr hdr;
+ u32 image_addr_low;
+ u32 image_addr_high;
+ u32 image_size;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_get_proc_boot_status - Get processor boot status
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ *
+ * Request type is TISCI_MSG_GET_PROC_BOOT_STATUS, response is appropriate
+ * message, or NACK in case of inability to satisfy request.
+ */
+struct ti_sci_msg_req_get_proc_boot_status {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+} __packed;
+
+/* ARMv8 Status Flags */
+#define PROC_BOOT_STATUS_FLAG_ARMV8_WFE 0x00000001
+#define PROC_BOOT_STATUS_FLAG_ARMV8_WFI 0x00000002
+
+/* R5 Status Flags */
+#define PROC_BOOT_STATUS_FLAG_R5_WFE 0x00000001
+#define PROC_BOOT_STATUS_FLAG_R5_WFI 0x00000002
+#define PROC_BOOT_STATUS_FLAG_R5_CLK_GATED 0x00000004
+#define PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED 0x00000100
+
+/**
+ * struct ti_sci_msg_resp_get_proc_boot_status - Processor boot status response
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ * @bootvector_low: Lower 32bit (Little Endian) of boot vector
+ * @bootvector_high: Higher 32bit (Little Endian) of boot vector
+ * @config_flags: Optional Processor specific Config Flags set.
+ * @control_flags: Optional Processor specific Control Flags.
+ * @status_flags: Optional Processor specific Status Flags set.
+ *
+ * Response to TISCI_MSG_GET_PROC_BOOT_STATUS.
+ */
+struct ti_sci_msg_resp_get_proc_boot_status {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+ u32 bootvector_low;
+ u32 bootvector_high;
+ u32 config_flags;
+ u32 control_flags;
+ u32 status_flags;
+} __packed;
+
+/**
+ * struct ti_sci_msg_req_wait_proc_boot_status - Wait for a processor
+ * boot status
+ * @hdr: Generic Header
+ * @processor_id: ID of processor
+ * @num_wait_iterations: Total number of iterations we will check before
+ * we will timeout and give up
+ * @num_match_iterations: How many iterations should we have continued
+ * status to account for status bits glitching.
+ * This is to make sure that match occurs for
+ * consecutive checks. This implies that the
+ * worst case should consider that the stable
+ * time should at the worst be num_wait_iterations
+ * num_match_iterations to prevent timeout.
+ * @delay_per_iteration_us: Specifies how long to wait (in micro seconds)
+ * between each status checks. This is the minimum
+ * duration, and overhead of register reads and
+ * checks are on top of this and can vary based on
+ * varied conditions.
+ * @delay_before_iterations_us: Specifies how long to wait (in micro seconds)
+ * before the very first check in the first
+ * iteration of status check loop. This is the
+ * minimum duration, and overhead of register
+ * reads and checks are.
+ * @status_flags_1_set_all_wait:If non-zero, Specifies that all bits of the
+ * status matching this field requested MUST be 1.
+ * @status_flags_1_set_any_wait:If non-zero, Specifies that at least one of the
+ * bits matching this field requested MUST be 1.
+ * @status_flags_1_clr_all_wait:If non-zero, Specifies that all bits of the
+ * status matching this field requested MUST be 0.
+ * @status_flags_1_clr_any_wait:If non-zero, Specifies that at least one of the
+ * bits matching this field requested MUST be 0.
+ *
+ * Request type is TISCI_MSG_WAIT_PROC_BOOT_STATUS, response is appropriate
+ * message, or NACK in case of inability to satisfy request.
+ */
+struct ti_sci_msg_req_wait_proc_boot_status {
+ struct ti_sci_msg_hdr hdr;
+ u8 processor_id;
+ u8 num_wait_iterations;
+ u8 num_match_iterations;
+ u8 delay_per_iteration_us;
+ u8 delay_before_iterations_us;
+ u32 status_flags_1_set_all_wait;
+ u32 status_flags_1_set_any_wait;
+ u32 status_flags_1_clr_all_wait;
+ u32 status_flags_1_clr_any_wait;
+} __packed;
+
+/**
+ * struct ti_sci_msg_rm_ring_cfg_req - Configure a Navigator Subsystem ring
+ *
+ * Configures the non-real-time registers of a Navigator Subsystem ring.
+ * @hdr: Generic Header
+ * @valid_params: Bitfield defining validity of ring configuration parameters.
+ * The ring configuration fields are not valid, and will not be used for
+ * ring configuration, if their corresponding valid bit is zero.
+ * Valid bit usage:
+ * 0 - Valid bit for @tisci_msg_rm_ring_cfg_req addr_lo
+ * 1 - Valid bit for @tisci_msg_rm_ring_cfg_req addr_hi
+ * 2 - Valid bit for @tisci_msg_rm_ring_cfg_req count
+ * 3 - Valid bit for @tisci_msg_rm_ring_cfg_req mode
+ * 4 - Valid bit for @tisci_msg_rm_ring_cfg_req size
+ * 5 - Valid bit for @tisci_msg_rm_ring_cfg_req order_id
+ * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated
+ * @index: ring index to be configured.
+ * @addr_lo: 32 LSBs of ring base address to be programmed into the ring's
+ * RING_BA_LO register
+ * @addr_hi: 16 MSBs of ring base address to be programmed into the ring's
+ * RING_BA_HI register.
+ * @count: Number of ring elements. Must be even if mode is CREDENTIALS or QM
+ * modes.
+ * @mode: Specifies the mode the ring is to be configured.
+ * @size: Specifies encoded ring element size. To calculate the encoded size use
+ * the formula (log2(size_bytes) - 2), where size_bytes cannot be
+ * greater than 256.
+ * @order_id: Specifies the ring's bus order ID.
+ */
+struct ti_sci_msg_rm_ring_cfg_req {
+ struct ti_sci_msg_hdr hdr;
+ u32 valid_params;
+ u16 nav_id;
+ u16 index;
+ u32 addr_lo;
+ u32 addr_hi;
+ u32 count;
+ u8 mode;
+ u8 size;
+ u8 order_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_rm_ring_cfg_resp - Response to configuring a ring.
+ *
+ * @hdr: Generic Header
+ */
+struct ti_sci_msg_rm_ring_cfg_resp {
+ struct ti_sci_msg_hdr hdr;
+} __packed;
+
+/**
+ * struct ti_sci_msg_rm_ring_get_cfg_req - Get RA ring's configuration
+ *
+ * Gets the configuration of the non-real-time register fields of a ring. The
+ * host, or a supervisor of the host, who owns the ring must be the requesting
+ * host. The values of the non-real-time registers are returned in
+ * @ti_sci_msg_rm_ring_get_cfg_resp.
+ *
+ * @hdr: Generic Header
+ * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated
+ * @index: ring index.
+ */
+struct ti_sci_msg_rm_ring_get_cfg_req {
+ struct ti_sci_msg_hdr hdr;
+ u16 nav_id;
+ u16 index;
+} __packed;
+
+/**
+ * struct ti_sci_msg_rm_ring_get_cfg_resp - Ring get configuration response
+ *
+ * Response received by host processor after RM has handled
+ * @ti_sci_msg_rm_ring_get_cfg_req. The response contains the ring's
+ * non-real-time register values.
+ *
+ * @hdr: Generic Header
+ * @addr_lo: Ring 32 LSBs of base address
+ * @addr_hi: Ring 16 MSBs of base address.
+ * @count: Ring number of elements.
+ * @mode: Ring mode.
+ * @size: encoded Ring element size
+ * @order_id: ing order ID.
+ */
+struct ti_sci_msg_rm_ring_get_cfg_resp {
+ struct ti_sci_msg_hdr hdr;
+ u32 addr_lo;
+ u32 addr_hi;
+ u32 count;
+ u8 mode;
+ u8 size;
+ u8 order_id;
+} __packed;
+
+/**
+ * struct ti_sci_msg_psil_pair - Pairs a PSI-L source thread to a destination
+ * thread
+ * @hdr: Generic Header
+ * @nav_id: SoC Navigator Subsystem device ID whose PSI-L config proxy is
+ * used to pair the source and destination threads.
+ * @src_thread: PSI-L source thread ID within the PSI-L System thread map.
+ *
+ * UDMAP transmit channels mapped to source threads will have their
+ * TCHAN_THRD_ID register programmed with the destination thread if the pairing
+ * is successful.
+
+ * @dst_thread: PSI-L destination thread ID within the PSI-L System thread map.
+ * PSI-L destination threads start at index 0x8000. The request is NACK'd if
+ * the destination thread is not greater than or equal to 0x8000.
+ *
+ * UDMAP receive channels mapped to destination threads will have their
+ * RCHAN_THRD_ID register programmed with the source thread if the pairing
+ * is successful.
+ *
+ * Request type is TI_SCI_MSG_RM_PSIL_PAIR, response is a generic ACK or NACK
+ * message.
+ */
+struct ti_sci_msg_psil_pair {
+ struct ti_sci_msg_hdr hdr;
+ u32 nav_id;
+ u32 src_thread;
+ u32 dst_thread;
+} __packed;
+
+/**
+ * struct ti_sci_msg_psil_unpair - Unpairs a PSI-L source thread from a
+ * destination thread
+ * @hdr: Generic Header
+ * @nav_id: SoC Navigator Subsystem device ID whose PSI-L config proxy is
+ * used to unpair the source and destination threads.
+ * @src_thread: PSI-L source thread ID within the PSI-L System thread map.
+ *
+ * UDMAP transmit channels mapped to source threads will have their
+ * TCHAN_THRD_ID register cleared if the unpairing is successful.
+ *
+ * @dst_thread: PSI-L destination thread ID within the PSI-L System thread map.
+ * PSI-L destination threads start at index 0x8000. The request is NACK'd if
+ * the destination thread is not greater than or equal to 0x8000.
+ *
+ * UDMAP receive channels mapped to destination threads will have their
+ * RCHAN_THRD_ID register cleared if the unpairing is successful.
+ *
+ * Request type is TI_SCI_MSG_RM_PSIL_UNPAIR, response is a generic ACK or NACK
+ * message.
+ */
+struct ti_sci_msg_psil_unpair {
+ struct ti_sci_msg_hdr hdr;
+ u32 nav_id;
+ u32 src_thread;
+ u32 dst_thread;
+} __packed;
+
+/**
+ * Configures a Navigator Subsystem UDMAP transmit channel
+ *
+ * Configures the non-real-time registers of a Navigator Subsystem UDMAP
+ * transmit channel. The channel index must be assigned to the host defined
+ * in the TISCI header via the RM board configuration resource assignment
+ * range list.
+ *
+ * @hdr: Generic Header
+ *
+ * @valid_params: Bitfield defining validity of tx channel configuration
+ * parameters. The tx channel configuration fields are not valid, and will not
+ * be used for ch configuration, if their corresponding valid bit is zero.
+ * Valid bit usage:
+ * 0 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_pause_on_err
+ * 1 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_atype
+ * 2 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_chan_type
+ * 3 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_fetch_size
+ * 4 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::txcq_qnum
+ * 5 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_priority
+ * 6 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_qos
+ * 7 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_orderid
+ * 8 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_sched_priority
+ * 9 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_filt_einfo
+ * 10 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_filt_pswords
+ * 11 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_supr_tdpkt
+ * 12 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_credit_count
+ * 13 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::fdepth
+ * 14 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_burst_size
+ * 15 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_tdtype
+ * 16 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::extended_ch_type
+ *
+ * @nav_id: SoC device ID of Navigator Subsystem where tx channel is located
+ *
+ * @index: UDMAP transmit channel index.
+ *
+ * @tx_pause_on_err: UDMAP transmit channel pause on error configuration to
+ * be programmed into the tx_pause_on_err field of the channel's TCHAN_TCFG
+ * register.
+ *
+ * @tx_filt_einfo: UDMAP transmit channel extended packet information passing
+ * configuration to be programmed into the tx_filt_einfo field of the
+ * channel's TCHAN_TCFG register.
+ *
+ * @tx_filt_pswords: UDMAP transmit channel protocol specific word passing
+ * configuration to be programmed into the tx_filt_pswords field of the
+ * channel's TCHAN_TCFG register.
+ *
+ * @tx_atype: UDMAP transmit channel non Ring Accelerator access pointer
+ * interpretation configuration to be programmed into the tx_atype field of
+ * the channel's TCHAN_TCFG register.
+ *
+ * @tx_chan_type: UDMAP transmit channel functional channel type and work
+ * passing mechanism configuration to be programmed into the tx_chan_type
+ * field of the channel's TCHAN_TCFG register.
+ *
+ * @tx_supr_tdpkt: UDMAP transmit channel teardown packet generation suppression
+ * configuration to be programmed into the tx_supr_tdpkt field of the channel's
+ * TCHAN_TCFG register.
+ *
+ * @tx_fetch_size: UDMAP transmit channel number of 32-bit descriptor words to
+ * fetch configuration to be programmed into the tx_fetch_size field of the
+ * channel's TCHAN_TCFG register. The user must make sure to set the maximum
+ * word count that can pass through the channel for any allowed descriptor type.
+ *
+ * @tx_credit_count: UDMAP transmit channel transfer request credit count
+ * configuration to be programmed into the count field of the TCHAN_TCREDIT
+ * register. Specifies how many credits for complete TRs are available.
+ *
+ * @txcq_qnum: UDMAP transmit channel completion queue configuration to be
+ * programmed into the txcq_qnum field of the TCHAN_TCQ register. The specified
+ * completion queue must be assigned to the host, or a subordinate of the host,
+ * requesting configuration of the transmit channel.
+ *
+ * @tx_priority: UDMAP transmit channel transmit priority value to be programmed
+ * into the priority field of the channel's TCHAN_TPRI_CTRL register.
+ *
+ * @tx_qos: UDMAP transmit channel transmit qos value to be programmed into the
+ * qos field of the channel's TCHAN_TPRI_CTRL register.
+ *
+ * @tx_orderid: UDMAP transmit channel bus order id value to be programmed into
+ * the orderid field of the channel's TCHAN_TPRI_CTRL register.
+ *
+ * @fdepth: UDMAP transmit channel FIFO depth configuration to be programmed
+ * into the fdepth field of the TCHAN_TFIFO_DEPTH register. Sets the number of
+ * Tx FIFO bytes which are allowed to be stored for the channel. Check the UDMAP
+ * section of the TRM for restrictions regarding this parameter.
+ *
+ * @tx_sched_priority: UDMAP transmit channel tx scheduling priority
+ * configuration to be programmed into the priority field of the channel's
+ * TCHAN_TST_SCHED register.
+ *
+ * @tx_burst_size: UDMAP transmit channel burst size configuration to be
+ * programmed into the tx_burst_size field of the TCHAN_TCFG register.
+ *
+ * @tx_tdtype: UDMAP transmit channel teardown type configuration to be
+ * programmed into the tdtype field of the TCHAN_TCFG register:
+ * 0 - Return immediately
+ * 1 - Wait for completion message from remote peer
+ *
+ * @extended_ch_type: Valid for BCDMA.
+ * 0 - the channel is split tx channel (tchan)
+ * 1 - the channel is block copy channel (bchan)
+ */
+struct ti_sci_msg_rm_udmap_tx_ch_cfg_req {
+ struct ti_sci_msg_hdr hdr;
+ u32 valid_params;
+ u16 nav_id;
+ u16 index;
+ u8 tx_pause_on_err;
+ u8 tx_filt_einfo;
+ u8 tx_filt_pswords;
+ u8 tx_atype;
+ u8 tx_chan_type;
+ u8 tx_supr_tdpkt;
+ u16 tx_fetch_size;
+ u8 tx_credit_count;
+ u16 txcq_qnum;
+ u8 tx_priority;
+ u8 tx_qos;
+ u8 tx_orderid;
+ u16 fdepth;
+ u8 tx_sched_priority;
+ u8 tx_burst_size;
+ u8 tx_tdtype;
+ u8 extended_ch_type;
+} __packed;
+
+/**
+ * Response to configuring a UDMAP transmit channel.
+ *
+ * @hdr: Standard TISCI header
+ */
+struct ti_sci_msg_rm_udmap_tx_ch_cfg_resp {
+ struct ti_sci_msg_hdr hdr;
+} __packed;
+
+/**
+ * Configures a Navigator Subsystem UDMAP receive channel
+ *
+ * Configures the non-real-time registers of a Navigator Subsystem UDMAP
+ * receive channel. The channel index must be assigned to the host defined
+ * in the TISCI header via the RM board configuration resource assignment
+ * range list.
+ *
+ * @hdr: Generic Header
+ *
+ * @valid_params: Bitfield defining validity of rx channel configuration
+ * parameters.
+ * The rx channel configuration fields are not valid, and will not be used for
+ * ch configuration, if their corresponding valid bit is zero.
+ * Valid bit usage:
+ * 0 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_pause_on_err
+ * 1 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_atype
+ * 2 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_chan_type
+ * 3 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_fetch_size
+ * 4 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rxcq_qnum
+ * 5 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_priority
+ * 6 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_qos
+ * 7 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_orderid
+ * 8 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_sched_priority
+ * 9 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::flowid_start
+ * 10 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::flowid_cnt
+ * 11 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_ignore_short
+ * 12 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_ignore_long
+ *
+ * @nav_id: SoC device ID of Navigator Subsystem where rx channel is located
+ *
+ * @index: UDMAP receive channel index.
+ *
+ * @rx_fetch_size: UDMAP receive channel number of 32-bit descriptor words to
+ * fetch configuration to be programmed into the rx_fetch_size field of the
+ * channel's RCHAN_RCFG register.
+ *
+ * @rxcq_qnum: UDMAP receive channel completion queue configuration to be
+ * programmed into the rxcq_qnum field of the RCHAN_RCQ register.
+ * The specified completion queue must be assigned to the host, or a subordinate
+ * of the host, requesting configuration of the receive channel.
+ *
+ * @rx_priority: UDMAP receive channel receive priority value to be programmed
+ * into the priority field of the channel's RCHAN_RPRI_CTRL register.
+ *
+ * @rx_qos: UDMAP receive channel receive qos value to be programmed into the
+ * qos field of the channel's RCHAN_RPRI_CTRL register.
+ *
+ * @rx_orderid: UDMAP receive channel bus order id value to be programmed into
+ * the orderid field of the channel's RCHAN_RPRI_CTRL register.
+ *
+ * @rx_sched_priority: UDMAP receive channel rx scheduling priority
+ * configuration to be programmed into the priority field of the channel's
+ * RCHAN_RST_SCHED register.
+ *
+ * @flowid_start: UDMAP receive channel additional flows starting index
+ * configuration to program into the flow_start field of the RCHAN_RFLOW_RNG
+ * register. Specifies the starting index for flow IDs the receive channel is to
+ * make use of beyond the default flow. flowid_start and @ref flowid_cnt must be
+ * set as valid and configured together. The starting flow ID set by
+ * @ref flowid_cnt must be a flow index within the Navigator Subsystem's subset
+ * of flows beyond the default flows statically mapped to receive channels.
+ * The additional flows must be assigned to the host, or a subordinate of the
+ * host, requesting configuration of the receive channel.
+ *
+ * @flowid_cnt: UDMAP receive channel additional flows count configuration to
+ * program into the flowid_cnt field of the RCHAN_RFLOW_RNG register.
+ * This field specifies how many flow IDs are in the additional contiguous range
+ * of legal flow IDs for the channel. @ref flowid_start and flowid_cnt must be
+ * set as valid and configured together. Disabling the valid_params field bit
+ * for flowid_cnt indicates no flow IDs other than the default are to be
+ * allocated and used by the receive channel. @ref flowid_start plus flowid_cnt
+ * cannot be greater than the number of receive flows in the receive channel's
+ * Navigator Subsystem. The additional flows must be assigned to the host, or a
+ * subordinate of the host, requesting configuration of the receive channel.
+ *
+ * @rx_pause_on_err: UDMAP receive channel pause on error configuration to be
+ * programmed into the rx_pause_on_err field of the channel's RCHAN_RCFG
+ * register.
+ *
+ * @rx_atype: UDMAP receive channel non Ring Accelerator access pointer
+ * interpretation configuration to be programmed into the rx_atype field of the
+ * channel's RCHAN_RCFG register.
+ *
+ * @rx_chan_type: UDMAP receive channel functional channel type and work passing
+ * mechanism configuration to be programmed into the rx_chan_type field of the
+ * channel's RCHAN_RCFG register.
+ *
+ * @rx_ignore_short: UDMAP receive channel short packet treatment configuration
+ * to be programmed into the rx_ignore_short field of the RCHAN_RCFG register.
+ *
+ * @rx_ignore_long: UDMAP receive channel long packet treatment configuration to
+ * be programmed into the rx_ignore_long field of the RCHAN_RCFG register.
+ */
+struct ti_sci_msg_rm_udmap_rx_ch_cfg_req {
+ struct ti_sci_msg_hdr hdr;
+ u32 valid_params;
+ u16 nav_id;
+ u16 index;
+ u16 rx_fetch_size;
+ u16 rxcq_qnum;
+ u8 rx_priority;
+ u8 rx_qos;
+ u8 rx_orderid;
+ u8 rx_sched_priority;
+ u16 flowid_start;
+ u16 flowid_cnt;
+ u8 rx_pause_on_err;
+ u8 rx_atype;
+ u8 rx_chan_type;
+ u8 rx_ignore_short;
+ u8 rx_ignore_long;
+} __packed;
+
+/**
+ * Response to configuring a UDMAP receive channel.
+ *
+ * @hdr: Standard TISCI header
+ */
+struct ti_sci_msg_rm_udmap_rx_ch_cfg_resp {
+ struct ti_sci_msg_hdr hdr;
+} __packed;
+
+/**
+ * Configures a Navigator Subsystem UDMAP receive flow
+ *
+ * Configures a Navigator Subsystem UDMAP receive flow's registers.
+ * Configuration does not include the flow registers which handle size-based
+ * free descriptor queue routing.
+ *
+ * The flow index must be assigned to the host defined in the TISCI header via
+ * the RM board configuration resource assignment range list.
+ *
+ * @hdr: Standard TISCI header
+ *
+ * @valid_params
+ * Bitfield defining validity of rx flow configuration parameters. The
+ * rx flow configuration fields are not valid, and will not be used for flow
+ * configuration, if their corresponding valid bit is zero. Valid bit usage:
+ * 0 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_einfo_present
+ * 1 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_psinfo_present
+ * 2 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_error_handling
+ * 3 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_desc_type
+ * 4 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_sop_offset
+ * 5 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_qnum
+ * 6 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_hi
+ * 7 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_lo
+ * 8 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_hi
+ * 9 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_lo
+ * 10 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_hi_sel
+ * 11 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_lo_sel
+ * 12 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_hi_sel
+ * 13 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_lo_sel
+ * 14 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq0_sz0_qnum
+ * 15 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq1_sz0_qnum
+ * 16 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq2_sz0_qnum
+ * 17 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq3_sz0_qnum
+ * 18 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_ps_location
+ *
+ * @nav_id: SoC device ID of Navigator Subsystem from which the receive flow is
+ * allocated
+ *
+ * @flow_index: UDMAP receive flow index for non-optional configuration.
+ *
+ * @rx_einfo_present:
+ * UDMAP receive flow extended packet info present configuration to be
+ * programmed into the rx_einfo_present field of the flow's RFLOW_RFA register.
+ *
+ * @rx_psinfo_present:
+ * UDMAP receive flow PS words present configuration to be programmed into the
+ * rx_psinfo_present field of the flow's RFLOW_RFA register.
+ *
+ * @rx_error_handling:
+ * UDMAP receive flow error handling configuration to be programmed into the
+ * rx_error_handling field of the flow's RFLOW_RFA register.
+ *
+ * @rx_desc_type:
+ * UDMAP receive flow descriptor type configuration to be programmed into the
+ * rx_desc_type field field of the flow's RFLOW_RFA register.
+ *
+ * @rx_sop_offset:
+ * UDMAP receive flow start of packet offset configuration to be programmed
+ * into the rx_sop_offset field of the RFLOW_RFA register. See the UDMAP
+ * section of the TRM for more information on this setting. Valid values for
+ * this field are 0-255 bytes.
+ *
+ * @rx_dest_qnum:
+ * UDMAP receive flow destination queue configuration to be programmed into the
+ * rx_dest_qnum field of the flow's RFLOW_RFA register. The specified
+ * destination queue must be valid within the Navigator Subsystem and must be
+ * owned by the host, or a subordinate of the host, requesting allocation and
+ * configuration of the receive flow.
+ *
+ * @rx_src_tag_hi:
+ * UDMAP receive flow source tag high byte constant configuration to be
+ * programmed into the rx_src_tag_hi field of the flow's RFLOW_RFB register.
+ * See the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_src_tag_lo:
+ * UDMAP receive flow source tag low byte constant configuration to be
+ * programmed into the rx_src_tag_lo field of the flow's RFLOW_RFB register.
+ * See the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_dest_tag_hi:
+ * UDMAP receive flow destination tag high byte constant configuration to be
+ * programmed into the rx_dest_tag_hi field of the flow's RFLOW_RFB register.
+ * See the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_dest_tag_lo:
+ * UDMAP receive flow destination tag low byte constant configuration to be
+ * programmed into the rx_dest_tag_lo field of the flow's RFLOW_RFB register.
+ * See the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_src_tag_hi_sel:
+ * UDMAP receive flow source tag high byte selector configuration to be
+ * programmed into the rx_src_tag_hi_sel field of the RFLOW_RFC register. See
+ * the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_src_tag_lo_sel:
+ * UDMAP receive flow source tag low byte selector configuration to be
+ * programmed into the rx_src_tag_lo_sel field of the RFLOW_RFC register. See
+ * the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_dest_tag_hi_sel:
+ * UDMAP receive flow destination tag high byte selector configuration to be
+ * programmed into the rx_dest_tag_hi_sel field of the RFLOW_RFC register. See
+ * the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_dest_tag_lo_sel:
+ * UDMAP receive flow destination tag low byte selector configuration to be
+ * programmed into the rx_dest_tag_lo_sel field of the RFLOW_RFC register. See
+ * the UDMAP section of the TRM for more information on this setting.
+ *
+ * @rx_fdq0_sz0_qnum:
+ * UDMAP receive flow free descriptor queue 0 configuration to be programmed
+ * into the rx_fdq0_sz0_qnum field of the flow's RFLOW_RFD register. See the
+ * UDMAP section of the TRM for more information on this setting. The specified
+ * free queue must be valid within the Navigator Subsystem and must be owned
+ * by the host, or a subordinate of the host, requesting allocation and
+ * configuration of the receive flow.
+ *
+ * @rx_fdq1_qnum:
+ * UDMAP receive flow free descriptor queue 1 configuration to be programmed
+ * into the rx_fdq1_qnum field of the flow's RFLOW_RFD register. See the
+ * UDMAP section of the TRM for more information on this setting. The specified
+ * free queue must be valid within the Navigator Subsystem and must be owned
+ * by the host, or a subordinate of the host, requesting allocation and
+ * configuration of the receive flow.
+ *
+ * @rx_fdq2_qnum:
+ * UDMAP receive flow free descriptor queue 2 configuration to be programmed
+ * into the rx_fdq2_qnum field of the flow's RFLOW_RFE register. See the
+ * UDMAP section of the TRM for more information on this setting. The specified
+ * free queue must be valid within the Navigator Subsystem and must be owned
+ * by the host, or a subordinate of the host, requesting allocation and
+ * configuration of the receive flow.
+ *
+ * @rx_fdq3_qnum:
+ * UDMAP receive flow free descriptor queue 3 configuration to be programmed
+ * into the rx_fdq3_qnum field of the flow's RFLOW_RFE register. See the
+ * UDMAP section of the TRM for more information on this setting. The specified
+ * free queue must be valid within the Navigator Subsystem and must be owned
+ * by the host, or a subordinate of the host, requesting allocation and
+ * configuration of the receive flow.
+ *
+ * @rx_ps_location:
+ * UDMAP receive flow PS words location configuration to be programmed into the
+ * rx_ps_location field of the flow's RFLOW_RFA register.
+ */
+struct ti_sci_msg_rm_udmap_flow_cfg_req {
+ struct ti_sci_msg_hdr hdr;
+ u32 valid_params;
+ u16 nav_id;
+ u16 flow_index;
+ u8 rx_einfo_present;
+ u8 rx_psinfo_present;
+ u8 rx_error_handling;
+ u8 rx_desc_type;
+ u16 rx_sop_offset;
+ u16 rx_dest_qnum;
+ u8 rx_src_tag_hi;
+ u8 rx_src_tag_lo;
+ u8 rx_dest_tag_hi;
+ u8 rx_dest_tag_lo;
+ u8 rx_src_tag_hi_sel;
+ u8 rx_src_tag_lo_sel;
+ u8 rx_dest_tag_hi_sel;
+ u8 rx_dest_tag_lo_sel;
+ u16 rx_fdq0_sz0_qnum;
+ u16 rx_fdq1_qnum;
+ u16 rx_fdq2_qnum;
+ u16 rx_fdq3_qnum;
+ u8 rx_ps_location;
+} __packed;
+
+/**
+ * Response to configuring a Navigator Subsystem UDMAP receive flow
+ *
+ * @hdr: Standard TISCI header
+ */
+struct ti_sci_msg_rm_udmap_flow_cfg_resp {
+ struct ti_sci_msg_hdr hdr;
+} __packed;
+
+#define FWL_MAX_PRIVID_SLOTS 3U
+
+/**
+ * struct ti_sci_msg_fwl_set_firewall_region_req - Request for configuring the firewall permissions.
+ *
+ * @hdr: Generic Header
+ *
+ * @fwl_id: Firewall ID in question
+ * @region: Region or channel number to set config info
+ * This field is unused in case of a simple firewall and must be initialized
+ * to zero. In case of a region based firewall, this field indicates the
+ * region in question. (index starting from 0) In case of a channel based
+ * firewall, this field indicates the channel in question (index starting
+ * from 0)
+ * @n_permission_regs: Number of permission registers to set
+ * @control: Contents of the firewall CONTROL register to set
+ * @permissions: Contents of the firewall PERMISSION register to set
+ * @start_address: Contents of the firewall START_ADDRESS register to set
+ * @end_address: Contents of the firewall END_ADDRESS register to set
+ */
+
+struct ti_sci_msg_fwl_set_firewall_region_req {
+ struct ti_sci_msg_hdr hdr;
+ u16 fwl_id;
+ u16 region;
+ u32 n_permission_regs;
+ u32 control;
+ u32 permissions[FWL_MAX_PRIVID_SLOTS];
+ u64 start_address;
+ u64 end_address;
+} __packed;
+
+/**
+ * struct ti_sci_msg_fwl_get_firewall_region_req - Request for retrieving the firewall permissions
+ *
+ * @hdr: Generic Header
+ *
+ * @fwl_id: Firewall ID in question
+ * @region: Region or channel number to get config info
+ * This field is unused in case of a simple firewall and must be initialized
+ * to zero. In case of a region based firewall, this field indicates the
+ * region in question (index starting from 0). In case of a channel based
+ * firewall, this field indicates the channel in question (index starting
+ * from 0).
+ * @n_permission_regs: Number of permission registers to retrieve
+ */
+struct ti_sci_msg_fwl_get_firewall_region_req {
+ struct ti_sci_msg_hdr hdr;
+ u16 fwl_id;
+ u16 region;
+ u32 n_permission_regs;
+} __packed;
+
+/**
+ * struct ti_sci_msg_fwl_get_firewall_region_resp - Response for retrieving the firewall permissions
+ *
+ * @hdr: Generic Header
+ *
+ * @fwl_id: Firewall ID in question
+ * @region: Region or channel number to set config info This field is
+ * unused in case of a simple firewall and must be initialized to zero. In
+ * case of a region based firewall, this field indicates the region in
+ * question. (index starting from 0) In case of a channel based firewall, this
+ * field indicates the channel in question (index starting from 0)
+ * @n_permission_regs: Number of permission registers retrieved
+ * @control: Contents of the firewall CONTROL register
+ * @permissions: Contents of the firewall PERMISSION registers
+ * @start_address: Contents of the firewall START_ADDRESS register This is not applicable for channelized firewalls.
+ * @end_address: Contents of the firewall END_ADDRESS register This is not applicable for channelized firewalls.
+ */
+struct ti_sci_msg_fwl_get_firewall_region_resp {
+ struct ti_sci_msg_hdr hdr;
+ u16 fwl_id;
+ u16 region;
+ u32 n_permission_regs;
+ u32 control;
+ u32 permissions[FWL_MAX_PRIVID_SLOTS];
+ u64 start_address;
+ u64 end_address;
+} __packed;
+
+/**
+ * struct ti_sci_msg_fwl_change_owner_info_req - Request for a firewall owner change
+ *
+ * @hdr: Generic Header
+ *
+ * @fwl_id: Firewall ID in question
+ * @region: Region or channel number if applicable
+ * @owner_index: New owner index to transfer ownership to
+ */
+struct ti_sci_msg_fwl_change_owner_info_req {
+ struct ti_sci_msg_hdr hdr;
+ u16 fwl_id;
+ u16 region;
+ u8 owner_index;
+} __packed;
+
+/**
+ * struct ti_sci_msg_fwl_change_owner_info_resp - Response for a firewall owner change
+ *
+ * @hdr: Generic Header
+ *
+ * @fwl_id: Firewall ID specified in request
+ * @region: Region or channel number specified in request
+ * @owner_index: Owner index specified in request
+ * @owner_privid: New owner priv-ID returned by DMSC.
+ * @owner_permission_bits: New owner permission bits returned by DMSC.
+ */
+struct ti_sci_msg_fwl_change_owner_info_resp {
+ struct ti_sci_msg_hdr hdr;
+ u16 fwl_id;
+ u16 region;
+ u8 owner_index;
+ u8 owner_privid;
+ u16 owner_permission_bits;
+} __packed;
+
+#endif /* __TI_SCI_H */
diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c
index 63d7398fd4..1a9a5c1b2c 100644
--- a/drivers/firmware/zynqmp-fpga.c
+++ b/drivers/firmware/zynqmp-fpga.c
@@ -15,7 +15,7 @@
#include <common.h>
#include <init.h>
#include <dma.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#define ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL BIT(0)
#define ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED BIT(1)
@@ -58,7 +58,7 @@ enum xilinx_byte_order {
struct fpgamgr {
struct firmware_handler fh;
- struct device_d dev;
+ struct device dev;
const struct zynqmp_eemi_ops *eemi_ops;
int programmed;
char *buf;
@@ -158,8 +158,8 @@ static int get_header_length(const char *header, size_t size)
return -EINVAL;
}
-static void zynqmp_fpga_show_header(const struct device_d *dev,
- struct bs_header *header, size_t size)
+static void zynqmp_fpga_show_header(const struct device *dev,
+ struct bs_header *header, size_t size)
{
struct bs_header_entry *entry;
unsigned int i;
@@ -197,6 +197,7 @@ static void zynqmp_fpga_show_header(const struct device_d *dev,
static int fpgamgr_program_finish(struct firmware_handler *fh)
{
struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh);
+ struct device *hw_dev = mgr->dev.parent;
u32 *buf_aligned;
u32 buf_size;
u32 *body;
@@ -205,7 +206,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
enum xilinx_byte_order byte_order;
dma_addr_t addr;
int status = 0;
- u8 flags = ZYNQMP_FPGA_BIT_ONLY_BIN;
+ u8 flags = 0;
if (!mgr->buf) {
status = -ENOBUFS;
@@ -254,9 +255,9 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
memcpy((u32 *)buf_aligned, body, body_length);
buf_aligned[body_length / sizeof(*buf_aligned)] = body_length;
- addr = dma_map_single(&mgr->dev, buf_aligned,
+ addr = dma_map_single(hw_dev, buf_aligned,
body_length + sizeof(buf_size), DMA_TO_DEVICE);
- if (dma_mapping_error(&mgr->dev, addr)) {
+ if (dma_mapping_error(hw_dev, addr)) {
status = -EFAULT;
goto err_free_dma;
}
@@ -267,7 +268,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
buf_size = addr + body_length;
status = mgr->eemi_ops->fpga_load((u64)addr, buf_size, flags);
- dma_unmap_single(&mgr->dev, addr, body_length + sizeof(buf_size),
+ dma_unmap_single(hw_dev, addr, body_length + sizeof(buf_size),
DMA_TO_DEVICE);
if (status < 0)
dev_err(&mgr->dev, "unable to load fpga\n");
@@ -326,11 +327,11 @@ static int programmed_get(struct param_d *p, void *priv)
return 0;
}
-static int zynqmp_fpga_probe(struct device_d *dev)
+static int zynqmp_fpga_probe(struct device *dev)
{
struct fpgamgr *mgr;
struct firmware_handler *fh;
- const char *alias = of_alias_get(dev->device_node);
+ const char *alias = of_alias_get(dev->of_node);
const char *model = NULL;
struct param_d *p;
u32 api_version;
@@ -347,7 +348,7 @@ static int zynqmp_fpga_probe(struct device_d *dev)
fh->open = fpgamgr_program_start;
fh->write = fpgamgr_program_write_buf;
fh->close = fpgamgr_program_finish;
- of_property_read_string(dev->device_node, "compatible", &model);
+ of_property_read_string(dev->of_node, "compatible", &model);
if (model)
fh->model = xstrdup(model);
fh->dev = dev;
@@ -381,7 +382,7 @@ static int zynqmp_fpga_probe(struct device_d *dev)
}
fh->dev = &mgr->dev;
- fh->device_node = dev->device_node;
+ fh->device_node = dev->of_node;
ret = firmwaremgr_register(fh);
if (ret != 0) {
@@ -405,8 +406,9 @@ static struct of_device_id zynqmpp_fpga_id_table[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, zynqmpp_fpga_id_table);
-static struct driver_d zynqmp_fpga_driver = {
+static struct driver zynqmp_fpga_driver = {
.name = "zynqmp_fpga_manager",
.of_compatible = DRV_OF_COMPAT(zynqmpp_fpga_id_table),
.probe = zynqmp_fpga_probe,
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index dd52b4cb20..5db24cb5bd 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -56,7 +56,7 @@ EXPORT_SYMBOL_GPL(fpga_bridge_disable);
struct fpga_bridge *of_fpga_bridge_get(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
struct fpga_bridge *bridge;
int ret = -EPROBE_DEFER;
@@ -179,7 +179,7 @@ static int set_enable(struct param_d *p, void *priv)
*
* Return: 0 for success, error code otherwise.
*/
-int fpga_bridge_register(struct device_d *dev, const char *name,
+int fpga_bridge_register(struct device *dev, const char *name,
const struct fpga_bridge_ops *br_ops, void *priv)
{
struct fpga_bridge *bridge;
@@ -201,7 +201,7 @@ int fpga_bridge_register(struct device_d *dev, const char *name,
bridge->priv = priv;
bridge->dev.parent = dev;
- bridge->dev.device_node = dev->device_node;
+ bridge->dev.of_node = dev->of_node;
bridge->dev.id = DEVICE_ID_DYNAMIC;
bridge->dev.name = xstrdup(name);
@@ -218,7 +218,7 @@ int fpga_bridge_register(struct device_d *dev, const char *name,
if (IS_ERR(p))
return PTR_ERR(p);
- of_platform_populate(dev->device_node, NULL, dev);
+ of_platform_populate(dev->of_node, NULL, dev);
dev_info(bridge->dev.parent, "fpga bridge [%s] registered\n",
bridge->dev.name);
diff --git a/drivers/fpga/socfpga-fpga2sdram-bridge.c b/drivers/fpga/socfpga-fpga2sdram-bridge.c
index 0b4ec39ee3..7f72bd8a65 100644
--- a/drivers/fpga/socfpga-fpga2sdram-bridge.c
+++ b/drivers/fpga/socfpga-fpga2sdram-bridge.c
@@ -52,7 +52,7 @@
#define F2S_BRIDGE_NAME "fpga2sdram"
struct alt_fpga2sdram_data {
- struct device_d *dev;
+ struct device *dev;
int mask;
};
@@ -68,9 +68,6 @@ static inline int _alt_fpga2sdram_enable_set(struct alt_fpga2sdram_data *priv,
else
val = 0;
- /* The kernel driver expects this value in this register :-( */
- writel(priv->mask, SOCFPGA_SYSMGR_ADDR + SYSMGR_ISWGRP_HANDOFF3);
-
dev_dbg(priv->dev, "setting fpgaportrst to 0x%08x\n", val);
return writel(val, SOCFPGA_SDRCTL_ADDR + ALT_SDR_CTL_FPGAPORTRST_OFST);
@@ -95,8 +92,9 @@ static struct of_device_id altera_fpga_of_match[] = {
{ .compatible = "altr,socfpga-fpga2sdram-bridge" },
{},
};
+MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
-static int alt_fpga_bridge_probe(struct device_d *dev)
+static int alt_fpga_bridge_probe(struct device *dev)
{
struct alt_fpga2sdram_data *priv;
int ret = 0;
@@ -105,8 +103,14 @@ static int alt_fpga_bridge_probe(struct device_d *dev)
if (!priv)
return -ENOMEM;
- /* enable all ports for now */
- priv->mask = ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK;
+ priv->mask = readl(SOCFPGA_SYSMGR_ADDR + SYSMGR_ISWGRP_HANDOFF3);
+ if (!priv->mask) {
+ /* enable all ports if we don't know better */
+ priv->mask = ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK;
+ /* The kernel driver expects this value in this register :-( */
+ writel(priv->mask, SOCFPGA_SYSMGR_ADDR + SYSMGR_ISWGRP_HANDOFF3);
+
+ }
priv->dev = dev;
@@ -120,7 +124,7 @@ static int alt_fpga_bridge_probe(struct device_d *dev)
return ret;
}
-static struct driver_d altera_fpga_driver = {
+static struct driver altera_fpga_driver = {
.probe = alt_fpga_bridge_probe,
.name = "altera-fpga2sdram-bridge",
.of_compatible = DRV_OF_COMPAT(altera_fpga_of_match),
diff --git a/drivers/fpga/socfpga-hps2fpga-bridge.c b/drivers/fpga/socfpga-hps2fpga-bridge.c
index b41db4512f..4855dbcf4d 100644
--- a/drivers/fpga/socfpga-hps2fpga-bridge.c
+++ b/drivers/fpga/socfpga-hps2fpga-bridge.c
@@ -39,7 +39,7 @@
#define FPGA2HPS_BRIDGE_NAME "fpga2hps"
struct altera_hps2fpga_data {
- struct device_d *dev;
+ struct device *dev;
const char *name;
struct reset_control *bridge_reset;
unsigned int remap_mask;
@@ -112,8 +112,9 @@ static struct of_device_id altera_fpga_of_match[] = {
.data = &fpga2hps_data },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
-static int alt_fpga_bridge_probe(struct device_d *dev)
+static int alt_fpga_bridge_probe(struct device *dev)
{
struct altera_hps2fpga_data *priv;
const struct of_device_id *of_id;
@@ -123,7 +124,7 @@ static int alt_fpga_bridge_probe(struct device_d *dev)
of_id = of_match_device(altera_fpga_of_match, dev);
priv = (struct altera_hps2fpga_data *)of_id->data;
- priv->bridge_reset = of_reset_control_get(dev->device_node, NULL);
+ priv->bridge_reset = of_reset_control_get(dev->of_node, NULL);
if (IS_ERR(priv->bridge_reset)) {
dev_err(dev, "Could not get %s reset control\n", priv->name);
return PTR_ERR(priv->bridge_reset);
@@ -143,7 +144,7 @@ static int alt_fpga_bridge_probe(struct device_d *dev)
priv->dev = dev;
- if (!of_property_read_u32(dev->device_node, "bridge-enable", &enable)) {
+ if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
if (enable > 1) {
dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
} else {
@@ -160,7 +161,7 @@ static int alt_fpga_bridge_probe(struct device_d *dev)
priv);
}
-static struct driver_d alt_fpga_bridge_driver = {
+static struct driver alt_fpga_bridge_driver = {
.probe = alt_fpga_bridge_probe,
.name = "altera-hps2fpga-bridge",
.of_compatible = DRV_OF_COMPAT(altera_fpga_of_match),
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ab75fe4ed9..d9bb68e06b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -45,8 +45,8 @@ config GPIO_CLPS711X
config GPIO_DAVINCI
bool "TI Davinci/Keystone GPIO support"
- default y if ARCH_DAVINCI
- depends on (ARM && ARCH_DAVINCI) || COMPILE_TEST
+ default y if ARCH_DAVINCI || ARCH_K3
+ depends on (ARM && (ARCH_DAVINCI || ARCH_K3)) || COMPILE_TEST
help
Say yes here to enable GPIO support for TI Davinci/Keystone SoCs.
@@ -63,7 +63,8 @@ config GPIO_IMX
config GPIO_VF610
bool "VF610 GPIO controller" if COMPILE_TEST
- default y if ARCH_VF610
+ depends on ARCH_IMX || ARCH_VF610 || COMPILE_TEST
+ default y if ARCH_VF610 || ARCH_IMX93
config GPIO_MXS
bool "MXS GPIO controller" if COMPILE_TEST
@@ -155,6 +156,13 @@ config GPIO_RASPBERRYPI_EXP
Turn on GPIO support for the expander on Raspberry Pi 3 boards, using
the firmware mailbox to communicate with VideoCore on BCM283x chips.
+config GPIO_ROCKCHIP
+ bool "Rockchip GPIO support"
+ depends on ARCH_ROCKCHIP
+ help
+ Say yes here to include the driver for the GPIO controller found on
+ Rockchip SoCs.
+
config GPIO_STMPE
depends on MFD_STMPE
bool "STMPE GPIO Expander"
@@ -205,6 +213,18 @@ config GPIO_ZYNQ
help
Say yes here to support Xilinx Zynq GPIO controller.
+config GPIO_LATCH
+ tristate "GPIO latch driver"
+ help
+ Say yes here to enable a driver for GPIO multiplexers based on latches
+ connected to other GPIOs.
+
+config GPIO_INTEL
+ bool "Intel GPIO driver"
+ depends on X86 || COMPILE_TEST
+ help
+ Say Y here to build support for the Intel GPIO driver.
+
endmenu
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fcb6a232e0..b0575ccf8c 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
+obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_DESIGNWARE) += gpio-dw.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
@@ -28,3 +29,5 @@ obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_STARFIVE) += gpio-starfive-vic.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
+obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o
+obj-$(CONFIG_GPIO_INTEL) += gpio-intel.o
diff --git a/drivers/gpio/gpio-74164.c b/drivers/gpio/gpio-74164.c
index 23dec89ec8..fb96e281b2 100644
--- a/drivers/gpio/gpio-74164.c
+++ b/drivers/gpio/gpio-74164.c
@@ -94,16 +94,16 @@ static struct platform_device_id gpio_74164_ids[] = {
{ }
};
-static int gpio_74164_probe(struct device_d *dev)
+static int gpio_74164_probe(struct device *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
struct gpio_74164 *priv;
u32 num_regs = 1;
dev->id = DEVICE_ID_DYNAMIC;
- if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) {
- dev->id = of_alias_get_id(dev->device_node, "gpio");
- of_property_read_u32(dev->device_node, "registers-number",
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) {
+ dev->id = of_alias_get_id(dev->of_node, "gpio");
+ of_property_read_u32(dev->of_node, "registers-number",
&num_regs);
}
@@ -121,7 +121,7 @@ static int gpio_74164_probe(struct device_d *dev)
return gpiochip_add(&priv->chip);
}
-static struct driver_d gpio_74164_driver = {
+static struct driver gpio_74164_driver = {
.name = "gpio-74164",
.probe = gpio_74164_probe,
.id_table = gpio_74164_ids,
diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c
index 5b688f4766..513d071c79 100644
--- a/drivers/gpio/gpio-74xx-mmio.c
+++ b/drivers/gpio/gpio-74xx-mmio.c
@@ -81,6 +81,7 @@ static const struct of_device_id mmio_74xx_gpio_ids[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, mmio_74xx_gpio_ids);
static inline
struct mmio_74xx_gpio_priv *to_mmio_74xx_gpio_priv(struct gpio_chip *gc)
@@ -120,7 +121,7 @@ static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
return -ENOTSUPP;
}
-static int mmio_74xx_gpio_probe(struct device_d *dev)
+static int mmio_74xx_gpio_probe(struct device *dev)
{
struct mmio_74xx_gpio_priv *priv;
void __iomem *dat;
@@ -152,7 +153,7 @@ static int mmio_74xx_gpio_probe(struct device_d *dev)
return gpiochip_add(gc);
}
-static struct driver_d mmio_74xx_gpio_driver = {
+static struct driver mmio_74xx_gpio_driver = {
.name = "74xx-mmio-gpio",
.of_compatible = DRV_OF_COMPAT(mmio_74xx_gpio_ids),
.probe = mmio_74xx_gpio_probe,
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index 72f76c8a54..b32e9552ce 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -102,11 +102,12 @@ static const struct of_device_id ath79_gpio_of_match[] = {
{ .compatible = "qca,ar7100-gpio" },
{},
};
+MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
-static int ath79_gpio_probe(struct device_d *dev)
+static int ath79_gpio_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int err;
if (!np) {
@@ -143,7 +144,7 @@ static int ath79_gpio_probe(struct device_d *dev)
return 0;
}
-static struct driver_d ath79_gpio_driver = {
+static struct driver ath79_gpio_driver = {
.name = "ath79-gpio",
.probe = ath79_gpio_probe,
.of_compatible = DRV_OF_COMPAT(ath79_gpio_of_match),
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
index cb4bd4025c..a59bdd212a 100644
--- a/drivers/gpio/gpio-clps711x.c
+++ b/drivers/gpio/gpio-clps711x.c
@@ -7,10 +7,10 @@
#include <linux/err.h>
#include <linux/basic_mmio_gpio.h>
-static int clps711x_gpio_probe(struct device_d *dev)
+static int clps711x_gpio_probe(struct device *dev)
{
struct resource *iores;
- int err, id = of_alias_get_id(dev->device_node, "gpio");
+ int err, id = of_alias_get_id(dev->of_node, "gpio");
void __iomem *dat, *dir = NULL, *dir_inv = NULL;
struct bgpio_chip *bgc;
@@ -63,8 +63,9 @@ static const struct of_device_id __maybe_unused clps711x_gpio_dt_ids[] = {
{ .compatible = "cirrus,ep7209-gpio", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, clps711x_gpio_dt_ids);
-static struct driver_d clps711x_gpio_driver = {
+static struct driver clps711x_gpio_driver = {
.name = "clps711x-gpio",
.probe = clps711x_gpio_probe,
.of_compatible = DRV_OF_COMPAT(clps711x_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index b4f2176606..831da2fc7f 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -30,40 +30,42 @@ struct davinci_gpio_controller {
struct gpio_chip chip;
/* Serialize access to GPIO registers */
void __iomem *regs;
- void __iomem *set_data;
- void __iomem *clr_data;
- void __iomem *in_data;
};
#define chip2controller(chip) \
container_of(chip, struct davinci_gpio_controller, chip)
-static struct davinci_gpio_regs __iomem *gpio2regs(void __iomem *gpio_base,
- unsigned gpio)
+static struct davinci_gpio_regs __iomem *gpio2regs(struct davinci_gpio_controller *d,
+ unsigned gpio)
{
void __iomem *ptr;
if (gpio < 32 * 1)
- ptr = gpio_base + 0x10;
+ ptr = d->regs + 0x10;
else if (gpio < 32 * 2)
- ptr = gpio_base + 0x38;
+ ptr = d->regs + 0x38;
else if (gpio < 32 * 3)
- ptr = gpio_base + 0x60;
+ ptr = d->regs + 0x60;
else if (gpio < 32 * 4)
- ptr = gpio_base + 0x88;
+ ptr = d->regs + 0x88;
else if (gpio < 32 * 5)
- ptr = gpio_base + 0xb0;
+ ptr = d->regs + 0xb0;
else
ptr = NULL;
return ptr;
}
+static inline u32 __gpio_mask(unsigned gpio)
+{
+ return 1 << (gpio % 32);
+}
+
static int davinci_get_direction(struct gpio_chip *chip, unsigned offset)
{
struct davinci_gpio_controller *d = chip2controller(chip);
- struct davinci_gpio_regs __iomem *g = d->regs;
+ struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset);
- return ((readl_relaxed(&g->dir)) & (1 << offset)) ?
+ return ((readl_relaxed(&g->dir)) & __gpio_mask(offset)) ?
GPIOF_DIR_IN : GPIOF_DIR_OUT;
}
@@ -71,9 +73,9 @@ static inline int __davinci_direction(struct gpio_chip *chip,
unsigned offset, bool out, int value)
{
struct davinci_gpio_controller *d = chip2controller(chip);
- struct davinci_gpio_regs __iomem *g = d->regs;
+ struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset);
u32 temp;
- u32 mask = 1 << offset;
+ u32 mask = __gpio_mask(offset);
temp = readl_relaxed(&g->dir);
if (out) {
@@ -108,9 +110,9 @@ davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value)
static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct davinci_gpio_controller *d = chip2controller(chip);
- struct davinci_gpio_regs __iomem *g = d->regs;
+ struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset);
- return ((1 << offset) & readl_relaxed(&g->in_data)) ? 1 : 0;
+ return (__gpio_mask(offset) & readl_relaxed(&g->in_data)) ? 1 : 0;
}
/*
@@ -120,9 +122,9 @@ static void
davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct davinci_gpio_controller *d = chip2controller(chip);
- struct davinci_gpio_regs __iomem *g = d->regs;
+ struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset);
- writel_relaxed((1 << offset), value ? &g->set_data : &g->clr_data);
+ writel_relaxed(__gpio_mask(offset), value ? &g->set_data : &g->clr_data);
}
static struct gpio_ops davinci_gpio_ops = {
@@ -133,17 +135,17 @@ static struct gpio_ops davinci_gpio_ops = {
.set = davinci_gpio_set,
};
-static int davinci_gpio_probe(struct device_d *dev)
+static int davinci_gpio_probe(struct device *dev)
{
struct resource *iores;
void __iomem *gpio_base;
int ret;
u32 val;
- int i, base;
unsigned ngpio;
struct davinci_gpio_controller *chips;
+ struct gpio_chip *gc;
- ret = of_property_read_u32(dev->device_node, "ti,ngpio", &val);
+ ret = of_property_read_u32(dev->of_node, "ti,ngpio", &val);
if (ret) {
dev_err(dev, "could not read 'ti,ngpio' property\n");
return -EINVAL;
@@ -154,7 +156,7 @@ static int davinci_gpio_probe(struct device_d *dev)
if (WARN_ON(ARCH_NR_GPIOS < ngpio))
ngpio = ARCH_NR_GPIOS;
- chips = xzalloc((ngpio / 32 + 1) * sizeof(*chips));
+ chips = xzalloc(sizeof(*chips));
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores)) {
@@ -163,37 +165,28 @@ static int davinci_gpio_probe(struct device_d *dev)
}
gpio_base = IOMEM(iores->start);
- for (i = 0, base = 0; base < ngpio; i++, base += 32) {
- struct davinci_gpio_regs __iomem *regs;
- struct gpio_chip *gc;
+ gc = &chips->chip;
+ gc->ops = &davinci_gpio_ops;
+ gc->dev = dev;
+ gc->ngpio = ngpio;
+ gc->base = -1;
- gc = &chips[i].chip;
- gc->ops = &davinci_gpio_ops;
+ chips->regs = gpio_base;
- gc->dev = dev;
- gc->base = base;
- gc->ngpio = ngpio - base;
- if (gc->ngpio > 32)
- gc->ngpio = 32;
-
- regs = gpio2regs(gpio_base, base);
- chips[i].regs = regs;
- chips[i].set_data = &regs->set_data;
- chips[i].clr_data = &regs->clr_data;
- chips[i].in_data = &regs->in_data;
-
- gpiochip_add(gc);
- }
+ gpiochip_add(gc);
return 0;
}
static struct of_device_id davinci_gpio_ids[] = {
+ { .compatible = "ti,keystone-gpio", },
+ { .compatible = "ti,am654-gpio", },
{ .compatible = "ti,dm6441-gpio", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, davinci_gpio_ids);
-static struct driver_d davinci_gpio_driver = {
+static struct driver davinci_gpio_driver = {
.name = "davinci_gpio",
.probe = davinci_gpio_probe,
.of_compatible = DRV_OF_COMPAT(davinci_gpio_ids),
diff --git a/drivers/gpio/gpio-digic.c b/drivers/gpio/gpio-digic.c
index 5a6dd95aae..7e2db85efe 100644
--- a/drivers/gpio/gpio-digic.c
+++ b/drivers/gpio/gpio-digic.c
@@ -106,7 +106,7 @@ static struct gpio_ops digic_gpio_ops = {
.set = digic_gpio_set_value,
};
-static int digic_gpio_probe(struct device_d *dev)
+static int digic_gpio_probe(struct device *dev)
{
struct resource *iores;
struct digic_gpio_chip *chip;
@@ -156,8 +156,9 @@ static __maybe_unused struct of_device_id digic_gpio_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, digic_gpio_dt_ids);
-static struct driver_d digic_gpio_driver = {
+static struct driver digic_gpio_driver = {
.name = "digic-gpio",
.probe = digic_gpio_probe,
.of_compatible = DRV_OF_COMPAT(digic_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-dw.c b/drivers/gpio/gpio-dw.c
index 448a04d4ba..e6eba6b423 100644
--- a/drivers/gpio/gpio-dw.c
+++ b/drivers/gpio/gpio-dw.c
@@ -104,7 +104,7 @@ static struct gpio_ops dw_gpio_ops = {
.set = dw_gpio_set,
};
-static int dw_gpio_add_port(struct device_d *dev, struct device_node *node,
+static int dw_gpio_add_port(struct device *dev, struct device_node *node,
struct dw_gpio *parent)
{
struct dw_gpio_instance *chip;
@@ -138,7 +138,7 @@ static int dw_gpio_add_port(struct device_d *dev, struct device_node *node,
return -ENODEV;
}
- chip->chip.dev->device_node = node;
+ chip->chip.dev->of_node = node;
ret = gpiochip_add(&chip->chip);
if (ret)
@@ -150,7 +150,7 @@ static int dw_gpio_add_port(struct device_d *dev, struct device_node *node,
return 0;
}
-static int dw_gpio_probe(struct device_d *dev)
+static int dw_gpio_probe(struct device *dev)
{
struct resource *iores;
struct dw_gpio *gpio;
@@ -163,7 +163,7 @@ static int dw_gpio_probe(struct device_d *dev)
return PTR_ERR(iores);
gpio->regs = IOMEM(iores->start);
- for_each_child_of_node(dev->device_node, node)
+ for_each_child_of_node(dev->of_node, node)
dw_gpio_add_port(dev, node, gpio);
return 0;
@@ -176,8 +176,9 @@ static __maybe_unused struct of_device_id dwgpio_match[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, dwgpio_match);
-static struct driver_d dwgpio_driver = {
+static struct driver dwgpio_driver = {
.name = "dw-apb-gpio",
.probe = dw_gpio_probe,
.of_compatible = DRV_OF_COMPAT(dwgpio_match),
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index 3066b3f7a1..1902c83960 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -239,7 +239,7 @@ static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
return 0;
}
-static int bgpio_setup_accessors(struct device_d *dev,
+static int bgpio_setup_accessors(struct device *dev,
struct bgpio_chip *bgc,
bool byte_be)
{
@@ -404,7 +404,7 @@ static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin)
* @flags: Different flags that will affect the behaviour of the device, such as
* endianness etc.
*/
-int bgpio_init(struct bgpio_chip *bgc, struct device_d *dev,
+int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
unsigned int sz, void __iomem *dat, void __iomem *set,
void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
unsigned long flags)
@@ -478,7 +478,7 @@ EXPORT_SYMBOL_GPL(bgpio_remove);
#ifdef CONFIG_GPIO_GENERIC_PLATFORM
-static void __iomem *bgpio_map(struct device_d *dev,
+static void __iomem *bgpio_map(struct device *dev,
const char *name,
resource_size_t sane_sz)
{
@@ -498,7 +498,7 @@ static void __iomem *bgpio_map(struct device_d *dev,
static const struct of_device_id bgpio_of_match[];
-static struct bgpio_pdata *bgpio_parse_dt(struct device_d *dev,
+static struct bgpio_pdata *bgpio_parse_dt(struct device *dev,
unsigned long *flags)
{
struct bgpio_pdata *pdata;
@@ -510,16 +510,16 @@ static struct bgpio_pdata *bgpio_parse_dt(struct device_d *dev,
pdata->base = -1;
- if (of_device_is_big_endian(dev->device_node))
+ if (of_device_is_big_endian(dev->of_node))
*flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER;
- if (of_property_read_bool(dev->device_node, "no-output"))
+ if (of_property_read_bool(dev->of_node, "no-output"))
*flags |= BGPIOF_NO_OUTPUT;
return pdata;
}
-static int bgpio_dev_probe(struct device_d *dev)
+static int bgpio_dev_probe(struct device *dev)
{
struct resource *r;
void __iomem *dat;
@@ -580,7 +580,7 @@ static int bgpio_dev_probe(struct device_d *dev)
return gpiochip_add(&bgc->gc);
}
-static void bgpio_dev_remove(struct device_d *dev)
+static void bgpio_dev_remove(struct device *dev)
{
struct bgpio_chip *bgc = dev->priv;
@@ -600,8 +600,9 @@ static const struct of_device_id bgpio_of_match[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, bgpio_of_match);
-static struct driver_d bgpio_driver = {
+static struct driver bgpio_driver = {
.name = "basic-mmio-gpio",
.of_compatible = bgpio_of_match,
.probe = bgpio_dev_probe,
diff --git a/drivers/gpio/gpio-imx.c b/drivers/gpio/gpio-imx.c
index 8ddec1244b..49c5555b98 100644
--- a/drivers/gpio/gpio-imx.c
+++ b/drivers/gpio/gpio-imx.c
@@ -117,7 +117,7 @@ static struct gpio_ops imx_gpio_ops = {
.get_direction = imx_get_direction,
};
-static int imx_gpio_probe(struct device_d *dev)
+static int imx_gpio_probe(struct device *dev)
{
struct resource *iores;
struct imx_gpio_chip *imxgpio;
@@ -135,7 +135,7 @@ static int imx_gpio_probe(struct device_d *dev)
imxgpio->base = IOMEM(iores->start);
imxgpio->chip.ops = &imx_gpio_ops;
if (dev->id < 0) {
- imxgpio->chip.base = of_alias_get_id(dev->device_node, "gpio");
+ imxgpio->chip.base = of_alias_get_id(dev->of_node, "gpio");
if (imxgpio->chip.base < 0)
return imxgpio->chip.base;
imxgpio->chip.base *= 32;
@@ -184,6 +184,7 @@ static __maybe_unused struct of_device_id imx_gpio_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_gpio_dt_ids);
static struct platform_device_id imx_gpio_ids[] = {
{
@@ -197,7 +198,7 @@ static struct platform_device_id imx_gpio_ids[] = {
},
};
-static struct driver_d imx_gpio_driver = {
+static struct driver imx_gpio_driver = {
.name = "imx-gpio",
.probe = imx_gpio_probe,
.of_compatible = DRV_OF_COMPAT(imx_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-intel.c b/drivers/gpio/gpio-intel.c
new file mode 100644
index 0000000000..ebec220f46
--- /dev/null
+++ b/drivers/gpio/gpio-intel.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2004 Tomas Marek, Elrest Solutions Company s.r.o.
+
+/*
+ * Based on the Linux kernel v6.8 drivers/pinctrl/intel/pinctrl-intel.c.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <gpio.h>
+
+#include <platform_data/gpio-intel.h>
+
+#define PADBAR 0x00c
+
+/* Offset from pad_regs */
+#define PADCFG0 0x000
+#define PADCFG0_RXEVCFG_MASK GENMASK(26, 25)
+#define PADCFG0_RXEVCFG_LEVEL (0 << 25)
+#define PADCFG0_RXEVCFG_EDGE (1 << 25)
+#define PADCFG0_RXEVCFG_DISABLED (2 << 25)
+#define PADCFG0_RXEVCFG_EDGE_BOTH (3 << 25)
+#define PADCFG0_PREGFRXSEL BIT(24)
+#define PADCFG0_RXINV BIT(23)
+#define PADCFG0_GPIROUTIOXAPIC BIT(20)
+#define PADCFG0_GPIROUTSCI BIT(19)
+#define PADCFG0_GPIROUTSMI BIT(18)
+#define PADCFG0_GPIROUTNMI BIT(17)
+#define PADCFG0_PMODE_SHIFT 10
+#define PADCFG0_PMODE_MASK GENMASK(13, 10)
+#define PADCFG0_PMODE_GPIO 0
+#define PADCFG0_GPIORXDIS BIT(9)
+#define PADCFG0_GPIOTXDIS BIT(8)
+#define PADCFG0_GPIORXSTATE BIT(1)
+#define PADCFG0_GPIOTXSTATE BIT(0)
+
+struct intel_gpio_chip {
+ struct gpio_chip chip;
+ void __iomem *community_pad_base;
+};
+
+static inline struct intel_gpio_chip *to_intel_gpio(struct gpio_chip *gc)
+{
+ return container_of(gc, struct intel_gpio_chip, chip);
+}
+
+static void __iomem *intel_gpio_padcfg0_reg(const struct intel_gpio_chip *chip,
+ const unsigned int gpio)
+{
+ const u32 pad_cfg_offset = 16;
+
+ return chip->community_pad_base + pad_cfg_offset * gpio;
+}
+
+static u32 intel_gpio_padcfg0_value(const struct intel_gpio_chip *chip,
+ const unsigned int gpio)
+{
+ return readl(intel_gpio_padcfg0_reg(chip, gpio));
+}
+
+static void intel_gpio_padcfg0_write(const struct intel_gpio_chip *chip,
+ const unsigned int gpio, u32 value)
+{
+ writel(value, intel_gpio_padcfg0_reg(chip, gpio));
+}
+
+static void intel_gpio_set_value(struct gpio_chip *gc, unsigned int gpio,
+ int value)
+{
+ struct intel_gpio_chip *chip = to_intel_gpio(gc);
+ u32 padcfg0;
+
+ padcfg0 = intel_gpio_padcfg0_value(chip, gpio);
+ if (value)
+ padcfg0 |= PADCFG0_GPIOTXSTATE;
+ else
+ padcfg0 &= ~PADCFG0_GPIOTXSTATE;
+ intel_gpio_padcfg0_write(chip, gpio, padcfg0);
+}
+
+static int intel_gpio_get_value(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct intel_gpio_chip *chip = to_intel_gpio(gc);
+ u32 padcfg0;
+
+ padcfg0 = intel_gpio_padcfg0_value(chip, gpio);
+ if (!(padcfg0 & PADCFG0_GPIOTXDIS))
+ return !!(padcfg0 & PADCFG0_GPIOTXSTATE);
+
+ return !!(padcfg0 & PADCFG0_GPIORXSTATE);
+}
+
+static int intel_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct intel_gpio_chip *chip = to_intel_gpio(gc);
+ u32 padcfg0;
+
+ padcfg0 = intel_gpio_padcfg0_value(chip, gpio);
+
+ if (padcfg0 & PADCFG0_PMODE_MASK)
+ return -EINVAL;
+
+ if (padcfg0 & PADCFG0_GPIOTXDIS)
+ return GPIOF_DIR_IN;
+
+ return GPIOF_DIR_OUT;
+}
+
+static int intel_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct intel_gpio_chip *chip = to_intel_gpio(gc);
+ u32 padcfg0;
+
+ padcfg0 = intel_gpio_padcfg0_value(chip, gpio);
+ padcfg0 &= ~PADCFG0_GPIORXDIS;
+ padcfg0 |= PADCFG0_GPIOTXDIS;
+ intel_gpio_padcfg0_write(chip, gpio, padcfg0);
+
+ return 0;
+}
+
+static int intel_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
+ int value)
+{
+ struct intel_gpio_chip *chip = to_intel_gpio(gc);
+ u32 padcfg0;
+
+ padcfg0 = intel_gpio_padcfg0_value(chip, gpio);
+ padcfg0 &= ~PADCFG0_GPIOTXDIS;
+ padcfg0 |= PADCFG0_GPIORXDIS;
+ if (value)
+ padcfg0 |= PADCFG0_GPIOTXSTATE;
+ else
+ padcfg0 &= ~PADCFG0_GPIOTXSTATE;
+ intel_gpio_padcfg0_write(chip, gpio, padcfg0);
+
+ return 0;
+}
+
+static struct gpio_ops intel_gpio_ops = {
+ .direction_input = intel_gpio_direction_input,
+ .direction_output = intel_gpio_direction_output,
+ .get_direction = intel_gpio_get_direction,
+ .get = intel_gpio_get_value,
+ .set = intel_gpio_set_value,
+};
+
+static int intel_gpio_probe(struct device *dev)
+{
+ const struct gpio_intel_platform_data *pdata;
+ struct intel_gpio_chip *intel_gpio;
+ void __iomem *community_pad_base;
+ void __iomem *community_base;
+ struct resource *iores;
+ int ret;
+
+ pdata = (struct gpio_intel_platform_data *)dev->platform_data;
+ if (!pdata) {
+ dev_err(dev, "Configuration missing!\n");
+ return -EINVAL;
+ }
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "Memory resource request failed: %ld\n",
+ PTR_ERR(iores));
+ return PTR_ERR(iores);
+ }
+
+ intel_gpio = xzalloc(sizeof(*intel_gpio));
+
+ intel_gpio->chip.ops = &intel_gpio_ops;
+ intel_gpio->chip.ngpio = pdata->ngpios;
+ intel_gpio->chip.dev = dev;
+
+ community_base = IOMEM(iores->start);
+ community_pad_base = IOMEM(community_base + readl(community_base + PADBAR));
+
+ intel_gpio->community_pad_base = community_pad_base;
+
+ ret = gpiochip_add(&intel_gpio->chip);
+
+ if (ret) {
+ dev_err(dev, "Couldn't add gpiochip: %pe\n", ERR_PTR(ret));
+ kfree(intel_gpio);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct driver_d intel_gpio_driver = {
+ .name = "intel-gpio",
+ .probe = intel_gpio_probe,
+};
+
+coredevice_platform_driver(intel_gpio_driver);
diff --git a/drivers/gpio/gpio-jz4740.c b/drivers/gpio/gpio-jz4740.c
index cf1afb9f1d..911f9f2470 100644
--- a/drivers/gpio/gpio-jz4740.c
+++ b/drivers/gpio/gpio-jz4740.c
@@ -78,7 +78,7 @@ static struct gpio_ops jz4740_gpio_ops = {
.set = jz4740_gpio_set_value,
};
-static int jz4740_gpio_probe(struct device_d *dev)
+static int jz4740_gpio_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -119,8 +119,9 @@ static __maybe_unused struct of_device_id jz4740_gpio_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, jz4740_gpio_dt_ids);
-static struct driver_d jz4740_gpio_driver = {
+static struct driver jz4740_gpio_driver = {
.name = "jz4740-gpio",
.probe = jz4740_gpio_probe,
.of_compatible = DRV_OF_COMPAT(jz4740_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
new file mode 100644
index 0000000000..9a1a87250e
--- /dev/null
+++ b/drivers/gpio/gpio-latch.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO latch driver
+ *
+ * Copyright (C) 2022 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This driver implements a GPIO (or better GPO as there is no input)
+ * multiplexer based on latches like this:
+ *
+ * CLK0 ----------------------. ,--------.
+ * CLK1 -------------------. `--------|> #0 |
+ * | | |
+ * OUT0 ----------------+--|-----------|D0 Q0|-----|<
+ * OUT1 --------------+-|--|-----------|D1 Q1|-----|<
+ * OUT2 ------------+-|-|--|-----------|D2 Q2|-----|<
+ * OUT3 ----------+-|-|-|--|-----------|D3 Q3|-----|<
+ * OUT4 --------+-|-|-|-|--|-----------|D4 Q4|-----|<
+ * OUT5 ------+-|-|-|-|-|--|-----------|D5 Q5|-----|<
+ * OUT6 ----+-|-|-|-|-|-|--|-----------|D6 Q6|-----|<
+ * OUT7 --+-|-|-|-|-|-|-|--|-----------|D7 Q7|-----|<
+ * | | | | | | | | | `--------'
+ * | | | | | | | | |
+ * | | | | | | | | | ,--------.
+ * | | | | | | | | `-----------|> #1 |
+ * | | | | | | | | | |
+ * | | | | | | | `--------------|D0 Q0|-----|<
+ * | | | | | | `----------------|D1 Q1|-----|<
+ * | | | | | `------------------|D2 Q2|-----|<
+ * | | | | `--------------------|D3 Q3|-----|<
+ * | | | `----------------------|D4 Q4|-----|<
+ * | | `------------------------|D5 Q5|-----|<
+ * | `--------------------------|D6 Q6|-----|<
+ * `----------------------------|D7 Q7|-----|<
+ * `--------'
+ *
+ * The above is just an example. The actual number of number of latches and
+ * the number of inputs per latch is derived from the number of GPIOs given
+ * in the corresponding device tree properties.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <of.h>
+#include <gpio.h>
+#include <init.h>
+#include <of_gpio.h>
+#include <linux/bitmap.h>
+
+struct gpio_latch_priv {
+ struct gpio_chip gc;
+ int *clk_gpios;
+ int *latched_gpios;
+ int n_latched_gpios;
+ unsigned int setup_duration_ns;
+ unsigned int clock_duration_ns;
+ unsigned long *shadow;
+};
+
+static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ return 0;
+}
+
+static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct gpio_latch_priv *priv = container_of(gc, struct gpio_latch_priv, gc);
+ int latch = offset / priv->n_latched_gpios;
+ int i;
+
+ assign_bit(offset, priv->shadow, val);
+
+ for (i = 0; i < priv->n_latched_gpios; i++)
+ gpio_set_value(priv->latched_gpios[i],
+ test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
+
+ ndelay(priv->setup_duration_ns);
+ gpio_set_value(priv->clk_gpios[latch], 1);
+ ndelay(priv->clock_duration_ns);
+ gpio_set_value(priv->clk_gpios[latch], 0);
+}
+static int gpio_latch_direction_output(struct gpio_chip *gc, unsigned gpio, int val)
+{
+ gpio_latch_set(gc, gpio, val);
+
+ return 0;
+}
+
+#define DURATION_NS_MAX 5000
+
+static struct gpio_ops gpio_latch_gpio_ops = {
+ .direction_output = gpio_latch_direction_output,
+ .set = gpio_latch_set,
+ .get_direction = gpio_latch_get_direction,
+};
+
+static int gpio_latch_probe(struct device *dev)
+{
+ struct gpio_latch_priv *priv;
+ int n_latches, i, ret;
+ struct device_node *np = dev->of_node;
+ enum of_gpio_flags flags;
+
+ priv = xzalloc(sizeof(*priv));
+
+ n_latches = of_gpio_named_count(np, "clk-gpios");
+ if (n_latches < 0) {
+ dev_err(dev, "invalid or missing clk-gpios");
+ ret = -EINVAL;
+ goto err_gpio;
+ }
+
+ priv->n_latched_gpios = of_gpio_named_count(np, "latched-gpios");
+ if (priv->n_latched_gpios < 0) {
+ dev_err(dev, "invalid or missing latched-gpios");
+ ret = -EINVAL;
+ goto err_gpio;
+ }
+
+ priv->clk_gpios = xzalloc(sizeof(int) * n_latches);
+ priv->latched_gpios = xzalloc(sizeof(int) * priv->n_latched_gpios);
+
+ for (i = 0; i < n_latches; i++) {
+ priv->clk_gpios[i] = of_get_named_gpio_flags(np, "clk-gpios",
+ i, &flags);
+ ret = gpio_request_one(priv->clk_gpios[i],
+ flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0,
+ dev_name(dev));
+ if (ret) {
+ dev_err(dev, "Cannot request gpio %d: %s\n", priv->clk_gpios[i],
+ strerror(-ret));
+ goto err_gpio;
+ }
+
+ gpio_direction_output(priv->clk_gpios[i], 0);
+ }
+
+ for (i = 0; i < priv->n_latched_gpios; i++) {
+ priv->latched_gpios[i] = of_get_named_gpio_flags(np, "latched-gpios",
+ i, &flags);
+ ret = gpio_request_one(priv->latched_gpios[i],
+ flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0,
+ dev_name(dev));
+ if (ret) {
+ dev_err(dev, "Cannot request gpio %d: %s\n", priv->latched_gpios[i],
+ strerror(-ret));
+ goto err_gpio;
+ }
+ }
+
+ priv->shadow = bitmap_zalloc(n_latches * priv->n_latched_gpios);
+
+ of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns);
+ if (priv->setup_duration_ns > DURATION_NS_MAX) {
+ dev_warn(dev, "setup-duration-ns too high, limit to %d\n",
+ DURATION_NS_MAX);
+ priv->setup_duration_ns = DURATION_NS_MAX;
+ }
+
+ of_property_read_u32(np, "clock-duration-ns", &priv->clock_duration_ns);
+ if (priv->clock_duration_ns > DURATION_NS_MAX) {
+ dev_warn(dev, "clock-duration-ns too high, limit to %d\n",
+ DURATION_NS_MAX);
+ priv->clock_duration_ns = DURATION_NS_MAX;
+ }
+
+ priv->gc.ops = &gpio_latch_gpio_ops;
+ priv->gc.ngpio = n_latches * priv->n_latched_gpios;
+ priv->gc.base = -1;
+ priv->gc.dev = dev;
+
+ return gpiochip_add(&priv->gc);
+
+err_gpio:
+ return ret;
+}
+
+static const struct of_device_id gpio_latch_ids[] = {
+ {
+ .compatible = "gpio-latch",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, gpio_latch_ids);
+
+static struct driver gpio_latch_driver = {
+ .name = "gpio-latch",
+ .probe = gpio_latch_probe,
+ .of_compatible = DRV_OF_COMPAT(gpio_latch_ids),
+};
+device_platform_driver(gpio_latch_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("GPIO latch driver");
diff --git a/drivers/gpio/gpio-libftdi1.c b/drivers/gpio/gpio-libftdi1.c
index d8e1fc8b34..b7c94b573d 100644
--- a/drivers/gpio/gpio-libftdi1.c
+++ b/drivers/gpio/gpio-libftdi1.c
@@ -66,7 +66,7 @@ static struct gpio_ops libftdi1_gpio_ops = {
.set = libftdi1_gpio_set_value,
};
-static int libftdi1_gpio_probe(struct device_d *dev)
+static int libftdi1_gpio_probe(struct device *dev)
{
struct libftdi1_gpio_chip *gpio;
struct ft2232_bitbang *ftbb;
@@ -74,13 +74,13 @@ static int libftdi1_gpio_probe(struct device_d *dev)
uint32_t id_vendor, id_product;
const char *i_serial_number = NULL;
- of_property_read_u32(dev->device_node, "usb,id_vendor",
+ of_property_read_u32(dev->of_node, "usb,id_vendor",
&id_vendor);
- of_property_read_u32(dev->device_node, "usb,id_product",
+ of_property_read_u32(dev->of_node, "usb,id_product",
&id_product);
- of_property_read_string(dev->device_node, "usb,i_serial_number",
+ of_property_read_string(dev->of_node, "usb,i_serial_number",
&i_serial_number);
ftbb = barebox_libftdi1_open(id_vendor, id_product,
@@ -112,13 +112,14 @@ static __maybe_unused const struct of_device_id libftdi1_gpio_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, libftdi1_gpio_dt_ids);
-static void libftdi1_gpio_remove(struct device_d *dev)
+static void libftdi1_gpio_remove(struct device *dev)
{
barebox_libftdi1_close();
}
-static struct driver_d libftdi1_gpio_driver = {
+static struct driver libftdi1_gpio_driver = {
.name = "libftdi1-gpio",
.probe = libftdi1_gpio_probe,
.remove = libftdi1_gpio_remove,
diff --git a/drivers/gpio/gpio-malta-fpga-i2c.c b/drivers/gpio/gpio-malta-fpga-i2c.c
index e835c3e4ab..d0e454015c 100644
--- a/drivers/gpio/gpio-malta-fpga-i2c.c
+++ b/drivers/gpio/gpio-malta-fpga-i2c.c
@@ -116,7 +116,7 @@ static struct gpio_ops malta_i2c_gpio_ops = {
.set = malta_i2c_gpio_set_value,
};
-static int malta_i2c_gpio_probe(struct device_d *dev)
+static int malta_i2c_gpio_probe(struct device *dev)
{
struct resource *iores;
void __iomem *gpio_base;
@@ -159,8 +159,9 @@ static __maybe_unused struct of_device_id malta_i2c_gpio_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, malta_i2c_gpio_dt_ids);
-static struct driver_d malta_i2c_gpio_driver = {
+static struct driver malta_i2c_gpio_driver = {
.name = "malta-fpga-i2c-gpio",
.probe = malta_i2c_gpio_probe,
.of_compatible = DRV_OF_COMPAT(malta_i2c_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index d48a8aa7fb..bf855958a3 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -34,7 +34,7 @@ struct mpc8xxx_gpio_devtype {
int (*gpio_get)(struct bgpio_chip *, unsigned int);
};
-static int mpc8xxx_probe(struct device_d *dev)
+static int mpc8xxx_probe(struct device *dev)
{
struct device_node *np;
struct resource *iores;
@@ -44,8 +44,8 @@ static int mpc8xxx_probe(struct device_d *dev)
mpc8xxx_gc = xzalloc(sizeof(*mpc8xxx_gc));
- if (dev->device_node) {
- np = dev->device_node;
+ if (dev->of_node) {
+ np = dev->of_node;
} else {
dev_err(dev, "no device_node\n");
return -ENODEV;
@@ -107,8 +107,9 @@ static __maybe_unused struct of_device_id mpc8xxx_gpio_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, mpc8xxx_gpio_ids);
-static struct driver_d mpc8xxx_driver = {
+static struct driver mpc8xxx_driver = {
.name = "mpc8xxx-gpio",
.probe = mpc8xxx_probe,
.of_compatible = DRV_OF_COMPAT(mpc8xxx_gpio_ids),
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index dde79f8fe8..770acb61c8 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -98,7 +98,7 @@ static struct gpio_ops mxs_gpio_ops = {
.get_direction = mxs_get_direction,
};
-static int mxs_gpio_probe(struct device_d *dev)
+static int mxs_gpio_probe(struct device *dev)
{
struct mxs_gpio_chip *mxsgpio;
struct mxs_gpio_regs *regs;
@@ -111,7 +111,7 @@ static int mxs_gpio_probe(struct device_d *dev)
mxsgpio = xzalloc(sizeof(*mxsgpio));
mxsgpio->chip.ops = &mxs_gpio_ops;
if (dev->id < 0) {
- id = of_alias_get_id(dev->device_node, "gpio");
+ id = of_alias_get_id(dev->of_node, "gpio");
if (id < 0)
return id;
mxsgpio->base = dev_get_mem_region(dev->parent, 0);
@@ -150,6 +150,7 @@ static __maybe_unused struct of_device_id mxs_gpio_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids);
static struct platform_device_id mxs_gpio_ids[] = {
{
@@ -163,7 +164,7 @@ static struct platform_device_id mxs_gpio_ids[] = {
},
};
-static struct driver_d mxs_gpio_driver = {
+static struct driver mxs_gpio_driver = {
.name = "gpio-mxs",
.probe = mxs_gpio_probe,
.of_compatible = DRV_OF_COMPAT(mxs_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index a328317ce8..3fcb7387e3 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -115,7 +115,7 @@ static struct gpio_ops omap_gpio_ops = {
.set = omap_gpio_set_value,
};
-static int omap_gpio_probe(struct device_d *dev)
+static int omap_gpio_probe(struct device *dev)
{
struct resource *iores;
struct omap_gpio_chip *omapgpio;
@@ -134,7 +134,7 @@ static int omap_gpio_probe(struct device_d *dev)
omapgpio->chip.ops = &omap_gpio_ops;
if (dev->id < 0) {
- omapgpio->chip.base = of_alias_get_id(dev->device_node, "gpio");
+ omapgpio->chip.base = of_alias_get_id(dev->of_node, "gpio");
if (omapgpio->chip.base < 0)
return omapgpio->chip.base;
omapgpio->chip.base *= 32;
@@ -161,8 +161,9 @@ static __maybe_unused struct of_device_id omap_gpio_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, omap_gpio_dt_ids);
-static struct driver_d omap_gpio_driver = {
+static struct driver omap_gpio_driver = {
.name = "omap-gpio",
.probe = omap_gpio_probe,
.of_compatible = DRV_OF_COMPAT(omap_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-orion.c b/drivers/gpio/gpio-orion.c
index afbcb9d321..0a1b50069b 100644
--- a/drivers/gpio/gpio-orion.c
+++ b/drivers/gpio/gpio-orion.c
@@ -78,7 +78,7 @@ static struct gpio_ops orion_gpio_ops = {
.set = orion_gpio_set_value,
};
-static int orion_gpio_probe(struct device_d *dev)
+static int orion_gpio_probe(struct device *dev)
{
struct resource *iores;
struct orion_gpio_chip *gpio;
@@ -94,13 +94,13 @@ static int orion_gpio_probe(struct device_d *dev)
gpio->chip.dev = dev;
gpio->chip.ops = &orion_gpio_ops;
- id = of_alias_get_id(dev->device_node, "gpio");
+ id = of_alias_get_id(dev->of_node, "gpio");
if (id < 0)
return id;
gpio->chip.base = id * 32;
gpio->chip.ngpio = 32;
- of_property_read_u32(dev->device_node, "ngpios", &gpio->chip.ngpio);
+ of_property_read_u32(dev->of_node, "ngpios", &gpio->chip.ngpio);
gpiochip_add(&gpio->chip);
@@ -113,8 +113,9 @@ static struct of_device_id orion_gpio_dt_ids[] = {
{ .compatible = "marvell,orion-gpio", },
{ }
};
+MODULE_DEVICE_TABLE(of, orion_gpio_dt_ids);
-static struct driver_d orion_gpio_driver = {
+static struct driver orion_gpio_driver = {
.name = "orion-gpio",
.probe = orion_gpio_probe,
.of_compatible = DRV_OF_COMPAT(orion_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 8d8cd21310..e303f6b21f 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -12,6 +12,8 @@
#include <common.h>
#include <malloc.h>
#include <driver.h>
+#include <linux/gpio/consumer.h>
+#include <regulator.h>
#include <xfuncs.h>
#include <errno.h>
#include <i2c/i2c.h>
@@ -37,6 +39,8 @@
#define PCA_GPIO_MASK 0x00FF
#define PCA_INT 0x0100
+#define PCA_PCAL 0x0200
+#define PCA_LATCH_INT (PCA_PCAL | PCA_INT)
#define PCA953X_TYPE 0x1000
#define PCA957X_TYPE 0x2000
@@ -56,6 +60,8 @@ static struct platform_device_id pca953x_id[] = {
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
+ { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
+
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
{ "max7313", 16 | PCA953X_TYPE | PCA_INT, },
@@ -406,12 +412,14 @@ out:
return ret;
}
-static int pca953x_probe(struct device_d *dev)
+static int pca953x_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long driver_data;
struct pca953x_platform_data *pdata;
struct pca953x_chip *chip;
+ struct regulator *reg;
+ struct gpio_desc *reset_gpio;
int ret;
u32 invert = 0;
@@ -435,6 +443,20 @@ static int pca953x_probe(struct device_d *dev)
chip->client = client;
+ reset_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio))
+ dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n");
+
+ reg = regulator_get(dev, "vcc");
+ if (IS_ERR(reg)) {
+ dev_warn(dev, "Failed to get 'vcc' regulator (ignored).\n");
+ reg = NULL;
+ }
+
+ ret = regulator_enable(reg);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable register\n");
+
chip->chip_type = driver_data & (PCA953X_TYPE | PCA957X_TYPE);
/* initialize cached registers from their original values.
@@ -453,6 +475,8 @@ static int pca953x_probe(struct device_d *dev)
if (ret)
return ret;
+ slice_depends_on(gpiochip_slice(&chip->gpio_chip), i2c_client_slice(client));
+
if (pdata && pdata->setup) {
ret = pdata->setup(client, chip->gpio_chip.base,
chip->gpio_chip.ngpio, pdata->context);
@@ -468,7 +492,10 @@ static int pca953x_probe(struct device_d *dev)
#define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
static const struct of_device_id pca953x_dt_ids[] = {
+ { .compatible = "nxp,pca6408", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), },
+ { .compatible = "nxp,pca9506", .data = OF_953X(40, PCA_INT), },
{ .compatible = "nxp,pca9534", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), },
@@ -483,21 +510,36 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
{ .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+ { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal9554b", .data = OF_953X( 8, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), },
+
{ .compatible = "maxim,max7310", .data = OF_953X( 8, 0), },
{ .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), },
{ .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), },
{ .compatible = "maxim,max7315", .data = OF_953X( 8, PCA_INT), },
+ { .compatible = "maxim,max7318", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,pca6107", .data = OF_953X( 8, PCA_INT), },
+ { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), },
{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
+ { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
+
+ { .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },
+ { .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), },
+ { .compatible = "onnn,pca9655", .data = OF_953X(16, PCA_INT), },
{ .compatible = "exar,xra1202", .data = OF_953X( 8, 0), },
{ }
};
+MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
-static struct driver_d pca953x_driver = {
+static struct driver pca953x_driver = {
.name = "pca953x",
.probe = pca953x_probe,
.of_compatible = DRV_OF_COMPAT(pca953x_dt_ids),
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 612ef3a82e..52c3a6d00a 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -145,10 +145,10 @@ static struct gpio_ops pcf857x_gpio_ops = {
.set = pcf857x_set,
};
-static int pcf857x_probe(struct device_d *dev)
+static int pcf857x_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct pcf857x *gpio;
unsigned long driver_data;
unsigned int n_latch = 0;
@@ -238,8 +238,9 @@ static const struct of_device_id pcf857x_dt_ids[] = {
{ .compatible = "maxim,max7329", .data = (void *)8 },
{ }
};
+MODULE_DEVICE_TABLE(of, pcf857x_dt_ids);
-static struct driver_d pcf857x_driver = {
+static struct driver pcf857x_driver = {
.name = "pcf857x",
.probe = pcf857x_probe,
.of_compatible = DRV_OF_COMPAT(pcf857x_dt_ids),
diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c
index b68f19bfd0..a021253489 100644
--- a/drivers/gpio/gpio-raspberrypi-exp.c
+++ b/drivers/gpio/gpio-raspberrypi-exp.c
@@ -11,7 +11,7 @@
#include <common.h>
#include <gpio.h>
#include <init.h>
-#include <mach/mbox.h>
+#include <mach/bcm283x/mbox.h>
#define NUM_GPIO 8
@@ -233,7 +233,7 @@ static struct gpio_ops rpi_exp_gpio_ops = {
.set = rpi_exp_gpio_set,
};
-static int rpi_exp_gpio_probe(struct device_d *dev)
+static int rpi_exp_gpio_probe(struct device *dev)
{
struct rpi_exp_gpio *rpi_gpio;
int ret;
@@ -262,8 +262,9 @@ static __maybe_unused struct of_device_id rpi_exp_gpio_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids);
-static struct driver_d rpi_exp_gpio_driver = {
+static struct driver rpi_exp_gpio_driver = {
.name = "rpi-exp-gpio",
.probe = rpi_exp_gpio_probe,
.of_compatible = DRV_OF_COMPAT(rpi_exp_gpio_ids),
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
new file mode 100644
index 0000000000..2c13e97b97
--- /dev/null
+++ b/drivers/gpio/gpio-rockchip.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <of.h>
+#include <gpio.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/basic_mmio_gpio.h>
+#include <mach/rockchip/rockchip.h>
+
+#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */
+#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */
+#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */
+
+struct rockchip_gpiochip {
+ struct device *dev;
+ void __iomem *reg_base;
+ struct clk *clk;
+ struct bgpio_chip bgpio_chip;
+};
+
+/* GPIO registers */
+enum {
+ RK_GPIO_SWPORT_DR = 0x00,
+ RK_GPIO_SWPORT_DDR = 0x04,
+ RK_GPIO_EXT_PORT = 0x50,
+};
+
+/* GPIO registers */
+enum {
+ RK_GPIOV2_DR_L = 0x00,
+ RK_GPIOV2_DR_H = 0x04,
+ RK_GPIOV2_DDR_L = 0x08,
+ RK_GPIOV2_DDR_H = 0x0c,
+ RK_GPIOV2_EXT_PORT = 0x70,
+};
+
+static struct rockchip_gpiochip *gc_to_rockchip_pinctrl(struct gpio_chip *gc)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return container_of(bgc, struct rockchip_gpiochip, bgpio_chip);
+}
+
+static int rockchip_gpiov2_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc);
+ u32 mask;
+
+ mask = 1 << (16 + (gpio % 16));
+
+ if (gpio < 16)
+ writel(mask, rgc->reg_base + RK_GPIOV2_DDR_L);
+ else
+ writel(mask, rgc->reg_base + RK_GPIOV2_DDR_H);
+
+ return 0;
+}
+
+static int rockchip_gpiov2_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc);
+ u32 r;
+
+ if (gpio < 16)
+ r = readl(rgc->reg_base + RK_GPIOV2_DDR_L);
+ else
+ r = readl(rgc->reg_base + RK_GPIOV2_DDR_H);
+
+ return r & BIT(gpio % 16) ? GPIOF_DIR_OUT : GPIOF_DIR_IN;
+}
+
+static void rockchip_gpiov2_set_value(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc);
+ u32 mask, vval = 0;
+
+ mask = 1 << (16 + (gpio % 16));
+ if (val)
+ vval = 1 << (gpio % 16);
+
+ if (gpio < 16)
+ writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_L);
+ else
+ writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_H);
+}
+
+static int rockchip_gpiov2_direction_output(struct gpio_chip *gc,
+ unsigned int gpio, int val)
+{
+ struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc);
+ u32 mask, out, vval = 0;
+
+ mask = 1 << (16 + (gpio % 16));
+ out = 1 << (gpio % 16);
+ if (val)
+ vval = 1 << (gpio % 16);
+
+ if (gpio < 16) {
+ writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_L);
+ writel(mask | out, rgc->reg_base + RK_GPIOV2_DDR_L);
+ } else {
+ writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_H);
+ writel(mask | out, rgc->reg_base + RK_GPIOV2_DDR_H);
+ }
+
+ return 0;
+}
+
+static int rockchip_gpiov2_get_value(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc);
+ u32 mask, r;
+
+ mask = 1 << (gpio % 32);
+ r = readl(rgc->reg_base + RK_GPIOV2_EXT_PORT);
+
+ return r & mask ? 1 : 0;
+}
+
+static struct gpio_ops rockchip_gpio_ops = {
+ .direction_input = rockchip_gpiov2_direction_input,
+ .direction_output = rockchip_gpiov2_direction_output,
+ .get = rockchip_gpiov2_get_value,
+ .set = rockchip_gpiov2_set_value,
+ .get_direction = rockchip_gpiov2_get_direction,
+};
+
+static int rockchip_gpio_probe(struct device *dev)
+{
+ struct rockchip_gpiochip *rgc;
+ struct gpio_chip *gpio;
+ struct resource *res;
+ void __iomem *reg_base;
+ u32 id, gpio_type;
+ int ret;
+
+ rgc = xzalloc(sizeof(*rgc));
+ gpio = &rgc->bgpio_chip.gc;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ rgc->reg_base = IOMEM(res->start);
+
+ rgc->clk = clk_get(dev, NULL);
+
+ if (IS_ERR(rgc->clk))
+ return PTR_ERR(rgc->clk);
+
+ ret = clk_enable(rgc->clk);
+ if (ret)
+ return ret;
+
+ reg_base = rgc->reg_base;
+
+ id = readl(reg_base + 0x78);
+ if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1)
+ gpio_type = GPIO_TYPE_V2;
+ else
+ gpio_type = GPIO_TYPE_V1;
+
+ if (gpio_type >= GPIO_TYPE_V2) {
+ gpio->ngpio = 32;
+ gpio->dev = dev;
+ gpio->ops = &rockchip_gpio_ops;
+ gpio->base = of_alias_get_id(dev->of_node, "gpio");
+ if (gpio->base < 0)
+ return -EINVAL;
+ gpio->base *= 32;
+ } else {
+ ret = bgpio_init(&rgc->bgpio_chip, dev, 4,
+ reg_base + RK_GPIO_EXT_PORT,
+ reg_base + RK_GPIO_SWPORT_DR, NULL,
+ reg_base + RK_GPIO_SWPORT_DDR, NULL, 0);
+ if (ret)
+ return ret;
+ }
+
+ ret = gpiochip_add(&rgc->bgpio_chip.gc);
+ if (ret) {
+ dev_err(dev, "failed to register gpio_chip:: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "registered GPIOv%d-compatible bank\n",
+ gpio_type == GPIO_TYPE_V1 ? 1 : 2);
+
+ return 0;
+}
+
+static struct of_device_id rockchip_gpio_dt_match[] = {
+ {
+ .compatible = "rockchip,gpio-bank",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, rockchip_gpio_dt_match);
+
+static struct driver rockchip_gpio_driver = {
+ .name = "rockchip-gpio",
+ .probe = rockchip_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(rockchip_gpio_dt_match),
+};
+
+core_platform_driver(rockchip_gpio_driver);
diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
index 4d0e12b0a8..58934fdfa7 100644
--- a/drivers/gpio/gpio-sifive.c
+++ b/drivers/gpio/gpio-sifive.c
@@ -28,7 +28,7 @@ static int __of_irq_count(struct device_node *np)
return npins / sizeof(__be32);
}
-static int sifive_gpio_probe(struct device_d *dev)
+static int sifive_gpio_probe(struct device *dev)
{
struct bgpio_chip *bgc;
struct resource *res;
@@ -44,7 +44,7 @@ static int sifive_gpio_probe(struct device_d *dev)
}
base = IOMEM(res->start);
- ngpio = __of_irq_count(dev->device_node);
+ ngpio = __of_irq_count(dev->of_node);
if (ngpio > SIFIVE_GPIO_MAX) {
dev_err(dev, "Too many GPIO interrupts (max=%d)\n",
SIFIVE_GPIO_MAX);
@@ -78,8 +78,9 @@ static const struct of_device_id sifive_gpio_match[] = {
{ .compatible = "sifive,fu540-c000-gpio" },
{ },
};
+MODULE_DEVICE_TABLE(of, sifive_gpio_match);
-static struct driver_d sifive_gpio_driver = {
+static struct driver sifive_gpio_driver = {
.name = "sifive_gpio",
.of_compatible = sifive_gpio_match,
.probe = sifive_gpio_probe,
diff --git a/drivers/gpio/gpio-starfive-vic.c b/drivers/gpio/gpio-starfive-vic.c
index a20fe0f346..399219a3a0 100644
--- a/drivers/gpio/gpio-starfive-vic.c
+++ b/drivers/gpio/gpio-starfive-vic.c
@@ -111,7 +111,7 @@ static struct gpio_ops starfive_gpio_ops = {
.set = starfive_set_value,
};
-static int starfive_gpio_probe(struct device_d *dev)
+static int starfive_gpio_probe(struct device *dev)
{
struct starfive_gpio *chip;
struct resource *res;
@@ -164,8 +164,9 @@ static const struct of_device_id starfive_gpio_match[] = {
{ .compatible = "starfive,gpio0", },
{ },
};
+MODULE_DEVICE_TABLE(of, starfive_gpio_match);
-static struct driver_d starfive_gpio_driver = {
+static struct driver starfive_gpio_driver = {
.probe = starfive_gpio_probe,
.name = "starfive_gpio",
.of_compatible = starfive_gpio_match,
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 4b572800a7..b736f66c7e 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -105,7 +105,7 @@ static struct gpio_ops stmpe_gpio_ops = {
.set = stmpe_gpio_set_value,
};
-static int stmpe_gpio_probe(struct device_d *dev)
+static int stmpe_gpio_probe(struct device *dev)
{
struct stmpe_gpio_chip *stmpegpio;
struct stmpe_client_info *ci;
@@ -136,7 +136,7 @@ static int stmpe_gpio_probe(struct device_d *dev)
return 0;
}
-static struct driver_d stmpe_gpio_driver = {
+static struct driver stmpe_gpio_driver = {
.name = "stmpe-gpio",
.probe = stmpe_gpio_probe,
};
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index 75c0be519d..ff7e86d644 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -19,7 +19,7 @@
#include <xfuncs.h>
#include <errno.h>
#include <i2c/i2c.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <gpio.h>
#include <of_device.h>
@@ -227,7 +227,7 @@ static struct gpio_ops sx150x_gpio_ops = {
.set = sx150x_gpio_set,
};
-static int sx150x_probe(struct device_d *dev)
+static int sx150x_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sx150x_gpio *sx150x;
@@ -255,8 +255,9 @@ static __maybe_unused struct of_device_id sx150x_dt_ids[] = {
{ .compatible = "semtech,sx1503q", .data = &sx1503q_device_data, },
{ }
};
+MODULE_DEVICE_TABLE(of, sx150x_dt_ids);
-static struct driver_d sx150x_driver = {
+static struct driver sx150x_driver = {
.name = "sx150x",
.probe = sx150x_probe,
.of_compatible = sx150x_dt_ids,
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 8ce4df4661..693432a8c9 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -125,7 +125,7 @@ static struct gpio_chip tegra_gpio_chip = {
.base = 0,
};
-static int tegra_gpio_probe(struct device_d *dev)
+static int tegra_gpio_probe(struct device *dev)
{
struct resource *iores;
int i, j, ret;
@@ -181,8 +181,9 @@ static __maybe_unused struct of_device_id tegra_gpio_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, tegra_gpio_dt_ids);
-static struct driver_d tegra_gpio_driver = {
+static struct driver tegra_gpio_driver = {
.name = "tegra-gpio",
.of_compatible = DRV_OF_COMPAT(tegra_gpio_dt_ids),
.probe = tegra_gpio_probe,
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index f8e31736f8..7c535c2e5e 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -21,6 +21,8 @@ struct vf610_gpio_port {
struct gpio_chip chip;
void __iomem *gpio_base;
unsigned int pinctrl_base;
+ bool have_paddr;
+ bool need_pinctrl;
};
#define GPIO_PDOR 0x00
@@ -28,19 +30,46 @@ struct vf610_gpio_port {
#define GPIO_PCOR 0x08
#define GPIO_PTOR 0x0c
#define GPIO_PDIR 0x10
+#define GPIO_PDDR 0x14
+
+struct fsl_gpio_soc_data {
+ /* SoCs has a Port Data Direction Register (PDDR) */
+ bool have_paddr;
+ bool need_pinctrl;
+};
+
+static const struct fsl_gpio_soc_data vf610_data = {
+ .need_pinctrl = true,
+};
+
+static const struct fsl_gpio_soc_data imx_data = {
+ .have_paddr = true,
+};
static const struct of_device_id vf610_gpio_dt_ids[] = {
- { .compatible = "fsl,vf610-gpio" },
+ { .compatible = "fsl,vf610-gpio", .data = &vf610_data, },
+ { .compatible = "fsl,imx7ulp-gpio", .data = &imx_data, },
+ { .compatible = "fsl,imx8ulp-gpio", .data = &imx_data, },
+ { .compatible = "fsl,imx93-gpio", .data = &imx_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, vf610_gpio_dt_ids);
static int vf610_gpio_get_value(struct gpio_chip *chip, unsigned int gpio)
{
struct vf610_gpio_port *port =
container_of(chip, struct vf610_gpio_port, chip);
+ unsigned long mask = BIT(gpio);
+ unsigned long offset = GPIO_PDIR;
- return !!(readl(port->gpio_base + GPIO_PDIR) & BIT(gpio));
+ if (port->have_paddr) {
+ mask &= readl(port->gpio_base + GPIO_PDDR);
+ if (mask)
+ offset = GPIO_PDOR;
+ }
+
+ return !!(readl(port->gpio_base + offset) & BIT(gpio));
}
static void vf610_gpio_set_value(struct gpio_chip *chip,
@@ -57,8 +86,19 @@ static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
{
struct vf610_gpio_port *port =
container_of(chip, struct vf610_gpio_port, chip);
+ unsigned long mask = BIT(gpio);
+ u32 val;
+
+ if (port->have_paddr) {
+ val = readl(port->gpio_base + GPIO_PDDR);
+ val &= ~mask;
+ writel(val, port->gpio_base + GPIO_PDDR);
+ }
+
+ if (port->need_pinctrl)
+ return pinctrl_gpio_direction_input(port->pinctrl_base + gpio);
- return pinctrl_gpio_direction_input(port->pinctrl_base + gpio);
+ return 0;
}
static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
@@ -66,18 +106,41 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
{
struct vf610_gpio_port *port =
container_of(chip, struct vf610_gpio_port, chip);
+ unsigned long mask = BIT(gpio);
+ u32 val;
vf610_gpio_set_value(chip, gpio, value);
- return pinctrl_gpio_direction_output(port->pinctrl_base + gpio);
+ if (port->have_paddr) {
+ val = readl(port->gpio_base + GPIO_PDDR);
+ val |= mask;
+ writel(val, port->gpio_base + GPIO_PDDR);
+ }
+
+ if (port->need_pinctrl)
+ return pinctrl_gpio_direction_output(port->pinctrl_base + gpio);
+
+ return 0;
}
static int vf610_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
{
struct vf610_gpio_port *port =
container_of(chip, struct vf610_gpio_port, chip);
+ u32 val;
+
+ if (port->have_paddr) {
+ val = readl(port->gpio_base + GPIO_PDDR);
+ if (val & BIT(gpio))
+ return GPIOF_DIR_OUT;
+ else
+ return GPIOF_DIR_IN;
+ }
+
+ if (port->need_pinctrl)
+ return pinctrl_gpio_get_direction(port->pinctrl_base + gpio);
- return pinctrl_gpio_get_direction(port->pinctrl_base + gpio);
+ return 0;
}
static struct gpio_ops vf610_gpio_ops = {
@@ -88,38 +151,56 @@ static struct gpio_ops vf610_gpio_ops = {
.get_direction = vf610_gpio_get_direction,
};
-static int vf610_gpio_probe(struct device_d *dev)
+static int vf610_gpio_probe(struct device *dev)
{
int ret, size;
struct resource *iores;
struct vf610_gpio_port *port;
const __be32 *gpio_ranges;
+ struct fsl_gpio_soc_data *devtype;
+
+ ret = dev_get_drvdata(dev, (const void **)&devtype);
+ if (ret)
+ return ret;
port = xzalloc(sizeof(*port));
- gpio_ranges = of_get_property(dev->device_node, "gpio-ranges", &size);
+ gpio_ranges = of_get_property(dev->of_node, "gpio-ranges", &size);
if (!gpio_ranges) {
- dev_err(dev, "Couldn't read 'gpio-ranges' propery of %s\n",
- dev->device_node->full_name);
+ dev_err(dev, "Couldn't read 'gpio-ranges' propery of %pOF\n",
+ dev->of_node);
ret = -EINVAL;
goto free_port;
}
+ port->have_paddr = devtype->have_paddr;
+ port->need_pinctrl = devtype->need_pinctrl;
+
port->pinctrl_base = be32_to_cpu(gpio_ranges[PINCTRL_BASE]);
- port->chip.ngpio = be32_to_cpu(gpio_ranges[COUNT]);
+ port->chip.ngpio = 32;
+ /*
+ * Some old bindings have two register ranges. When we have two ranges
+ * the GPIO base is in the second range. With only one range the GPIO
+ * base is at offset 0x40.
+ */
iores = dev_request_mem_resource(dev, 1);
if (IS_ERR(iores)) {
- ret = PTR_ERR(iores);
- dev_dbg(dev, "Failed to request memory resource\n");
- goto free_port;
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ ret = PTR_ERR(iores);
+ dev_dbg(dev, "Failed to request memory resource\n");
+ goto free_port;
+ } else {
+ port->gpio_base = IOMEM(iores->start) + 0x40;
+ }
+ } else {
+ port->gpio_base = IOMEM(iores->start);
}
- port->gpio_base = IOMEM(iores->start);
-
port->chip.ops = &vf610_gpio_ops;
if (dev->id < 0) {
- port->chip.base = of_alias_get_id(dev->device_node, "gpio");
+ port->chip.base = of_alias_get_id(dev->of_node, "gpio");
if (port->chip.base < 0) {
ret = port->chip.base;
dev_dbg(dev, "Failed to get GPIO alias\n");
@@ -140,7 +221,7 @@ free_port:
return ret;
}
-static struct driver_d vf610_gpio_driver = {
+static struct driver vf610_gpio_driver = {
.name = "gpio-vf610",
.probe = vf610_gpio_probe,
.of_compatible = DRV_OF_COMPAT(vf610_gpio_dt_ids),
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index bb77857611..1358182547 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -360,7 +360,7 @@ static struct gpio_ops zynq_gpio_ops = {
.get_direction = zynq_gpio_get_direction,
};
-static int zynqmp_gpio_probe(struct device_d *dev)
+static int zynqmp_gpio_probe(struct device *dev)
{
struct resource *iores;
struct zynq_gpio *gpio;
@@ -376,7 +376,7 @@ static int zynqmp_gpio_probe(struct device_d *dev)
}
gpio->base_addr = IOMEM(iores->start);
- gpio->chip.base = of_alias_get_id(dev->device_node, "gpio");
+ gpio->chip.base = of_alias_get_id(dev->of_node, "gpio");
gpio->chip.ops = &zynq_gpio_ops;
gpio->chip.ngpio = p_data->ngpio;
gpio->chip.dev = dev;
@@ -426,8 +426,9 @@ static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
{ /* end of table */ }
};
+MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
-static struct driver_d zynqmp_gpio_driver = {
+static struct driver zynqmp_gpio_driver = {
.name = "zynqmp-gpio",
.of_compatible = zynq_gpio_of_match,
.probe = zynqmp_gpio_probe,
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 7f20709035..a70e13eafc 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -7,39 +7,70 @@
#include <complete.h>
#include <gpio.h>
#include <of_gpio.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
+#include <linux/overflow.h>
#include <errno.h>
#include <malloc.h>
static LIST_HEAD(chip_list);
-struct gpio_info {
+struct gpio_desc {
struct gpio_chip *chip;
bool requested;
bool active_low;
char *label;
- char *name;
+ const char *name;
};
-static struct gpio_info *gpio_desc;
+/*
+ * This descriptor validation needs to be inserted verbatim into each
+ * function taking a descriptor, so we need to use a preprocessor
+ * macro to avoid endless duplication. If the desc is NULL it is an
+ * optional GPIO and calls should just bail out.
+ */
+static int validate_desc(const struct gpio_desc *desc, const char *func)
+{
+ if (!desc)
+ return 0;
+ if (IS_ERR(desc)) {
+ pr_warn("%s: invalid GPIO (errorpointer)\n", func);
+ return PTR_ERR(desc);
+ }
+
+ return 1;
+}
+
+#define VALIDATE_DESC(desc) do { \
+ int __valid = validate_desc(desc, __func__); \
+ if (__valid <= 0) \
+ return __valid; \
+ } while (0)
+
+#define VALIDATE_DESC_VOID(desc) do { \
+ int __valid = validate_desc(desc, __func__); \
+ if (__valid <= 0) \
+ return; \
+ } while (0)
+
+static struct gpio_desc *gpio_desc;
static int gpio_desc_alloc(void)
{
- gpio_desc = xzalloc(sizeof(struct gpio_info) * ARCH_NR_GPIOS);
+ gpio_desc = xzalloc(sizeof(struct gpio_desc) * ARCH_NR_GPIOS);
return 0;
}
pure_initcall(gpio_desc_alloc);
-static int gpio_ensure_requested(struct gpio_info *gi, int gpio)
+static int gpio_ensure_requested(struct gpio_desc *desc, int gpio)
{
- if (gi->requested)
+ if (desc->requested)
return 0;
return gpio_request(gpio, "gpio");
}
-static struct gpio_info *gpio_to_desc(unsigned gpio)
+static struct gpio_desc *gpio_to_desc(unsigned gpio)
{
if (gpio_is_valid(gpio))
if (gpio_desc[gpio].chip)
@@ -50,46 +81,46 @@ static struct gpio_info *gpio_to_desc(unsigned gpio)
return NULL;
}
-static unsigned gpioinfo_chip_offset(struct gpio_info *gi)
+static unsigned gpiodesc_chip_offset(const struct gpio_desc *desc)
{
- return (gi - gpio_desc) - gi->chip->base;
+ return (desc - gpio_desc) - desc->chip->base;
}
-static int gpio_adjust_value(struct gpio_info *gi,
+static int gpio_adjust_value(const struct gpio_desc *desc,
int value)
{
if (value < 0)
return value;
- return !!value ^ gi->active_low;
+ return !!value ^ desc->active_low;
}
-static int gpioinfo_request(struct gpio_info *gi, const char *label)
+static int gpiodesc_request(struct gpio_desc *desc, const char *label)
{
int ret;
- if (gi->requested) {
+ if (desc->requested) {
ret = -EBUSY;
goto done;
}
ret = 0;
- if (gi->chip->ops->request) {
- ret = gi->chip->ops->request(gi->chip,
- gpioinfo_chip_offset(gi));
+ if (desc->chip->ops->request) {
+ ret = desc->chip->ops->request(desc->chip,
+ gpiodesc_chip_offset(desc));
if (ret)
goto done;
}
- gi->requested = true;
- gi->active_low = false;
- gi->label = xstrdup(label);
+ desc->requested = true;
+ desc->active_low = false;
+ desc->label = xstrdup(label);
done:
if (ret)
pr_err("_gpio_request: gpio-%td (%s) status %d\n",
- gi - gpio_desc, label ? : "?", ret);
+ desc - gpio_desc, label ? : "?", ret);
return ret;
}
@@ -99,7 +130,7 @@ int gpio_find_by_label(const char *label)
int i;
for (i = 0; i < ARCH_NR_GPIOS; i++) {
- struct gpio_info *info = &gpio_desc[i];
+ struct gpio_desc *info = &gpio_desc[i];
if (!info->requested || !info->chip || !info->label)
continue;
@@ -116,7 +147,7 @@ int gpio_find_by_name(const char *name)
int i;
for (i = 0; i < ARCH_NR_GPIOS; i++) {
- struct gpio_info *info = &gpio_desc[i];
+ struct gpio_desc *info = &gpio_desc[i];
if (!info->chip || !info->name)
continue;
@@ -130,174 +161,307 @@ int gpio_find_by_name(const char *name)
int gpio_request(unsigned gpio, const char *label)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi) {
+ if (!desc) {
pr_err("_gpio_request: gpio-%d (%s) status %d\n",
gpio, label ? : "?", -ENODEV);
return -ENODEV;
}
- return gpioinfo_request(gi, label);
+ return gpiodesc_request(desc, label);
}
-static void gpioinfo_free(struct gpio_info *gi)
+bool gpiod_slice_acquired(struct gpio_desc *desc)
{
- if (!gi->requested)
+ if (!desc)
+ return false;
+
+ return slice_acquired(&desc->chip->slice);
+}
+
+bool gpio_slice_acquired(unsigned gpio)
+{
+ struct gpio_desc *desc = gpio_to_desc(gpio);
+
+ return gpiod_slice_acquired(desc);
+}
+
+static void gpiodesc_free(struct gpio_desc *desc)
+{
+ if (!desc->requested)
return;
- if (gi->chip->ops->free)
- gi->chip->ops->free(gi->chip, gpioinfo_chip_offset(gi));
+ if (desc->chip->ops->free)
+ desc->chip->ops->free(desc->chip, gpiodesc_chip_offset(desc));
- gi->requested = false;
- gi->active_low = false;
- free(gi->label);
- gi->label = NULL;
+ desc->requested = false;
+ desc->active_low = false;
+ free(desc->label);
+ desc->label = NULL;
}
void gpio_free(unsigned gpio)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi)
+ gpiodesc_free(desc);
+}
+
+/**
+ * gpiod_put - dispose of a GPIO descriptor
+ * @desc: GPIO descriptor to dispose of
+ *
+ * No descriptor can be used after gpiod_put() has been called on it.
+ */
+void gpiod_put(struct gpio_desc *desc)
+{
+ if (!desc)
return;
- gpioinfo_free(gi);
+ gpiodesc_free(desc);
+}
+EXPORT_SYMBOL(gpiod_put);
+
+/**
+ * gpiod_put_array - dispose of multiple GPIO descriptors
+ * @descs: struct gpio_descs containing an array of descriptors
+ */
+void gpiod_put_array(struct gpio_descs *descs)
+{
+ unsigned int i;
+
+ for (i = 0; i < descs->ndescs; i++)
+ gpiod_put(descs->desc[i]);
+
+ kfree(descs);
}
+EXPORT_SYMBOL_GPL(gpiod_put_array);
-static void gpioinfo_set_value(struct gpio_info *gi, int value)
+/**
+ * gpiod_set_raw_value() - assign a gpio's raw value
+ * @desc: gpio whose value will be assigned
+ * @value: value to assign
+ *
+ * Set the raw value of the GPIO, i.e. the value of its physical line without
+ * regard for its ACTIVE_LOW status.
+ */
+void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
- if (gi->chip->ops->set)
- gi->chip->ops->set(gi->chip, gpioinfo_chip_offset(gi), value);
+ VALIDATE_DESC_VOID(desc);
+
+ if (desc->chip->ops->set)
+ desc->chip->ops->set(desc->chip, gpiodesc_chip_offset(desc), value);
}
+EXPORT_SYMBOL(gpiod_set_raw_value);
void gpio_set_value(unsigned gpio, int value)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi)
+ if (!desc)
return;
- if (gpio_ensure_requested(gi, gpio))
+ if (gpio_ensure_requested(desc, gpio))
return;
- gpioinfo_set_value(gi, value);
+ gpiod_set_raw_value(desc, value);
}
EXPORT_SYMBOL(gpio_set_value);
+/**
+ * gpiod_set_value() - assign a gpio's value
+ * @desc: gpio whose value will be assigned
+ * @value: value to assign
+ *
+ * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW,
+ * OPEN_DRAIN and OPEN_SOURCE flags into account.
+ */
+void gpiod_set_value(struct gpio_desc *desc, int value)
+{
+ VALIDATE_DESC_VOID(desc);
+ gpiod_set_raw_value(desc, gpio_adjust_value(desc, value));
+}
+EXPORT_SYMBOL_GPL(gpiod_set_value);
+
void gpio_set_active(unsigned gpio, bool value)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi)
+ if (!desc)
return;
- gpio_set_value(gpio, gpio_adjust_value(gi, value));
+ gpiod_set_value(desc, value);
}
EXPORT_SYMBOL(gpio_set_active);
-static int gpioinfo_get_value(struct gpio_info *gi)
+/**
+ * gpiod_get_raw_value() - return a gpio's raw value
+ * @desc: gpio whose value will be returned
+ *
+ * Return the GPIO's raw value, i.e. the value of the physical line disregarding
+ * its ACTIVE_LOW status, or negative errno on failure.
+ */
+int gpiod_get_raw_value(const struct gpio_desc *desc)
{
- if (!gi->chip->ops->get)
+ VALIDATE_DESC(desc);
+
+ if (!desc->chip->ops->get)
return -ENOSYS;
- return gi->chip->ops->get(gi->chip, gpioinfo_chip_offset(gi));
+ return desc->chip->ops->get(desc->chip, gpiodesc_chip_offset(desc));
}
+EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
int gpio_get_value(unsigned gpio)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
int ret;
- if (!gi)
+ if (!desc)
return -ENODEV;
- ret = gpio_ensure_requested(gi, gpio);
+ ret = gpio_ensure_requested(desc, gpio);
if (ret)
return ret;
- return gpioinfo_get_value(gi);
+ return gpiod_get_raw_value(desc);
}
EXPORT_SYMBOL(gpio_get_value);
+/**
+ * gpiod_get_value() - return a gpio's value
+ * @desc: gpio whose value will be returned
+ *
+ * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into
+ * account, or negative errno on failure.
+ */
+int gpiod_get_value(const struct gpio_desc *desc)
+{
+ VALIDATE_DESC(desc);
+
+ return gpio_adjust_value(desc, gpiod_get_raw_value(desc));
+}
+EXPORT_SYMBOL_GPL(gpiod_get_value);
+
int gpio_is_active(unsigned gpio)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi)
+ if (!desc)
return -ENODEV;
- return gpio_adjust_value(gi, gpio_get_value(gpio));
+ return gpiod_get_value(desc);
}
EXPORT_SYMBOL(gpio_is_active);
-static int gpioinfo_direction_output(struct gpio_info *gi, int value)
+/**
+ * gpiod_direction_output_raw - set the GPIO direction to output
+ * @desc: GPIO to set to output
+ * @value: initial output value of the GPIO
+ *
+ * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
+ * be called safely on it. The initial value of the output must be specified
+ * as raw value on the physical line without regard for the ACTIVE_LOW status.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
- if (!gi->chip->ops->direction_output)
+ VALIDATE_DESC(desc);
+
+ if (!desc->chip->ops->direction_output)
return -ENOSYS;
- return gi->chip->ops->direction_output(gi->chip,
- gpioinfo_chip_offset(gi), value);
+ return desc->chip->ops->direction_output(desc->chip,
+ gpiodesc_chip_offset(desc), value);
}
+EXPORT_SYMBOL(gpiod_direction_output_raw);
int gpio_direction_output(unsigned gpio, int value)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
int ret;
- if (!gi)
+ if (!desc)
return -ENODEV;
- ret = gpio_ensure_requested(gi, gpio);
+ ret = gpio_ensure_requested(desc, gpio);
if (ret)
return ret;
- return gpioinfo_direction_output(gi, value);
+ return gpiod_direction_output_raw(desc, value);
}
EXPORT_SYMBOL(gpio_direction_output);
-static int gpioinfo_direction_active(struct gpio_info *gi, bool value)
+/**
+ * gpiod_direction_output - set the GPIO direction to output
+ * @desc: GPIO to set to output
+ * @value: initial output value of the GPIO
+ *
+ * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
+ * be called safely on it. The initial value of the output must be specified
+ * as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
+ * account.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_output(struct gpio_desc *desc, int value)
{
- return gpioinfo_direction_output(gi, gpio_adjust_value(gi, value));
+ VALIDATE_DESC(desc);
+
+ return gpiod_direction_output_raw(desc, gpio_adjust_value(desc, value));
}
int gpio_direction_active(unsigned gpio, bool value)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi)
+ if (!desc)
return -ENODEV;
- return gpioinfo_direction_active(gi, value);
+ return gpiod_direction_output(desc, value);
}
EXPORT_SYMBOL(gpio_direction_active);
-static int gpioinfo_direction_input(struct gpio_info *gi)
+/**
+ * gpiod_direction_input - set the GPIO direction to input
+ * @desc: GPIO to set to input
+ *
+ * Set the direction of the passed GPIO to input, such as gpiod_get_value() can
+ * be called safely on it.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_input(struct gpio_desc *desc)
{
- if (!gi->chip->ops->direction_input)
+ VALIDATE_DESC(desc);
+
+ if (!desc->chip->ops->direction_input)
return -ENOSYS;
- return gi->chip->ops->direction_input(gi->chip,
- gpioinfo_chip_offset(gi));
+ return desc->chip->ops->direction_input(desc->chip,
+ gpiodesc_chip_offset(desc));
}
+EXPORT_SYMBOL(gpiod_direction_input);
int gpio_direction_input(unsigned gpio)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
int ret;
- if (!gi)
+ if (!desc)
return -ENODEV;
- ret = gpio_ensure_requested(gi, gpio);
+ ret = gpio_ensure_requested(desc, gpio);
if (ret)
return ret;
- return gpioinfo_direction_input(gi);
+ return gpiod_direction_input(desc);
}
EXPORT_SYMBOL(gpio_direction_input);
-static int gpioinfo_request_one(struct gpio_info *gi, unsigned long flags,
+static int gpiodesc_request_one(struct gpio_desc *desc, unsigned long flags,
const char *label)
{
int err;
@@ -312,21 +476,21 @@ static int gpioinfo_request_one(struct gpio_info *gi, unsigned long flags,
const bool init_active = (flags & GPIOF_INIT_ACTIVE) == GPIOF_INIT_ACTIVE;
const bool init_high = (flags & GPIOF_INIT_HIGH) == GPIOF_INIT_HIGH;
- err = gpioinfo_request(gi, label);
+ err = gpiodesc_request(desc, label);
if (err)
return err;
- gi->active_low = active_low;
+ desc->active_low = active_low;
if (dir_in)
- err = gpioinfo_direction_input(gi);
+ err = gpiod_direction_input(desc);
else if (logical)
- err = gpioinfo_direction_active(gi, init_active);
+ err = gpiod_direction_output(desc, init_active);
else
- err = gpioinfo_direction_output(gi, init_high);
+ err = gpiod_direction_output_raw(desc, init_high);
if (err)
- gpioinfo_free(gi);
+ gpiodesc_free(desc);
return err;
}
@@ -339,12 +503,12 @@ static int gpioinfo_request_one(struct gpio_info *gi, unsigned long flags,
*/
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- if (!gi)
+ if (!desc)
return -ENODEV;
- return gpioinfo_request_one(gi, flags, label);
+ return gpiodesc_request_one(desc, flags, label);
}
EXPORT_SYMBOL_GPL(gpio_request_one);
@@ -440,31 +604,50 @@ static int gpiochip_find_base(int ngpio)
return base;
}
+#ifdef CONFIG_OF_GPIO
+
static int of_hog_gpio(struct device_node *np, struct gpio_chip *chip,
unsigned int idx)
{
- struct device_node *chip_np = chip->dev->device_node;
- struct of_phandle_args gpiospec;
+ struct device_node *chip_np = chip->dev->of_node;
unsigned long flags = 0;
- u32 gpio_flags;
+ u32 gpio_cells, gpio_num, gpio_flags;
int ret, gpio;
const char *name = NULL;
- ret = of_parse_phandle_with_args(chip_np, "gpios", "#gpio-cells", idx,
- &gpiospec);
+ ret = of_property_read_u32(chip_np, "#gpio-cells", &gpio_cells);
+ if (ret)
+ return ret;
+
+ /*
+ * Support for GPIOs that don't have #gpio-cells set to 2 is
+ * not implemented
+ */
+ if (WARN_ON(gpio_cells != 2))
+ return -ENOTSUPP;
+
+ ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells,
+ &gpio_num);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells + 1,
+ &gpio_flags);
if (ret)
return ret;
- gpio = gpio_of_xlate(chip->dev, &gpiospec, &gpio_flags);
+ if (gpio_flags & OF_GPIO_ACTIVE_LOW)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ gpio = gpio_get_num(chip->dev, gpio_num);
if (gpio == -EPROBE_DEFER)
return gpio;
+
if (gpio < 0) {
- dev_err(chip->dev, "unable to get gpio: %d\n", gpio);
+ dev_err(chip->dev, "unable to get gpio %u\n", gpio_num);
return gpio;
}
- if (gpio_flags & OF_GPIO_ACTIVE_LOW)
- flags |= GPIOF_ACTIVE_LOW;
/*
* Note that, in order to be compatible with Linux, the code
@@ -498,26 +681,9 @@ static int of_hog_gpio(struct device_node *np, struct gpio_chip *chip,
static int of_gpiochip_scan_hogs(struct gpio_chip *chip)
{
struct device_node *np;
- int ret, i, count;
-
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !chip->dev->device_node)
- return 0;
-
- count = of_property_count_strings(chip->dev->device_node, "gpio-line-names");
-
- if (count > 0) {
- const char **arr = xzalloc(count * sizeof(char *));
-
- of_property_read_string_array(chip->dev->device_node,
- "gpio-line-names", arr, count);
-
- for (i = 0; i < chip->ngpio && i < count; i++)
- gpio_desc[chip->base + i].name = xstrdup(arr[i]);
-
- free(arr);
- }
+ int ret, i;
- for_each_available_child_of_node(chip->dev->device_node, np) {
+ for_each_available_child_of_node(chip->dev->of_node, np) {
if (!of_property_read_bool(np, "gpio-hog"))
continue;
@@ -536,23 +702,143 @@ static int of_gpiochip_scan_hogs(struct gpio_chip *chip)
return 0;
}
+/*
+ * of_gpiochip_set_names - Set GPIO line names using OF properties
+ * @chip: GPIO chip whose lines should be named, if possible
+ *
+ * Looks for device property "gpio-line-names" and if it exists assigns
+ * GPIO line names for the chip. The memory allocated for the assigned
+ * names belong to the underlying firmware node and should not be released
+ * by the caller.
+ */
+static int of_gpiochip_set_names(struct gpio_chip *chip)
+{
+ struct device_node *np = dev_of_node(chip->dev);
+ const char **names;
+ int ret, i, count;
+
+ count = of_property_count_strings(np, "gpio-line-names");
+ if (count < 0)
+ return 0;
+
+ names = kcalloc(count, sizeof(*names), GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
+
+ ret = of_property_read_string_array(np, "gpio-line-names",
+ names, count);
+ if (ret < 0) {
+ kfree(names);
+ return ret;
+ }
+
+ /*
+ * Since property 'gpio-line-names' cannot contains gaps, we
+ * have to be sure we only assign those pins that really exists
+ * since chip->ngpio can be less.
+ */
+ if (count > chip->ngpio)
+ count = chip->ngpio;
+
+ for (i = 0; i < count; i++) {
+ /*
+ * Allow overriding "fixed" names provided by the GPIO
+ * provider. The "fixed" names are more often than not
+ * generic and less informative than the names given in
+ * device properties.
+ */
+ if (names[i] && names[i][0])
+ gpio_desc[chip->base + i].name = names[i];
+ }
+
+ free(names);
+
+ return 0;
+}
+
+/**
+ * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
+ * @gc: pointer to the gpio_chip structure
+ * @gpiospec: GPIO specifier as found in the device tree
+ * @flags: a flags pointer to fill in
+ *
+ * This is simple translation function, suitable for the most 1:1 mapped
+ * GPIO chips. This function performs only one sanity check: whether GPIO
+ * is less than ngpios (that is specified in the gpio_chip).
+ */
+static int of_gpio_simple_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ /*
+ * We're discouraging gpio_cells < 2, since that way you'll have to
+ * write your own xlate function (that will have to retrieve the GPIO
+ * number and the flags from a single gpio cell -- this is possible,
+ * but not recommended).
+ */
+ if (WARN_ON(gc->of_gpio_n_cells < 2))
+ return -EINVAL;
+
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] >= gc->ngpio)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gc->base + gpiospec->args[0];
+}
+
+static int of_gpiochip_add(struct gpio_chip *chip)
+{
+ struct device_node *np;
+ int ret;
+
+ np = dev_of_node(chip->dev);
+ if (!np)
+ return 0;
+
+ if (!chip->ops->of_xlate)
+ chip->ops->of_xlate = of_gpio_simple_xlate;
+
+ /*
+ * Separate check since the 'struct gpio_ops' is always the same for
+ * every 'struct gpio_chip' of the same instance (e.g. 'struct
+ * imx_gpio_chip').
+ */
+ if (chip->ops->of_xlate == of_gpio_simple_xlate)
+ chip->of_gpio_n_cells = 2;
+
+ if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
+ return -EINVAL;
+
+ ret = of_gpiochip_set_names(chip);
+ if (ret)
+ return ret;
+
+ return of_gpiochip_scan_hogs(chip);
+}
+#else
+static int of_gpiochip_add(struct gpio_chip *chip)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_OFDEVICE
static const char *gpio_suffixes[] = {
"gpios",
"gpio",
};
-/* Linux compatibility helper: Get a GPIO descriptor from device tree */
-int gpiod_get(struct device_d *dev, const char *_con_id, enum gpiod_flags flags)
+static struct property *of_find_gpio_property(struct device_node *np,
+ const char *_con_id)
{
- struct device_node *np = dev->device_node;
- enum of_gpio_flags of_flags;
- const char *label = dev_name(dev);
- char *buf = NULL, *con_id;
- int gpio;
- int ret, i;
-
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
- return -ENODEV;
+ struct property *pp = NULL;
+ char *con_id;
+ int i;
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
if (_con_id)
@@ -561,34 +847,171 @@ int gpiod_get(struct device_d *dev, const char *_con_id, enum gpiod_flags flags)
con_id = basprintf("%s", gpio_suffixes[i]);
if (!con_id)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- gpio = of_get_named_gpio_flags(np, con_id, 0, &of_flags);
+ pp = of_find_property(np, con_id, NULL);
free(con_id);
- if (gpio_is_valid(gpio))
- break;
+ if (pp)
+ return pp;
}
+ return NULL;
+}
+
+/* Linux compatibility helper: Get a GPIO descriptor from device tree */
+struct gpio_desc *dev_gpiod_get_index(struct device *dev,
+ struct device_node *np,
+ const char *con_id, int index,
+ enum gpiod_flags flags,
+ const char *label)
+{
+ struct gpio_desc *desc = NULL;
+ enum of_gpio_flags of_flags;
+ struct property *pp;
+ char *buf = NULL;
+ int gpio;
+ int ret;
+
+ if (!np)
+ return ERR_PTR(-ENODEV);
+
+ pp = of_find_gpio_property(np, con_id);
+ if (!pp)
+ return ERR_PTR(-ENOENT);
+
+ gpio = of_get_named_gpio_flags(dev->device_node, pp->name,
+ index, &of_flags);
if (!gpio_is_valid(gpio))
- return gpio < 0 ? gpio : -EINVAL;
+ return ERR_PTR(gpio < 0 ? gpio : -EINVAL);
+
+ desc = gpio_to_desc(gpio);
if (of_flags & OF_GPIO_ACTIVE_LOW)
flags |= GPIOF_ACTIVE_LOW;
buf = NULL;
- if (_con_id) {
- label = buf = basprintf("%s-%s", dev_name(dev), _con_id);
- if (!label)
- return -ENOMEM;
+ if (!label) {
+ if (con_id)
+ label = buf = basprintf("%s-%s", dev_name(dev), con_id);
+ else
+ label = dev_name(dev);
}
- ret = gpio_request_one(gpio, flags, label);
+ ret = gpiodesc_request_one(desc, flags, label);
free(buf);
- return ret ?: gpio;
+ return ret ? ERR_PTR(ret): desc;
+}
+
+/**
+ * gpiod_count - return the number of GPIOs associated with a device / function
+ * or -ENOENT if no GPIO has been assigned to the requested function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @_con_id: function within the GPIO consumer
+ */
+int gpiod_count(struct device *dev, const char *con_id)
+{
+ struct device_node *np = dev_of_node(dev);
+ struct property *pp;
+
+ if (!np)
+ return -ENODEV;
+
+ pp = of_find_gpio_property(np, con_id);
+ if (!pp)
+ return -ENOENT;
+
+ return of_gpio_named_count(np, pp->name);
}
+EXPORT_SYMBOL_GPL(gpiod_count);
+
+/**
+ * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This function acquires all the GPIOs defined under a given function.
+ *
+ * Return a struct gpio_descs containing an array of descriptors, -ENOENT if
+ * no GPIO has been assigned to the requested function, or another IS_ERR()
+ * code if an error occurred while trying to acquire the GPIOs.
+ */
+struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ struct gpio_desc *desc;
+ struct gpio_descs *descs;
+ int count;
+
+ count = gpiod_count(dev, con_id);
+ if (count < 0)
+ return ERR_PTR(count);
+
+ descs = kzalloc(struct_size(descs, desc, count), GFP_KERNEL);
+ if (!descs)
+ return ERR_PTR(-ENOMEM);
+
+ for (descs->ndescs = 0; descs->ndescs < count; descs->ndescs++) {
+ desc = dev_gpiod_get_index(dev, dev_of_node(dev), con_id,
+ descs->ndescs, flags, NULL);
+ if (IS_ERR(desc)) {
+ gpiod_put_array(descs);
+ return ERR_CAST(desc);
+ }
+
+ descs->desc[descs->ndescs] = desc;
+ }
+
+ return descs;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_array);
+
+#endif
+
+static int gpiod_set_array_value_complex(bool raw,
+ unsigned int array_size,
+ struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
+{
+ int i;
+
+ BUG_ON(array_info != NULL);
+
+ for (i = 0; i < array_size; i++)
+ gpiod_set_value(desc_array[i], test_bit(i, value_bitmap));
+
+ return 0;
+}
+
+/**
+ * gpiod_set_array_value() - assign values to an array of GPIOs
+ * @array_size: number of elements in the descriptor array / value bitmap
+ * @desc_array: array of GPIO descriptors whose values will be assigned
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap of values to assign
+ *
+ * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account. NOTE: This function has no special handling for GPIOs
+ * in the same bank that could've been set atomically: GPIO sequencing
+ * is not guaranteed to always remain in the same order.
+ */
+int gpiod_set_array_value(unsigned int array_size,
+ struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
+{
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_set_array_value_complex(false, array_size,
+ desc_array, array_info,
+ value_bitmap);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_array_value);
int gpiochip_add(struct gpio_chip *chip)
{
@@ -605,13 +1028,14 @@ int gpiochip_add(struct gpio_chip *chip)
return -ENOSPC;
}
+ slice_init(&chip->slice, dev_name(chip->dev));
list_add_tail(&chip->list, &chip_list);
for (i = chip->base; i < chip->base + chip->ngpio; i++)
gpio_desc[i].chip = chip;
- return of_gpiochip_scan_hogs(chip);
+ return of_gpiochip_add(chip);
}
void gpiochip_remove(struct gpio_chip *chip)
@@ -619,24 +1043,7 @@ void gpiochip_remove(struct gpio_chip *chip)
list_del(&chip->list);
}
-static int of_gpio_simple_xlate(struct gpio_chip *chip,
- const struct of_phandle_args *gpiospec,
- u32 *flags)
-{
- /*
- * Support for GPIOs that don't have #gpio-cells set to 2 is
- * not implemented
- */
- if (WARN_ON(gpiospec->args_count != 2))
- return -ENOTSUPP;
-
- if (flags)
- *flags = gpiospec->args[1];
-
- return chip->base + gpiospec->args[0];
-}
-
-struct gpio_chip *gpio_get_chip_by_dev(struct device_d *dev)
+struct gpio_chip *gpio_get_chip_by_dev(struct device *dev)
{
struct gpio_chip *chip;
@@ -648,7 +1055,7 @@ struct gpio_chip *gpio_get_chip_by_dev(struct device_d *dev)
return NULL;
}
-int gpio_of_xlate(struct device_d *dev, struct of_phandle_args *gpiospec, int *flags)
+int gpio_get_num(struct device *dev, int gpio)
{
struct gpio_chip *chip;
@@ -659,17 +1066,14 @@ int gpio_of_xlate(struct device_d *dev, struct of_phandle_args *gpiospec, int *f
if (!chip)
return -EPROBE_DEFER;
- if (chip->ops->of_xlate)
- return chip->ops->of_xlate(chip, gpiospec, flags);
- else
- return of_gpio_simple_xlate(chip, gpiospec, flags);
+ return chip->base + gpio;
}
struct gpio_chip *gpio_get_chip(int gpio)
{
- struct gpio_info *gi = gpio_to_desc(gpio);
+ struct gpio_desc *desc = gpio_to_desc(gpio);
- return gi ? gi->chip : NULL;
+ return desc ? desc->chip : NULL;
}
#ifdef CONFIG_CMD_GPIO
@@ -681,8 +1085,8 @@ static int do_gpiolib(int argc, char *argv[])
if (argc > 2)
return COMMAND_ERROR_USAGE;
- if (argc == 1) {
- struct device_d *dev;
+ if (argc > 1) {
+ struct device *dev;
dev = find_device(argv[1]);
if (!dev)
@@ -694,38 +1098,38 @@ static int do_gpiolib(int argc, char *argv[])
}
for (i = 0; i < ARCH_NR_GPIOS; i++) {
- struct gpio_info *gi = &gpio_desc[i];
+ struct gpio_desc *desc = &gpio_desc[i];
int val = -1, dir = -1;
int idx;
- if (!gi->chip)
+ if (!desc->chip)
continue;
- if (chip && chip != gi->chip)
+ if (chip && chip != desc->chip)
continue;
/* print chip information and header on first gpio */
- if (gi->chip->base == i) {
+ if (desc->chip->base == i) {
printf("\nGPIOs %u-%u, chip %s:\n",
- gi->chip->base,
- gi->chip->base + gi->chip->ngpio - 1,
- gi->chip->dev->name);
+ desc->chip->base,
+ desc->chip->base + desc->chip->ngpio - 1,
+ dev_name(desc->chip->dev));
printf(" %-3s %-3s %-9s %-20s %-20s\n", "dir", "val", "requested", "name", "label");
}
- idx = i - gi->chip->base;
+ idx = i - desc->chip->base;
- if (gi->chip->ops->get_direction)
- dir = gi->chip->ops->get_direction(gi->chip, idx);
- if (gi->chip->ops->get)
- val = gi->chip->ops->get(gi->chip, idx);
+ if (desc->chip->ops->get_direction)
+ dir = desc->chip->ops->get_direction(desc->chip, idx);
+ if (desc->chip->ops->get)
+ val = desc->chip->ops->get(desc->chip, idx);
printf(" GPIO %4d: %-3s %-3s %-9s %-20s %-20s\n", chip ? idx : i,
(dir < 0) ? "unk" : ((dir == GPIOF_DIR_IN) ? "in" : "out"),
(val < 0) ? "unk" : ((val == 0) ? "lo" : "hi"),
- gi->requested ? (gi->active_low ? "active low" : "true") : "false",
- gi->name ? gi->name : "",
- gi->label ? gi->label : "");
+ desc->requested ? (desc->active_low ? "active low" : "true") : "false",
+ desc->name ? desc->name : "",
+ desc->label ? desc->label : "");
}
return 0;
diff --git a/drivers/hab/hab.c b/drivers/hab/hab.c
index 2746f27a1e..ed091058d8 100644
--- a/drivers/hab/hab.c
+++ b/drivers/hab/hab.c
@@ -5,14 +5,17 @@
#include <fcntl.h>
#include <environment.h>
#include <libfile.h>
-#include <mach/generic.h>
+#include <mach/imx/generic.h>
#include <hab.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <fs.h>
-#include <mach/iim.h>
-#include <mach/imx25-fusemap.h>
-#include <mach/ocotp.h>
-#include <mach/imx6-fusemap.h>
+#include <mach/imx/iim.h>
+#include <mach/imx/imx25-fusemap.h>
+#include <mach/imx/ocotp.h>
+#include <mach/imx/imx6-fusemap.h>
+#include <mach/imx/ele.h>
+
+#include "hab.h"
bool imx_hab_srk_hash_valid(const void *buf)
{
@@ -94,7 +97,7 @@ static int imx_hab_permanent_write_enable_iim(int enable)
return imx_iim_permanent_write(enable);
}
-static int imx_hab_lockdown_device_iim(void)
+static int imx_hab_lockdown_device_iim(unsigned flags)
{
return imx_iim_write_field(IMX25_IIM_HAB_TYPE, 3);
}
@@ -150,7 +153,7 @@ static int imx_hab_permanent_write_enable_ocotp(int enable)
return imx_ocotp_permanent_write(enable);
}
-static int imx_hab_lockdown_device_ocotp(void)
+static int imx6_hab_lockdown_device_ocotp(unsigned flags)
{
int ret;
@@ -161,7 +164,22 @@ static int imx_hab_lockdown_device_ocotp(void)
return imx_ocotp_write_field(OCOTP_SEC_CONFIG_1, 1);
}
-static int imx_hab_device_locked_down_ocotp(void)
+static int imx8m_hab_lockdown_device_ocotp(unsigned flags)
+{
+ int ret;
+
+ ret = imx_ocotp_write_field(MX8M_OCOTP_SEC_CONFIG_1, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Only i.MX8MQ requires fusing of DIR_BT_DIS */
+ if (!cpu_is_mx8mq())
+ return ret;
+
+ return imx_ocotp_write_field(MX8MQ_OCOTP_DIR_BT_DIS, 1);
+}
+
+static int imx6_hab_device_locked_down_ocotp(void)
{
int ret;
unsigned int v;
@@ -173,13 +191,25 @@ static int imx_hab_device_locked_down_ocotp(void)
return v;
}
+static int imx8m_hab_device_locked_down_ocotp(void)
+{
+ int ret;
+ unsigned int v;
+
+ ret = imx_ocotp_read_field(MX8M_OCOTP_SEC_CONFIG_1, &v);
+ if (ret < 0)
+ return ret;
+
+ return v;
+}
+
struct imx_hab_ops {
- int (*init)(void);
int (*write_srk_hash)(const u8 *srk, unsigned flags);
int (*read_srk_hash)(u8 *srk);
int (*permanent_write_enable)(int enable);
- int (*lockdown_device)(void);
+ int (*lockdown_device)(unsigned flags);
int (*device_locked_down)(void);
+ int (*print_status)(void);
};
static struct imx_hab_ops imx_hab_ops_iim = {
@@ -188,39 +218,143 @@ static struct imx_hab_ops imx_hab_ops_iim = {
.lockdown_device = imx_hab_lockdown_device_iim,
.device_locked_down = imx_hab_device_locked_down_iim,
.permanent_write_enable = imx_hab_permanent_write_enable_iim,
+ .print_status = imx25_hab_print_status,
};
-static struct imx_hab_ops imx_hab_ops_ocotp = {
+static struct imx_hab_ops imx6_hab_ops_ocotp = {
.write_srk_hash = imx_hab_write_srk_hash_ocotp,
.read_srk_hash = imx_hab_read_srk_hash_ocotp,
- .lockdown_device = imx_hab_lockdown_device_ocotp,
- .device_locked_down = imx_hab_device_locked_down_ocotp,
+ .lockdown_device = imx6_hab_lockdown_device_ocotp,
+ .device_locked_down = imx6_hab_device_locked_down_ocotp,
.permanent_write_enable = imx_hab_permanent_write_enable_ocotp,
+ .print_status = imx6_hab_print_status,
};
-static struct imx_hab_ops *imx_get_hab_ops(void)
+static struct imx_hab_ops imx8m_hab_ops_ocotp = {
+ .write_srk_hash = imx_hab_write_srk_hash_ocotp,
+ .read_srk_hash = imx_hab_read_srk_hash_ocotp,
+ .lockdown_device = imx8m_hab_lockdown_device_ocotp,
+ .device_locked_down = imx8m_hab_device_locked_down_ocotp,
+ .permanent_write_enable = imx_hab_permanent_write_enable_ocotp,
+ .print_status = imx8m_hab_print_status,
+};
+
+static int imx_ahab_write_srk_hash(const u8 *__newsrk, unsigned flags)
+{
+ u32 *newsrk = (u32 *)__newsrk;
+ u32 resp;
+ int ret, i;
+
+ if (!(flags & IMX_SRK_HASH_WRITE_PERMANENT)) {
+ pr_err("Cannot write fuses temporarily\n");
+ return -EPERM;
+ }
+
+ for (i = 0; i < 32 / sizeof(u32); i++) {
+ ret = ele_write_fuse(0x80 + i, newsrk[i], false, &resp);
+ if (ret)
+ pr_err("Writing fuse index 0x%02x failed with %d, response 0x%08x\n",
+ i, ret, resp);
+ }
+
+ return 0;
+}
+
+static int imx_ahab_read_srk_hash(u8 *__srk)
+{
+ u32 *srk = (u32 *)__srk;
+ u32 resp;
+ int ret, i;
+
+ for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) {
+ ret = ele_read_common_fuse(0x80 + i, &srk[i], &resp);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_ahab_permanent_write_enable(int enable)
+{
+ return 0;
+}
+
+static int imx_ahab_lockdown_device(unsigned flags)
{
- static struct imx_hab_ops *ops, *tmp;
+ unsigned int lc;
int ret;
+ if (!(flags & IMX_SRK_HASH_WRITE_PERMANENT)) {
+ pr_err("Cannot write fuses temporarily\n");
+ return -EPERM;
+ }
+
+ lc = imx93_ahab_read_lifecycle();
+ if (lc == ELE_LIFECYCLE_OEM_CLOSED) {
+ pr_info("already OEM closed\n");
+ return 0;
+ }
+
+ if (lc != ELE_LIFECYCLE_OEM_OPEN) {
+ pr_err("Current lifecycle is NOT OEM open, can't move to OEM closed\n");
+ return -EPERM;
+ }
+
+ ret = ele_forward_lifecycle(ELE_LIFECYCLE_OEM_CLOSED, NULL);
+ if (ret) {
+ printf("failed to forward lifecycle to OEM closed\n");
+ return ret;
+ }
+
+ printf("Change to OEM closed successfully\n");
+
+ return 0;
+}
+
+static int imx_ahab_device_locked_down(void)
+{
+ return imx93_ahab_read_lifecycle() != ELE_LIFECYCLE_OEM_OPEN;
+}
+
+static int imx_ahab_print_status(void)
+{
+ int ret;
+
+ ret = ele_print_events();
+ if (ret)
+ pr_err("Cannot read ELE events: %pe\n", ERR_PTR(ret));
+
+ return ret;
+}
+
+static struct imx_hab_ops imx93_ahab_ops = {
+ .write_srk_hash = imx_ahab_write_srk_hash,
+ .read_srk_hash = imx_ahab_read_srk_hash,
+ .lockdown_device = imx_ahab_lockdown_device,
+ .device_locked_down = imx_ahab_device_locked_down,
+ .permanent_write_enable = imx_ahab_permanent_write_enable,
+ .print_status = imx_ahab_print_status,
+};
+
+static struct imx_hab_ops *imx_get_hab_ops(void)
+{
+ static struct imx_hab_ops *ops;
+
if (ops)
return ops;
- if (IS_ENABLED(CONFIG_HABV3) && (cpu_is_mx25() || cpu_is_mx35()))
- tmp = &imx_hab_ops_iim;
- else if (IS_ENABLED(CONFIG_HABV4) && (cpu_is_mx6() || cpu_is_mx8mq()))
- tmp = &imx_hab_ops_ocotp;
+ if (IS_ENABLED(CONFIG_HABV3) && cpu_is_mx25())
+ ops = &imx_hab_ops_iim;
+ else if (IS_ENABLED(CONFIG_HABV4) && cpu_is_mx6())
+ ops = &imx6_hab_ops_ocotp;
+ else if (IS_ENABLED(CONFIG_HABV4) && cpu_is_mx8m())
+ ops = &imx8m_hab_ops_ocotp;
+ else if (IS_ENABLED(CONFIG_AHAB) && cpu_is_mx93())
+ ops = &imx93_ahab_ops;
else
return NULL;
- if (tmp->init) {
- ret = tmp->init();
- if (ret)
- return NULL;
- }
-
- ops = tmp;
-
return ops;
}
@@ -318,7 +452,7 @@ int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags)
int imx_hab_lockdown_device(unsigned flags)
{
struct imx_hab_ops *ops = imx_get_hab_ops();
- u8 srk[SRK_HASH_SIZE];
+ u8 srk[SRK_HASH_SIZE] = {};
int ret;
ret = imx_hab_read_srk_hash(srk);
@@ -339,7 +473,7 @@ int imx_hab_lockdown_device(unsigned flags)
return ret;
}
- ret = ops->lockdown_device();
+ ret = ops->lockdown_device(flags);
if (flags & IMX_SRK_HASH_WRITE_PERMANENT)
ops->permanent_write_enable(0);
@@ -351,5 +485,26 @@ int imx_hab_device_locked_down(void)
{
struct imx_hab_ops *ops = imx_get_hab_ops();
+ if (!ops)
+ return -ENOSYS;
+
return ops->device_locked_down();
}
+
+int imx_hab_print_status(void)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ if (!ops)
+ return -ENOSYS;
+
+ return ops->print_status();
+}
+
+static int init_imx_hab_print_status(void)
+{
+ imx_hab_print_status();
+
+ return 0;
+}
+postmmu_initcall(init_imx_hab_print_status);
diff --git a/drivers/hab/hab.h b/drivers/hab/hab.h
new file mode 100644
index 0000000000..7be0e8386b
--- /dev/null
+++ b/drivers/hab/hab.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef __DRIVER_HAB_HAB_H
+#define __DRIVER_HAB_HAB_H
+
+int imx25_hab_print_status(void);
+int imx6_hab_print_status(void);
+int imx8m_hab_print_status(void);
+
+#endif /* __DRIVER_HAB_HAB_H */
diff --git a/drivers/hab/habv3.c b/drivers/hab/habv3.c
index 235db78c10..e28e9998d7 100644
--- a/drivers/hab/habv3.c
+++ b/drivers/hab/habv3.c
@@ -5,7 +5,7 @@
#include <common.h>
#include <hab.h>
#include <io.h>
-#include <mach/generic.h>
+#include <mach/imx/generic.h>
struct hab_status {
u8 value;
@@ -69,11 +69,7 @@ static int imx_habv3_get_status(uint32_t status)
return -EPERM;
}
-int imx25_hab_get_status(void)
+int imx25_hab_print_status(void)
{
- if (!cpu_is_mx25())
- return 0;
-
return imx_habv3_get_status(readl(IOMEM(0x780018d4)));
}
-postmmu_initcall(imx25_hab_get_status);
diff --git a/drivers/hab/habv4.c b/drivers/hab/habv4.c
index 9101fc1cae..0f5618116e 100644
--- a/drivers/hab/habv4.c
+++ b/drivers/hab/habv4.c
@@ -11,13 +11,18 @@
#include <hab.h>
#include <init.h>
#include <types.h>
+#include <mmu.h>
+#include <dma.h>
+#include <zero_page.h>
+#include <linux/sizes.h>
#include <linux/arm-smccc.h>
#include <asm/cache.h>
-#include <mach/generic.h>
-#include <mach/imx8mq.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx8mq.h>
+
+#include "hab.h"
-#define HABV4_RVT_IMX28 0xffff8af8
#define HABV4_RVT_IMX6_OLD 0x00000094
#define HABV4_RVT_IMX6_NEW 0x00000098
#define HABV4_RVT_IMX6UL 0x00000100
@@ -65,18 +70,6 @@ enum hab_config {
HAB_CONFIG_CLOSED = 0xcc, /* Secure IC */
};
-/* State definitions */
-enum hab_state {
- HAB_STATE_INITIAL = 0x33, /* Initialising state (transitory) */
- HAB_STATE_CHECK = 0x55, /* Check state (non-secure) */
- HAB_STATE_NONSECURE = 0x66, /* Non-secure state */
- HAB_STATE_TRUSTED = 0x99, /* Trusted state */
- HAB_STATE_SECURE = 0xaa, /* Secure state */
- HAB_STATE_FAIL_SOFT = 0xcc, /* Soft fail state */
- HAB_STATE_FAIL_HARD = 0xff, /* Hard fail state (terminal) */
- HAB_STATE_NONE = 0xf0, /* No security state machine */
-};
-
enum hab_reason {
HAB_REASON_RSN_ANY = 0x00, /* Match any reason */
HAB_REASON_UNS_COMMAND = 0x03, /* Unsupported command */
@@ -153,33 +146,79 @@ struct hab_header {
uint8_t par;
} __packed;
-typedef enum hab_status hab_loader_callback_fn(void **start, uint32_t *bytes, const void *boot_data);
+typedef enum hab_status hab_loader_callback_fn(void **start, size_t *bytes, const void *boot_data);
+typedef void hab_image_entry_fn(void);
+/*
+ * This table is constructed from the NXP manual "High Assurance Boot
+ * Version 4 Application Programming Interface Reference Manual",
+ * section 4.5 ROM vector table. Revision 1.4
+ */
struct habv4_rvt {
struct hab_header header;
enum hab_status (*entry)(void);
enum hab_status (*exit)(void);
- enum hab_status (*check_target)(enum hab_target target, const void *start, uint32_t bytes);
- void *(*authenticate_image)(uint8_t cid, uint32_t ivt_offset, void **start, uint32_t *bytes, hab_loader_callback_fn *loader);
- enum hab_status (*run_dcd)(const void *dcd);
- enum hab_status (*run_csf)(const void *csf, uint8_t cid);
+ enum hab_status (*check_target)(enum hab_target target, const void *start, size_t bytes);
+ void *(*authenticate_image)(uint8_t cid, ptrdiff_t ivt_offset, void **start, size_t *bytes, hab_loader_callback_fn *loader);
+ enum hab_status (*run_dcd)(const uint8_t *dcd);
+ enum hab_status (*run_csf)(const uint8_t *csf, uint8_t cid, uint32_t srkmask);
enum hab_status (*assert)(enum hab_assertion assertion, const void *data, uint32_t count);
- enum hab_status (*report_event)(enum hab_status status, uint32_t index, void *event, uint32_t *bytes);
- enum hab_status (*report_status)(enum hab_config *config, enum hab_state *state);
+ enum hab_status (*report_event)(enum hab_status status, uint32_t index, uint8_t *event, size_t *bytes);
+ enum hab_status (*report_status)(enum hab_config *config, enum habv4_state *state);
void (*failsafe)(void);
+ hab_image_entry_fn* (* authenticate_image_no_dcd)(uint8_t cid, ptrdiff_t ivt_offset, void **start, size_t *bytes, hab_loader_callback_fn *loader);
+ uint32_t (*get_version)(void);
+ enum hab_status (*authenticate_container)(uint8_t cid, ptrdiff_t ivt_offset, void **start, size_t *bytes, hab_loader_callback_fn *loader, uint32_t srkmask, int skip_dcd);
} __packed;
-#define FSL_SIP_HAB 0xC2000007
-#define FSL_SIP_HAB_AUTHENTICATE 0x00
-#define FSL_SIP_HAB_ENTRY 0x01
-#define FSL_SIP_HAB_EXIT 0x02
-#define FSL_SIP_HAB_REPORT_EVENT 0x03
-#define FSL_SIP_HAB_REPORT_STATUS 0x04
-#define FSL_SIP_HAB_FAILSAFE 0x05
-#define FSL_SIP_HAB_CHECK_TARGET 0x06
+#define FSL_SIP_HAB 0xC2000007
+
+/*
+ * These values correspondent to the jump table found in the upstream
+ * TF-A version 2.10 `imx_hab_handler`, not all HAB rom functions are
+ * supported yet.
+ */
+enum hab_sip_cmd {
+ FSL_SIP_HAB_AUTHENTICATE = 0x00,
+ FSL_SIP_HAB_ENTRY = 0x01,
+ FSL_SIP_HAB_EXIT = 0x02,
+ FSL_SIP_HAB_REPORT_EVENT = 0x03,
+ FSL_SIP_HAB_REPORT_STATUS = 0x04,
+ FSL_SIP_HAB_FAILSAFE = 0x05,
+ FSL_SIP_HAB_CHECK_TARGET = 0x06,
+ FSL_SIP_HAB_GET_VERSION = 0x07,
+ FSL_SIP_HAB_AUTH_IMG_NO_DCD = 0x08,
+};
+
+static enum hab_status hab_sip_report_event(enum hab_status status,
+ uint32_t index, uint8_t *event,
+ size_t *bytes)
+{
+ struct arm_smccc_res res;
+
+ v8_flush_dcache_range((unsigned long)bytes,
+ (unsigned long)bytes + sizeof(*bytes));
+
+ if (event)
+ v8_flush_dcache_range((unsigned long)event,
+ (unsigned long)event + *bytes);
+
+ arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_REPORT_EVENT,
+ (unsigned long)index, (unsigned long)event,
+ (unsigned long)bytes, 0, 0, 0, &res);
+
+ v8_inv_dcache_range((unsigned long)bytes,
+ (unsigned long)bytes + sizeof(*bytes));
+
+ if (event)
+ v8_inv_dcache_range((unsigned long)event,
+ (unsigned long)event + *bytes);
+
+ return (enum hab_status)res.a0;
+}
static enum hab_status hab_sip_report_status(enum hab_config *config,
- enum hab_state *state)
+ enum habv4_state *state)
{
struct arm_smccc_res res;
@@ -202,30 +241,62 @@ static enum hab_status hab_sip_report_status(enum hab_config *config,
return (enum hab_status)res.a0;
}
+static uint32_t hab_sip_get_version(void)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(FSL_SIP_HAB, FSL_SIP_HAB_GET_VERSION, 0, 0, 0, 0, 0, 0, &res);
+
+ return (uint32_t)res.a0;
+}
+
+#define HABV4_EVENT_MAX_LEN 0x80
+
+#define IMX8MQ_ROM_OCRAM_ADDRESS 0x9061C0
+
static enum hab_status imx8m_read_sram_events(enum hab_status status,
- uint32_t index, void *event,
- uint32_t *bytes)
+ uint32_t index, uint8_t *event,
+ size_t *bytes)
{
struct hab_event_record *events[10];
int num_events = 0;
- char *sram = (char *)0x9061c0;
+ u8 *sram;
int i = 0;
int internal_index = 0;
- char *end = 0;
+ uint16_t ev_len;
+ u8 *end = 0;
struct hab_event_record *search;
+ if (cpu_is_mx8mq())
+ sram = (char *)IMX8MQ_ROM_OCRAM_ADDRESS;
+ else
+ return HAB_STATUS_FAILURE;
+
/*
* AN12263 HABv4 Guidelines and Recommendations
* recommends the address and size, however errors are usually contained
* within the first bytes. Scan only the first few bytes to rule out
* lots of false positives.
+ * The max event length is just a sanity check.
*/
- end = sram + 0x1a0;
+ end = sram + 0x1a0;
while (sram < end) {
if (*sram == 0xdb) {
search = (void *)sram;
- sram = sram + be16_to_cpu(search->hdr.len);
+ ev_len = be16_to_cpu(search->hdr.len);
+ if (ev_len > HABV4_EVENT_MAX_LEN)
+ break;
+
+ sram += ev_len;
+ if (sram > end)
+ break;
+
+ if (num_events == ARRAY_SIZE(events)) {
+ pr_warn("Discarding excess event\n");
+ continue;
+ }
+
events[num_events] = search;
num_events++;
} else {
@@ -233,7 +304,7 @@ static enum hab_status imx8m_read_sram_events(enum hab_status status,
}
}
while (i < num_events) {
- if (events[i]->status == status) {
+ if (events[i]->status >= status) {
if (internal_index == index) {
*bytes = sizeof(struct hab_event_record) +
be16_to_cpu(events[i]->hdr.len);
@@ -249,9 +320,19 @@ static enum hab_status imx8m_read_sram_events(enum hab_status status,
return HAB_STATUS_FAILURE;
}
+static enum hab_status imx8m_report_event(enum hab_status status,
+ uint32_t index, uint8_t *event,
+ size_t *bytes)
+{
+ if (cpu_is_mx8mq())
+ return imx8m_read_sram_events(status, index, event, bytes);
+ else
+ return hab_sip_report_event(status, index, event, bytes);
+}
+
struct habv4_rvt hab_smc_ops = {
.header = { .tag = 0xdd },
- .report_event = imx8m_read_sram_events,
+ .report_event = imx8m_report_event,
.report_status = hab_sip_report_status,
};
@@ -287,7 +368,7 @@ static const char *habv4_get_config_str(enum hab_config config)
return "<unknown>";
}
-static const char *habv4_get_state_str(enum hab_state state)
+static const char *habv4_get_state_str(enum habv4_state state)
{
switch (state) {
case HAB_STATE_INITIAL:
@@ -445,7 +526,7 @@ static void habv4_display_event_record(struct hab_event_record *record)
pr_err("Engine: %s (0x%02x)\n", habv4_get_engine_str(record->engine), record->engine);
}
-static void habv4_display_event(uint8_t *data, uint32_t len)
+static void habv4_display_event(uint8_t *data, size_t len)
{
unsigned int i;
@@ -492,7 +573,7 @@ static bool is_known_rng_fail_event(const uint8_t *data, size_t len)
return false;
}
-static uint8_t *hab_get_event(const struct habv4_rvt *rvt, int index, int *len)
+static uint8_t *hab_get_event(const struct habv4_rvt *rvt, int index, size_t *len)
{
enum hab_status err;
uint8_t *buf;
@@ -501,7 +582,7 @@ static uint8_t *hab_get_event(const struct habv4_rvt *rvt, int index, int *len)
if (err != HAB_STATUS_SUCCESS)
return NULL;
- buf = malloc(*len);
+ buf = dma_alloc(*len);
if (!buf)
return NULL;
@@ -515,14 +596,21 @@ static uint8_t *hab_get_event(const struct habv4_rvt *rvt, int index, int *len)
return buf;
}
+static int habv4_state = -EPROBE_DEFER;
+
+int habv4_get_state(void)
+{
+ return habv4_state;
+}
+
static int habv4_get_status(const struct habv4_rvt *rvt)
{
uint8_t *data;
- uint32_t len;
+ size_t len;
int i;
enum hab_status status;
enum hab_config config = 0x0;
- enum hab_state state = 0x0;
+ enum habv4_state state = 0x0;
if (rvt->header.tag != HAB_TAG_RVT) {
pr_err("ERROR - RVT not found!\n");
@@ -530,6 +618,8 @@ static int habv4_get_status(const struct habv4_rvt *rvt)
}
status = rvt->report_status(&config, &state);
+ habv4_state = state;
+
pr_info("Status: %s (0x%02x)\n", habv4_get_status_str(status), status);
pr_info("Config: %s (0x%02x)\n", habv4_get_config_str(config), config);
pr_info("State: %s (0x%02x)\n", habv4_get_state_str(state), state);
@@ -559,19 +649,22 @@ static int habv4_get_status(const struct habv4_rvt *rvt)
return -EPERM;
}
-int imx6_hab_get_status(void)
+static int imx6_hab_get_status(void)
{
const struct habv4_rvt *rvt;
rvt = (void *)HABV4_RVT_IMX6_OLD;
+ OPTIMIZER_HIDE_VAR(rvt);
if (rvt->header.tag == HAB_TAG_RVT)
return habv4_get_status(rvt);
rvt = (void *)HABV4_RVT_IMX6_NEW;
+ OPTIMIZER_HIDE_VAR(rvt);
if (rvt->header.tag == HAB_TAG_RVT)
return habv4_get_status(rvt);
rvt = (void *)HABV4_RVT_IMX6UL;
+ OPTIMIZER_HIDE_VAR(rvt);
if (rvt->header.tag == HAB_TAG_RVT)
return habv4_get_status(rvt);
@@ -580,73 +673,23 @@ int imx6_hab_get_status(void)
return -EINVAL;
}
-static int imx8m_hab_get_status(void)
+int imx8m_hab_print_status(void)
{
- return habv4_get_status(&hab_smc_ops);
-}
+ pr_info("ROM version: 0x%x\n", hab_sip_get_version());
-static int init_imx8m_hab_get_status(void)
-{
- if (!cpu_is_mx8mq())
- /* can happen in multi-image builds and is not an error */
- return 0;
-
- /*
- * Nobody will check the return value if there were HAB errors, but the
- * initcall will fail spectaculously with a strange error message.
- */
- imx8m_hab_get_status();
+ habv4_get_status(&hab_smc_ops);
return 0;
}
-/*
- *
- *
- *
- */
-postmmu_initcall(init_imx8m_hab_get_status);
-
-static int init_imx6_hab_get_status(void)
+int imx6_hab_print_status(void)
{
- if (!cpu_is_mx6())
- /* can happen in multi-image builds and is not an error */
- return 0;
+ remap_range(0x0, SZ_1M, MAP_CACHED);
- /*
- * Nobody will check the return value if there were HAB errors, but the
- * initcall will fail spectaculously with a strange error message.
- */
imx6_hab_get_status();
- return 0;
-}
-
-/*
- * Need to run before MMU setup because i.MX6 ROM code is mapped near 0x0,
- * which will no longer be accessible when the MMU sets the zero page to
- * faulting.
- */
-postconsole_initcall(init_imx6_hab_get_status);
-
-int imx28_hab_get_status(void)
-{
- const struct habv4_rvt *rvt = (void *)HABV4_RVT_IMX28;
-
- return habv4_get_status(rvt);
-}
-
-static int init_imx28_hab_get_status(void)
-{
- if (!cpu_is_mx28())
- /* can happen in multi-image builds and is not an error */
- return 0;
-
+ zero_page_faulting();
+ remap_range((void *)PAGE_SIZE, SZ_1M - PAGE_SIZE, MAP_UNCACHED);
- /* nobody will check the return value if there were HAB errors, but the
- * initcall will fail spectaculously with a strange error message. */
- imx28_hab_get_status();
return 0;
}
-/* i.MX28 ROM code can be run after MMU setup to make use of caching */
-postmmu_initcall(init_imx28_hab_get_status);
diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig
index 32b84b028b..763929f7d6 100644
--- a/drivers/hw_random/Kconfig
+++ b/drivers/hw_random/Kconfig
@@ -8,11 +8,18 @@ menuconfig HWRNG
if HWRNG
+config HW_RANDOM_TIMERIOMEM
+ tristate "Timer IOMEM HW Random Number Generator support"
+ help
+ This driver provides barebox support for a generic Random
+ Number Generator used by reading a 'dumb' iomem address that
+ is to be read no faster than, for example, once a second.
+
config HWRNG_MXC_RNGC
tristate "Freescale i.MX RNGC Random Number Generator"
depends on ARCH_IMX25 || ARCH_IMX35 || ARCH_IMX53 || COMPILE_TEST
help
- This driver provides kernel-side support for the Random Number
+ This driver provides barebox support for the Random Number
Generator hardware found on some Freescale i.MX processors.
config HWRNG_STM32
@@ -44,4 +51,55 @@ config HW_RANDOM_STARFIVE
This driver provides barebox support for the Random Number
Generator hardware found on the StarFive family of SoCs.
+config HW_RANDOM_EFI
+ tristate "EFI Random Number Generator"
+ depends on EFI
+ help
+ This driver provides barebox support for the Random Number
+ Generator Protocol offered by EFI firmware
+
+config HW_RANDOM_OPTEE
+ tristate "OP-TEE based Random Number Generator support"
+ depends on OPTEE
+ help
+ This driver provides support for OP-TEE based Random Number
+ Generator on ARM SoCs where hardware entropy sources are not
+ accessible to normal world (barebox and e.g. Linux after it).
+
+config HW_RANDOM_ATMEL
+ tristate "Atmel Random Number Generator support"
+ depends on ARCH_AT91 || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on Atmel AT91 devices.
+
+config HW_RANDOM_BCM2835
+ tristate "Broadcom BCM2835/BCM63xx Random Number Generator support"
+ depends on ARCH_BCM283X || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on the Broadcom BCM2835 SoCs.
+
+config HW_RANDOM_IPROC_RNG200
+ tristate "Broadcom iProc/STB RNG200 support"
+ depends on ARCH_BCM283X || COMPILE_TEST
+ help
+ This driver provides barebox support for the RNG200
+ hardware found on the BCM2711.
+
+config HW_RANDOM_ROCKCHIP
+ tristate "Rockchip Random Number Generator support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on Rockchip cpus.
+
+config HW_RANDOM_OMAP
+ tristate "OMAP Random Number Generator support"
+ depends on ARCH_OMAP || ARCH_K3 || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on OMAP2/3/4/5, AM33xx/AM43xx
+ multimedia processors, and Marvell Armada 7k/8k SoCs.
+
endif
diff --git a/drivers/hw_random/Makefile b/drivers/hw_random/Makefile
index 6fe21bb84c..7f65a6c41e 100644
--- a/drivers/hw_random/Makefile
+++ b/drivers/hw_random/Makefile
@@ -5,3 +5,11 @@ obj-$(CONFIG_HWRNG_STM32) += stm32-rng.o
obj-$(CONFIG_HWRNG_DEV_RANDOM) += dev-random.o
obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
obj-$(CONFIG_HW_RANDOM_STARFIVE) += starfive-vic-rng.o
+obj-$(CONFIG_HW_RANDOM_EFI) += efi-rng.o
+obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
+obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
+obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o
+obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
+obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
diff --git a/drivers/hw_random/atmel-rng.c b/drivers/hw_random/atmel-rng.c
new file mode 100644
index 0000000000..bdd2139b08
--- /dev/null
+++ b/drivers/hw_random/atmel-rng.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2011 Peter Korsgaard <jacmet@sunsite.dk>
+
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/hw_random.h>
+#include <of.h>
+#include <linux/device.h>
+
+#define TRNG_CR 0x00
+#define TRNG_MR 0x04
+#define TRNG_ISR 0x1c
+#define TRNG_ISR_DATRDY BIT(0)
+#define TRNG_ODATA 0x50
+
+#define TRNG_KEY 0x524e4700 /* RNG */
+
+#define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */
+
+struct atmel_trng_data {
+ bool has_half_rate;
+};
+
+struct atmel_trng {
+ struct clk *clk;
+ void __iomem *base;
+ struct hwrng rng;
+ bool has_half_rate;
+};
+
+static bool atmel_trng_wait_ready(struct atmel_trng *trng, bool wait)
+{
+ int ready;
+
+ ready = readl(trng->base + TRNG_ISR) & TRNG_ISR_DATRDY;
+ if (!ready && wait)
+ readl_poll_timeout(trng->base + TRNG_ISR, ready,
+ ready & TRNG_ISR_DATRDY, 20000);
+
+ return !!ready;
+}
+
+static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
+ u32 *data = buf;
+ int ret;
+
+ ret = atmel_trng_wait_ready(trng, wait);
+ if (!ret)
+ return 0;
+
+ *data = readl(trng->base + TRNG_ODATA);
+ /*
+ * ensure data ready is only set again AFTER the next data word is ready
+ * in case it got set between checking ISR and reading ODATA, so we
+ * don't risk re-reading the same word
+ */
+ readl(trng->base + TRNG_ISR);
+ ret = 4;
+
+ return ret;
+}
+
+static int atmel_trng_init(struct hwrng *rng)
+{
+ struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
+ unsigned long rate;
+ int ret;
+
+ ret = clk_prepare_enable(trng->clk);
+ if (ret)
+ return ret;
+
+ if (trng->has_half_rate) {
+ rate = clk_get_rate(trng->clk);
+
+ /* if peripheral clk is above 100MHz, set HALFR */
+ if (rate > 100000000)
+ writel(TRNG_HALFR, trng->base + TRNG_MR);
+ }
+
+ writel(TRNG_KEY | 1, trng->base + TRNG_CR);
+
+ return 0;
+}
+
+static void atmel_trng_cleanup(struct atmel_trng *trng)
+{
+ writel(TRNG_KEY, trng->base + TRNG_CR);
+ clk_disable_unprepare(trng->clk);
+}
+
+static int atmel_trng_probe(struct device *dev)
+{
+ struct atmel_trng *trng;
+ const struct atmel_trng_data *data;
+
+ trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ trng->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(trng->base))
+ return PTR_ERR(trng->base);
+
+ trng->clk = clk_get(dev, NULL);
+ if (IS_ERR(trng->clk))
+ return PTR_ERR(trng->clk);
+ data = device_get_match_data(dev);
+ if (!data)
+ return -ENODEV;
+
+ trng->has_half_rate = data->has_half_rate;
+ trng->rng.name = dev_name(dev);
+ trng->rng.read = atmel_trng_read;
+ trng->rng.init = atmel_trng_init;
+ dev->priv = trng;
+
+ return hwrng_register(dev, &trng->rng);
+}
+
+static void atmel_trng_remove(struct device *dev)
+{
+ atmel_trng_cleanup(dev->priv);
+}
+
+static const struct atmel_trng_data at91sam9g45_config = {
+ .has_half_rate = false,
+};
+
+static const struct atmel_trng_data sam9x60_config = {
+ .has_half_rate = true,
+};
+
+static const struct of_device_id atmel_trng_dt_ids[] = {
+ {
+ .compatible = "atmel,at91sam9g45-trng",
+ .data = &at91sam9g45_config,
+ }, {
+ .compatible = "microchip,sam9x60-trng",
+ .data = &sam9x60_config,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
+
+static struct driver atmel_trng_driver = {
+ .name = "atmel-trng",
+ .probe = atmel_trng_probe,
+ .remove = atmel_trng_remove,
+ .of_match_table = atmel_trng_dt_ids,
+};
+device_platform_driver(atmel_trng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Atmel true random number generator driver");
diff --git a/drivers/hw_random/bcm2835-rng.c b/drivers/hw_random/bcm2835-rng.c
new file mode 100644
index 0000000000..d82331c950
--- /dev/null
+++ b/drivers/hw_random/bcm2835-rng.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
+ * Copyright (c) 2013 Lubomir Rintel
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <of.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#define RNG_CTRL 0x0
+#define RNG_STATUS 0x4
+#define RNG_DATA 0x8
+#define RNG_INT_MASK 0x10
+
+/* enable rng */
+#define RNG_RBGEN 0x1
+
+/* the initial numbers generated are "less random" so will be discarded */
+#define RNG_WARMUP_COUNT 0x40000
+
+#define RNG_INT_OFF 0x1
+
+struct bcm2835_rng_priv {
+ struct hwrng rng;
+ void __iomem *base;
+ bool mask_interrupts;
+ struct clk *clk;
+ struct reset_control *reset;
+};
+
+static inline struct bcm2835_rng_priv *to_rng_priv(struct hwrng *rng)
+{
+ return container_of(rng, struct bcm2835_rng_priv, rng);
+}
+
+static inline u32 rng_readl(struct bcm2835_rng_priv *priv, u32 offset)
+{
+ /* MIPS chips strapped for BE will automagically configure the
+ * peripheral registers for CPU-native byte order.
+ */
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ return __raw_readl(priv->base + offset);
+ else
+ return readl(priv->base + offset);
+}
+
+static inline void rng_writel(struct bcm2835_rng_priv *priv, u32 val,
+ u32 offset)
+{
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ __raw_writel(val, priv->base + offset);
+ else
+ writel(val, priv->base + offset);
+}
+
+static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct bcm2835_rng_priv *priv = to_rng_priv(rng);
+ u32 max_words = max / sizeof(u32);
+ u32 num_words, count;
+
+ while ((rng_readl(priv, RNG_STATUS) >> 24) == 0) {
+ if (!wait)
+ return 0;
+ hwrng_yield(rng);
+ }
+
+ num_words = rng_readl(priv, RNG_STATUS) >> 24;
+ if (num_words > max_words)
+ num_words = max_words;
+
+ for (count = 0; count < num_words; count++)
+ ((u32 *)buf)[count] = rng_readl(priv, RNG_DATA);
+
+ return num_words * sizeof(u32);
+}
+
+static int bcm2835_rng_init(struct hwrng *rng)
+{
+ struct bcm2835_rng_priv *priv = to_rng_priv(rng);
+ int ret = 0;
+ u32 val;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ return ret;
+
+ if (priv->mask_interrupts) {
+ /* mask the interrupt */
+ val = rng_readl(priv, RNG_INT_MASK);
+ val |= RNG_INT_OFF;
+ rng_writel(priv, val, RNG_INT_MASK);
+ }
+
+ /* set warm-up count & enable */
+ rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
+ rng_writel(priv, RNG_RBGEN, RNG_CTRL);
+
+ return ret;
+}
+
+static void bcm2835_rng_cleanup(struct bcm2835_rng_priv *priv)
+{
+ /* disable rng hardware */
+ rng_writel(priv, 0, RNG_CTRL);
+
+ clk_disable_unprepare(priv->clk);
+}
+
+struct bcm2835_rng_of_data {
+ bool mask_interrupts;
+};
+
+static const struct bcm2835_rng_of_data nsp_rng_of_data = {
+ .mask_interrupts = true,
+};
+
+static const struct of_device_id bcm2835_rng_of_match[] = {
+ { .compatible = "brcm,bcm2835-rng"},
+ { .compatible = "brcm,bcm-nsp-rng", .data = &nsp_rng_of_data },
+ { .compatible = "brcm,bcm5301x-rng", .data = &nsp_rng_of_data },
+ { .compatible = "brcm,bcm6368-rng"},
+ {},
+};
+
+static int bcm2835_rng_probe(struct device *dev)
+{
+ const struct bcm2835_rng_of_data *of_data;
+ struct bcm2835_rng_priv *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* map peripheral */
+ priv->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ /* Clock is optional on most platforms */
+ priv->clk = clk_get_optional(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->reset = reset_control_get_optional(dev, NULL);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
+ priv->rng.name = dev_name(dev);
+ priv->rng.init = bcm2835_rng_init;
+ priv->rng.read = bcm2835_rng_read;
+
+ of_data = device_get_match_data(dev);
+ if (of_data)
+ priv->mask_interrupts = of_data->mask_interrupts;
+
+ /* register driver */
+ err = hwrng_register(dev, &priv->rng);
+ if (err)
+ dev_err(dev, "hwrng registration failed\n");
+ else
+ dev_info(dev, "hwrng registered\n");
+
+ dev->priv = priv;
+
+ return err;
+}
+
+static void bcm2835_rng_remove(struct device *dev)
+{
+ bcm2835_rng_cleanup(dev->priv);
+}
+
+MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
+
+static struct driver bcm2835_rng_driver = {
+ .name = "bcm2835-rng",
+ .of_match_table = bcm2835_rng_of_match,
+ .probe = bcm2835_rng_probe,
+ .remove = bcm2835_rng_remove,
+};
+device_platform_driver(bcm2835_rng_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("BCM2835 Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hw_random/core.c b/drivers/hw_random/core.c
index f68ca070f5..7bc3c33319 100644
--- a/drivers/hw_random/core.c
+++ b/drivers/hw_random/core.c
@@ -44,10 +44,10 @@ static ssize_t rng_dev_read(struct cdev *cdev, void *buf, size_t size,
while (count) {
int max = min(count, (size_t)RNG_BUFFER_SIZE);
len = hwrng_get_data(rng, rng->buf, max, true);
- if (len < 0) {
- cur = len;
- break;
- }
+ if (len < 0)
+ return len;
+ if (!len && ctrlc())
+ return cur;
memcpy(buf + cur, rng->buf, len);
@@ -64,12 +64,12 @@ static struct cdev_operations rng_chrdev_ops = {
static int hwrng_register_cdev(struct hwrng *rng)
{
- struct device_d *dev = rng->dev;
+ struct device *dev = rng->dev;
const char *alias;
char *devname;
int err;
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
if (alias) {
devname = xstrdup(alias);
} else {
@@ -103,7 +103,7 @@ struct hwrng *hwrng_get_first(void)
return list_first_entry(&hwrngs, struct hwrng, list);
}
-int hwrng_register(struct device_d *dev, struct hwrng *rng)
+int hwrng_register(struct device *dev, struct hwrng *rng)
{
int err;
diff --git a/drivers/hw_random/dev-random.c b/drivers/hw_random/dev-random.c
index d25cf681a4..52f4847e4a 100644
--- a/drivers/hw_random/dev-random.c
+++ b/drivers/hw_random/dev-random.c
@@ -33,7 +33,7 @@ static int devrandom_rnd_init(struct hwrng *hwrng)
return 0;
}
-static int devrandom_rnd_probe(struct device_d *dev)
+static int devrandom_rnd_probe(struct device *dev)
{
struct devrandom_rnd *rnd;
int ret;
@@ -56,7 +56,7 @@ static int devrandom_rnd_probe(struct device_d *dev)
return 0;
}
-static struct driver_d devrandom_rnd_driver = {
+static struct driver devrandom_rnd_driver = {
.name = "devrandom",
.probe = devrandom_rnd_probe,
};
diff --git a/drivers/hw_random/efi-rng.c b/drivers/hw_random/efi-rng.c
new file mode 100644
index 0000000000..61cb01caf6
--- /dev/null
+++ b/drivers/hw_random/efi-rng.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/hw_random.h>
+#include <efi.h>
+#include <efi/efi-device.h>
+
+struct efi_rng_priv {
+ struct efi_rng_protocol *protocol;
+ struct hwrng hwrng;
+};
+
+static inline struct efi_rng_priv *to_efi_rng(struct hwrng *hwrng)
+{
+ return container_of(hwrng, struct efi_rng_priv, hwrng);
+}
+
+static int efi_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wait)
+{
+ struct efi_rng_protocol *protocol = to_efi_rng(hwrng)->protocol;
+ efi_status_t efiret;
+
+ efiret = protocol->get_rng(protocol, NULL, len, data);
+
+ return -efi_errno(efiret) ?: len;
+}
+
+static int efi_rng_probe(struct efi_device *efidev)
+{
+ struct efi_rng_priv *priv;
+
+ priv = xzalloc(sizeof(*priv));
+
+ BS->handle_protocol(efidev->handle, &efi_rng_protocol_guid,
+ (void **)&priv->protocol);
+ if (!priv->protocol)
+ return -ENODEV;
+
+ priv->hwrng.name = dev_name(&efidev->dev);
+ priv->hwrng.read = efi_rng_read;
+
+ return hwrng_register(&efidev->dev, &priv->hwrng);
+}
+
+static struct efi_driver efi_rng_driver = {
+ .driver = {
+ .name = "efi-rng",
+ },
+ .probe = efi_rng_probe,
+ .guid = EFI_RNG_PROTOCOL_GUID,
+};
+device_efi_driver(efi_rng_driver);
diff --git a/drivers/hw_random/iproc-rng200.c b/drivers/hw_random/iproc-rng200.c
new file mode 100644
index 0000000000..4cb3573a7d
--- /dev/null
+++ b/drivers/hw_random/iproc-rng200.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+*/
+/*
+ * DESCRIPTION: The Broadcom iProc RNG200 Driver
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>
+#include <clock.h>
+
+/* Registers */
+#define RNG_CTRL_OFFSET 0x00
+#define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF
+#define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001
+
+#define RNG_SOFT_RESET_OFFSET 0x04
+#define RNG_SOFT_RESET 0x00000001
+
+#define RBG_SOFT_RESET_OFFSET 0x08
+#define RBG_SOFT_RESET 0x00000001
+
+#define RNG_INT_STATUS_OFFSET 0x18
+#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000
+#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000
+#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020
+#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001
+
+#define RNG_FIFO_DATA_OFFSET 0x20
+
+#define RNG_FIFO_COUNT_OFFSET 0x24
+#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF
+
+struct iproc_rng200_dev {
+ struct hwrng rng;
+ void __iomem *base;
+};
+
+#define to_rng_priv(rng) container_of(rng, struct iproc_rng200_dev, rng)
+
+static void iproc_rng200_enable_set(void __iomem *rng_base, bool enable)
+{
+ u32 val;
+
+ val = ioread32(rng_base + RNG_CTRL_OFFSET);
+ val &= ~RNG_CTRL_RNG_RBGEN_MASK;
+
+ if (enable)
+ val |= RNG_CTRL_RNG_RBGEN_ENABLE;
+
+ iowrite32(val, rng_base + RNG_CTRL_OFFSET);
+}
+
+static void iproc_rng200_restart(void __iomem *rng_base)
+{
+ uint32_t val;
+
+ iproc_rng200_enable_set(rng_base, false);
+
+ /* Clear all interrupt status */
+ iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET);
+
+ /* Reset RNG and RBG */
+ val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);
+ val |= RBG_SOFT_RESET;
+ iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);
+
+ val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);
+ val |= RNG_SOFT_RESET;
+ iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);
+
+ val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);
+ val &= ~RNG_SOFT_RESET;
+ iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);
+
+ val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);
+ val &= ~RBG_SOFT_RESET;
+ iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);
+
+ iproc_rng200_enable_set(rng_base, true);
+}
+
+static int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
+ uint32_t num_remaining = max;
+ uint32_t status;
+ u64 start;
+
+ #define MAX_RESETS_PER_READ 1
+ uint32_t num_resets = 0;
+
+ #define MAX_IDLE_TIME_NS (NSEC_PER_SEC)
+
+ start = get_time_ns();
+
+ while ((num_remaining > 0) && !is_timeout(start, MAX_IDLE_TIME_NS)) {
+
+ /* Is RNG sane? If not, reset it. */
+ status = ioread32(priv->base + RNG_INT_STATUS_OFFSET);
+ if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK |
+ RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) {
+
+ if (num_resets >= MAX_RESETS_PER_READ)
+ return max - num_remaining;
+
+ iproc_rng200_restart(priv->base);
+ num_resets++;
+ }
+
+ /* Are there any random numbers available? */
+ if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
+ RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) {
+
+ if (num_remaining >= sizeof(uint32_t)) {
+ /* Buffer has room to store entire word */
+ *(uint32_t *)buf = ioread32(priv->base +
+ RNG_FIFO_DATA_OFFSET);
+ buf += sizeof(uint32_t);
+ num_remaining -= sizeof(uint32_t);
+ } else {
+ /* Buffer can only store partial word */
+ uint32_t rnd_number = ioread32(priv->base +
+ RNG_FIFO_DATA_OFFSET);
+ memcpy(buf, &rnd_number, num_remaining);
+ buf += num_remaining;
+ num_remaining = 0;
+ }
+
+ /* Reset the IDLE timeout */
+ start = get_time_ns();
+ } else {
+ if (!wait)
+ /* Cannot wait, return immediately */
+ return max - num_remaining;
+ }
+ }
+
+ return max - num_remaining;
+}
+
+static int iproc_rng200_init(struct hwrng *rng)
+{
+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
+
+ iproc_rng200_enable_set(priv->base, true);
+
+ return 0;
+}
+
+static void iproc_rng200_cleanup(struct iproc_rng200_dev *priv)
+{
+ iproc_rng200_enable_set(priv->base, false);
+}
+
+static int iproc_rng200_probe(struct device *dev)
+{
+ struct iproc_rng200_dev *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Map peripheral */
+ priv->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(priv->base)) {
+ dev_err(dev, "failed to remap rng regs\n");
+ return PTR_ERR(priv->base);
+ }
+
+ dev->priv = priv;
+
+ priv->rng.name = "iproc-rng200";
+ priv->rng.read = iproc_rng200_read;
+ priv->rng.init = iproc_rng200_init;
+
+ /* Register driver */
+ ret = hwrng_register(dev, &priv->rng);
+ if (ret) {
+ dev_err(dev, "hwrng registration failed\n");
+ return ret;
+ }
+
+ dev_info(dev, "hwrng registered\n");
+
+ return 0;
+}
+
+static void iproc_rng200_remove(struct device *dev)
+{
+ iproc_rng200_cleanup(dev->priv);
+}
+
+static const struct of_device_id iproc_rng200_of_match[] = {
+ { .compatible = "brcm,bcm2711-rng200", },
+ { .compatible = "brcm,bcm7211-rng200", },
+ { .compatible = "brcm,bcm7278-rng200", },
+ { .compatible = "brcm,iproc-rng200", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, iproc_rng200_of_match);
+
+static struct driver iproc_rng200_driver = {
+ .name = "iproc-rng200",
+ .of_match_table = iproc_rng200_of_match,
+ .probe = iproc_rng200_probe,
+ .remove = iproc_rng200_remove,
+};
+device_platform_driver(iproc_rng200_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hw_random/mxc-rngc.c b/drivers/hw_random/mxc-rngc.c
index 9bc29de1c2..39608b97af 100644
--- a/drivers/hw_random/mxc-rngc.c
+++ b/drivers/hw_random/mxc-rngc.c
@@ -101,7 +101,7 @@
#define RNG_ADDR_RANGE 0x34
struct mxc_rngc {
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
void __iomem *base;
struct hwrng rng;
@@ -240,7 +240,7 @@ static int mxc_rngc_init(struct hwrng *rng)
return 0;
}
-static int mxc_rngc_probe(struct device_d *dev)
+static int mxc_rngc_probe(struct device *dev)
{
struct mxc_rngc *rngc;
int ret;
@@ -282,8 +282,9 @@ static const struct of_device_id mxc_rngc_dt_ids[] = {
{ .compatible = "fsl,imx25-rngb" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mxc_rngc_dt_ids);
-static struct driver_d mxc_rngc_driver = {
+static struct driver mxc_rngc_driver = {
.name = "mxc_rngc",
.probe = mxc_rngc_probe,
.of_compatible = mxc_rngc_dt_ids,
diff --git a/drivers/hw_random/omap-rng.c b/drivers/hw_random/omap-rng.c
new file mode 100644
index 0000000000..9fa50bc8e7
--- /dev/null
+++ b/drivers/hw_random/omap-rng.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * omap-rng.c - RNG driver for TI OMAP CPU family
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * Mostly based on original driver:
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ */
+
+#include <init.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/hw_random.h>
+#include <clock.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <of.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#define RNG_REG_STATUS_RDY (1 << 0)
+
+#define RNG_REG_INTACK_RDY_MASK (1 << 0)
+#define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK (1 << 1)
+#define RNG_SHUTDOWN_OFLO_MASK (1 << 1)
+
+#define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
+#define RNG_CONTROL_STARTUP_CYCLES_MASK (0xffff << 16)
+#define RNG_CONTROL_ENABLE_TRNG_SHIFT 10
+#define RNG_CONTROL_ENABLE_TRNG_MASK (1 << 10)
+
+#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
+#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK (0xffff << 16)
+#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
+#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK (0xff << 0)
+
+#define RNG_CONTROL_STARTUP_CYCLES 0xff
+#define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
+#define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
+
+#define RNG_ALARMCNT_ALARM_TH_SHIFT 0x0
+#define RNG_ALARMCNT_ALARM_TH_MASK (0xff << 0)
+#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
+#define RNG_ALARMCNT_SHUTDOWN_TH_MASK (0x1f << 16)
+#define RNG_ALARM_THRESHOLD 0xff
+#define RNG_SHUTDOWN_THRESHOLD 0x4
+
+#define RNG_REG_FROENABLE_MASK 0xffffff
+#define RNG_REG_FRODETUNE_MASK 0xffffff
+
+#define OMAP2_RNG_OUTPUT_SIZE 0x4
+#define OMAP4_RNG_OUTPUT_SIZE 0x8
+#define EIP76_RNG_OUTPUT_SIZE 0x10
+
+/*
+ * EIP76 RNG takes approx. 700us to produce 16 bytes of output data
+ * as per testing results. And to account for the lack of udelay()'s
+ * reliability, we keep the timeout as 1000us.
+ */
+#define RNG_DATA_FILL_TIMEOUT 100
+
+enum {
+ RNG_OUTPUT_0_REG = 0,
+ RNG_OUTPUT_1_REG,
+ RNG_OUTPUT_2_REG,
+ RNG_OUTPUT_3_REG,
+ RNG_STATUS_REG,
+ RNG_INTMASK_REG,
+ RNG_INTACK_REG,
+ RNG_CONTROL_REG,
+ RNG_CONFIG_REG,
+ RNG_ALARMCNT_REG,
+ RNG_FROENABLE_REG,
+ RNG_FRODETUNE_REG,
+ RNG_ALARMMASK_REG,
+ RNG_ALARMSTOP_REG,
+ RNG_REV_REG,
+ RNG_SYSCONFIG_REG,
+};
+
+static const u16 reg_map_omap2[] = {
+ [RNG_OUTPUT_0_REG] = 0x0,
+ [RNG_STATUS_REG] = 0x4,
+ [RNG_CONFIG_REG] = 0x28,
+ [RNG_REV_REG] = 0x3c,
+ [RNG_SYSCONFIG_REG] = 0x40,
+};
+
+static const u16 reg_map_omap4[] = {
+ [RNG_OUTPUT_0_REG] = 0x0,
+ [RNG_OUTPUT_1_REG] = 0x4,
+ [RNG_STATUS_REG] = 0x8,
+ [RNG_INTMASK_REG] = 0xc,
+ [RNG_INTACK_REG] = 0x10,
+ [RNG_CONTROL_REG] = 0x14,
+ [RNG_CONFIG_REG] = 0x18,
+ [RNG_ALARMCNT_REG] = 0x1c,
+ [RNG_FROENABLE_REG] = 0x20,
+ [RNG_FRODETUNE_REG] = 0x24,
+ [RNG_ALARMMASK_REG] = 0x28,
+ [RNG_ALARMSTOP_REG] = 0x2c,
+ [RNG_REV_REG] = 0x1FE0,
+ [RNG_SYSCONFIG_REG] = 0x1FE4,
+};
+
+static const u16 reg_map_eip76[] = {
+ [RNG_OUTPUT_0_REG] = 0x0,
+ [RNG_OUTPUT_1_REG] = 0x4,
+ [RNG_OUTPUT_2_REG] = 0x8,
+ [RNG_OUTPUT_3_REG] = 0xc,
+ [RNG_STATUS_REG] = 0x10,
+ [RNG_INTACK_REG] = 0x10,
+ [RNG_CONTROL_REG] = 0x14,
+ [RNG_CONFIG_REG] = 0x18,
+ [RNG_ALARMCNT_REG] = 0x1c,
+ [RNG_FROENABLE_REG] = 0x20,
+ [RNG_FRODETUNE_REG] = 0x24,
+ [RNG_ALARMMASK_REG] = 0x28,
+ [RNG_ALARMSTOP_REG] = 0x2c,
+ [RNG_REV_REG] = 0x7c,
+};
+
+struct omap_rng_dev;
+/**
+ * struct omap_rng_pdata - RNG IP block-specific data
+ * @regs: Pointer to the register offsets structure.
+ * @data_size: No. of bytes in RNG output.
+ * @data_present: Callback to determine if data is available.
+ * @init: Callback for IP specific initialization sequence.
+ * @cleanup: Callback for IP specific cleanup sequence.
+ */
+struct omap_rng_pdata {
+ u16 *regs;
+ u32 data_size;
+ u32 (*data_present)(struct omap_rng_dev *priv);
+ int (*init)(struct omap_rng_dev *priv);
+ void (*cleanup)(struct omap_rng_dev *priv);
+};
+
+struct omap_rng_dev {
+ void __iomem *base;
+ struct device *dev;
+ const struct omap_rng_pdata *pdata;
+ struct hwrng rng;
+ struct clk *clk;
+ struct clk *clk_reg;
+};
+
+static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)
+{
+ return __raw_readl(priv->base + priv->pdata->regs[reg]);
+}
+
+static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg,
+ u32 val)
+{
+ __raw_writel(val, priv->base + priv->pdata->regs[reg]);
+}
+
+
+static int omap_rng_do_read(struct hwrng *rng, void *data, size_t max,
+ bool wait)
+{
+ struct omap_rng_dev *priv;
+ int i, present;
+
+ priv = (struct omap_rng_dev *)rng->priv;
+
+ /* In Linux, max is always at least 32 bytes, which is greater than
+ * the 4 bytes required by the IP not to raise a data abort.
+ * In barebox, reading 4 bytes from a HWRNG is something we want
+ * support, so we check against 4 here and restrict memcpy_fromio
+ * size below.
+ */
+ if (max < sizeof(u32))
+ return -EFAULT;
+
+ for (i = 0; i < RNG_DATA_FILL_TIMEOUT; i++) {
+ present = priv->pdata->data_present(priv);
+ if (present || !wait)
+ break;
+
+ udelay(10);
+ }
+ if (!present)
+ return 0;
+
+ max = min(max, priv->pdata->data_size);
+
+ memcpy_fromio(data, priv->base + priv->pdata->regs[RNG_OUTPUT_0_REG], max);
+
+ if (priv->pdata->regs[RNG_INTACK_REG])
+ omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK);
+
+ return max;
+}
+
+static int omap_rng_init(struct hwrng *rng)
+{
+ struct omap_rng_dev *priv;
+
+ priv = (struct omap_rng_dev *)rng->priv;
+ return priv->pdata->init(priv);
+}
+
+static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv)
+{
+ return omap_rng_read(priv, RNG_STATUS_REG) ? 0 : 1;
+}
+
+static int omap2_rng_init(struct omap_rng_dev *priv)
+{
+ omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x1);
+ return 0;
+}
+
+static void omap2_rng_cleanup(struct omap_rng_dev *priv)
+{
+ omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x0);
+}
+
+static struct omap_rng_pdata omap2_rng_pdata = {
+ .regs = (u16 *)reg_map_omap2,
+ .data_size = OMAP2_RNG_OUTPUT_SIZE,
+ .data_present = omap2_rng_data_present,
+ .init = omap2_rng_init,
+ .cleanup = omap2_rng_cleanup,
+};
+
+static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv)
+{
+ return omap_rng_read(priv, RNG_STATUS_REG) & RNG_REG_STATUS_RDY;
+}
+
+static int eip76_rng_init(struct omap_rng_dev *priv)
+{
+ u32 val;
+
+ /* Return if RNG is already running. */
+ if (omap_rng_read(priv, RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK)
+ return 0;
+
+ /* Number of 512 bit blocks of raw Noise Source output data that must
+ * be processed by either the Conditioning Function or the
+ * SP 800-90 DRBG ‘BC_DF’ functionality to yield a ‘full entropy’
+ * output value.
+ */
+ val = 0x5 << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
+
+ /* Number of FRO samples that are XOR-ed together into one bit to be
+ * shifted into the main shift register
+ */
+ val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
+ omap_rng_write(priv, RNG_CONFIG_REG, val);
+
+ /* Enable TRNG */
+ val = RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+
+ return 0;
+}
+
+static int omap4_rng_init(struct omap_rng_dev *priv)
+{
+ u32 val;
+
+ /* Return if RNG is already running. */
+ if (omap_rng_read(priv, RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK)
+ return 0;
+
+ val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
+ val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
+ omap_rng_write(priv, RNG_CONFIG_REG, val);
+
+ val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
+ val |= RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+
+ return 0;
+}
+
+static void omap4_rng_cleanup(struct omap_rng_dev *priv)
+{
+ int val;
+
+ val = omap_rng_read(priv, RNG_CONTROL_REG);
+ val &= ~RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+}
+
+static struct omap_rng_pdata omap4_rng_pdata = {
+ .regs = (u16 *)reg_map_omap4,
+ .data_size = OMAP4_RNG_OUTPUT_SIZE,
+ .data_present = omap4_rng_data_present,
+ .init = omap4_rng_init,
+ .cleanup = omap4_rng_cleanup,
+};
+
+static struct omap_rng_pdata eip76_rng_pdata = {
+ .regs = (u16 *)reg_map_eip76,
+ .data_size = EIP76_RNG_OUTPUT_SIZE,
+ .data_present = omap4_rng_data_present,
+ .init = eip76_rng_init,
+ .cleanup = omap4_rng_cleanup,
+};
+
+static const struct of_device_id omap_rng_of_match[] __maybe_unused = {
+ {
+ .compatible = "ti,omap2-rng",
+ .data = &omap2_rng_pdata,
+ },
+ {
+ .compatible = "ti,omap4-rng",
+ .data = &omap4_rng_pdata,
+ },
+ {
+ .compatible = "inside-secure,safexcel-eip76",
+ .data = &eip76_rng_pdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_rng_of_match);
+
+static int of_get_omap_rng_device_details(struct omap_rng_dev *priv,
+ struct device *dev)
+{
+ priv->pdata = device_get_match_data(dev);
+ if (!priv->pdata)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct clk *ti_sysc_clk_get_enabled(struct device *dev, const char *clk_id)
+{
+ struct clk *clk;
+
+ clk = clk_get_optional_enabled(dev, clk_id);
+ if (!clk)
+ clk = clk_get_optional_enabled(dev->parent, clk_id);
+
+ if (IS_ERR(clk))
+ dev_errp_probe(dev, clk, "Unable to enable the clk\n");
+ return clk;
+}
+
+static int omap_rng_probe(struct device *dev)
+{
+ struct omap_rng_dev *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->rng.read = omap_rng_do_read;
+ priv->rng.init = omap_rng_init;
+
+ priv->rng.priv = (unsigned long)priv;
+ dev->priv = priv;
+ priv->dev = dev;
+
+ priv->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto err_ioremap;
+ }
+
+ priv->rng.name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+ if (!priv->rng.name) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ priv->clk = ti_sysc_clk_get_enabled(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto err_ioremap;
+ }
+
+ priv->clk_reg = ti_sysc_clk_get_enabled(dev, "reg");
+ if (IS_ERR(priv->clk_reg)) {
+ ret = PTR_ERR(priv->clk_reg);
+ goto err_ioremap;
+ }
+
+ ret = of_get_omap_rng_device_details(priv, dev);
+ if (ret)
+ goto err_register;
+
+ ret = hwrng_register(dev, &priv->rng);
+ if (ret)
+ goto err_register;
+
+ dev_info(dev, "Random Number Generator ver. %02x\n",
+ omap_rng_read(priv, RNG_REV_REG));
+
+ return 0;
+
+err_register:
+ priv->base = NULL;
+
+ clk_disable_unprepare(priv->clk_reg);
+ clk_disable_unprepare(priv->clk);
+err_ioremap:
+ dev_err(dev, "initialization failed.\n");
+ return ret;
+}
+
+static void omap_rng_remove(struct device *dev)
+{
+ struct omap_rng_dev *priv = dev->priv;
+
+
+ priv->pdata->cleanup(priv);
+
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_reg);
+}
+
+static struct driver omap_rng_driver = {
+ .name = "omap_rng",
+ .of_match_table = of_match_ptr(omap_rng_of_match),
+ .probe = omap_rng_probe,
+ .remove = omap_rng_remove,
+};
+
+device_platform_driver(omap_rng_driver);
+MODULE_ALIAS("platform:omap_rng");
+MODULE_AUTHOR("Deepak Saxena (and others)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hw_random/optee-rng.c b/drivers/hw_random/optee-rng.c
new file mode 100644
index 0000000000..d1d2904821
--- /dev/null
+++ b/drivers/hw_random/optee-rng.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2019 Linaro Ltd.
+ */
+
+#include <of.h>
+#include <linux/hw_random.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+
+#define DRIVER_NAME "optee-rng"
+
+#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001
+
+/*
+ * TA_CMD_GET_ENTROPY - Get Entropy from RNG
+ *
+ * param[0] (inout memref) - Entropy buffer memory reference
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
+ * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
+ */
+#define TA_CMD_GET_ENTROPY 0x0
+
+/*
+ * TA_CMD_GET_RNG_INFO - Get RNG information
+ *
+ * param[0] (out value) - value.a: RNG data-rate in bytes per second
+ * value.b: Quality/Entropy per 1024 bit of data
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ */
+#define TA_CMD_GET_RNG_INFO 0x1
+
+#define MAX_ENTROPY_REQ_SZ (4 * 1024)
+
+/**
+ * struct optee_rng_private - OP-TEE Random Number Generator private data
+ * @dev: OP-TEE based RNG device.
+ * @ctx: OP-TEE context handler.
+ * @session_id: RNG TA session identifier.
+ * @data_rate: RNG data rate.
+ * @entropy_shm_pool: Memory pool shared with RNG device.
+ * @optee_rng: OP-TEE RNG driver structure.
+ */
+struct optee_rng_private {
+ struct device *dev;
+ struct tee_context *ctx;
+ u32 session_id;
+ u32 data_rate;
+ struct tee_shm *entropy_shm_pool;
+ struct hwrng optee_rng;
+ u16 quality;
+ void (*bus_devinfo)(struct device *);
+};
+
+#define to_optee_rng_private(r) \
+ container_of(r, struct optee_rng_private, optee_rng)
+
+static size_t get_optee_rng_data(struct optee_rng_private *pvt_data,
+ void *buf, size_t req_size)
+{
+ int ret = 0;
+ u8 *rng_data = NULL;
+ size_t rng_size = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */
+ inv_arg.func = TA_CMD_GET_ENTROPY;
+ inv_arg.session = pvt_data->session_id;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[0].u.memref.shm = pvt_data->entropy_shm_pool;
+ param[0].u.memref.size = req_size;
+ param[0].u.memref.shm_offs = 0;
+
+ ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param);
+ if ((ret < 0) || (inv_arg.ret != 0)) {
+ dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n",
+ inv_arg.ret);
+ return 0;
+ }
+
+ rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0);
+ if (IS_ERR(rng_data)) {
+ dev_err(pvt_data->dev, "tee_shm_get_va failed\n");
+ return 0;
+ }
+
+ rng_size = param[0].u.memref.size;
+ memcpy(buf, rng_data, rng_size);
+
+ return rng_size;
+}
+
+static int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+ size_t read = 0, rng_size;
+ int timeout = 1;
+ u8 *data = buf;
+
+ if (max > MAX_ENTROPY_REQ_SZ)
+ max = MAX_ENTROPY_REQ_SZ;
+
+ while (read < max) {
+ rng_size = get_optee_rng_data(pvt_data, data, (max - read));
+
+ data += rng_size;
+ read += rng_size;
+
+ if (wait && pvt_data->data_rate) {
+ if ((timeout-- == 0) || (read == max))
+ return read;
+ } else {
+ return read;
+ }
+ }
+
+ return read;
+}
+
+static int optee_rng_init(struct hwrng *rng)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+ struct tee_shm *entropy_shm_pool = NULL;
+
+ entropy_shm_pool = tee_shm_alloc_kernel_buf(pvt_data->ctx,
+ MAX_ENTROPY_REQ_SZ);
+ if (IS_ERR(entropy_shm_pool)) {
+ dev_err(pvt_data->dev, "tee_shm_alloc_kernel_buf failed\n");
+ return PTR_ERR(entropy_shm_pool);
+ }
+
+ pvt_data->entropy_shm_pool = entropy_shm_pool;
+
+ return 0;
+}
+
+static struct optee_rng_private pvt_data = {
+ .optee_rng = {
+ .name = DRIVER_NAME,
+ .init = optee_rng_init,
+ .read = optee_rng_read,
+ }
+};
+
+static int get_optee_rng_info(struct device *dev)
+{
+ int ret = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */
+ inv_arg.func = TA_CMD_GET_RNG_INFO;
+ inv_arg.session = pvt_data.session_id;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+
+ ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
+ if ((ret < 0) || (inv_arg.ret != 0)) {
+ dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n",
+ inv_arg.ret);
+ return -EINVAL;
+ }
+
+ pvt_data.data_rate = param[0].u.value.a;
+ pvt_data.quality = param[0].u.value.b;
+
+ return 0;
+}
+
+static void optee_rng_devinfo(struct device *dev)
+{
+ printf("Data rate: %u\n", pvt_data.data_rate);
+ printf("Quality: %u\n", pvt_data.quality);
+
+ if (pvt_data.bus_devinfo)
+ pvt_data.bus_devinfo(dev);
+}
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+ return 1;
+ else
+ return 0;
+}
+
+static int optee_rng_probe(struct device *dev)
+{
+ struct tee_client_device *rng_device = to_tee_client_device(dev);
+ int ret = 0, err = -ENODEV;
+ struct tee_ioctl_open_session_arg sess_arg;
+
+ memset(&sess_arg, 0, sizeof(sess_arg));
+
+ /* Open context with TEE driver */
+ pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
+ NULL);
+ if (IS_ERR(pvt_data.ctx))
+ return -ENODEV;
+
+ /* Open session with hwrng Trusted App */
+ export_uuid(sess_arg.uuid, &rng_device->id.uuid);
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
+ sess_arg.num_params = 0;
+
+ ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
+ if ((ret < 0) || (sess_arg.ret != 0)) {
+ dev_err(dev, "tee_client_open_session failed, err: %x\n",
+ sess_arg.ret);
+ err = -EINVAL;
+ goto out_ctx;
+ }
+ pvt_data.session_id = sess_arg.session;
+
+ err = get_optee_rng_info(dev);
+ if (err)
+ goto out_sess;
+
+ err = hwrng_register(dev, &pvt_data.optee_rng);
+ if (err) {
+ dev_err(dev, "hwrng registration failed (%d)\n", err);
+ goto out_sess;
+ }
+
+ pvt_data.dev = dev;
+ pvt_data.bus_devinfo = dev->info;
+ dev->info = optee_rng_devinfo;
+
+ return 0;
+
+out_sess:
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+out_ctx:
+ tee_client_close_context(pvt_data.ctx);
+
+ return err;
+}
+
+static void optee_rng_remove(struct device *dev)
+{
+ hwrng_unregister(&pvt_data.optee_rng);
+
+ tee_shm_free(pvt_data.entropy_shm_pool);
+
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+ tee_client_close_context(pvt_data.ctx);
+}
+
+static const struct tee_client_device_id optee_rng_id_table[] = {
+ {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f,
+ 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(tee, optee_rng_id_table);
+
+static struct tee_client_driver optee_rng_driver = {
+ .id_table = optee_rng_id_table,
+ .driver = {
+ .name = DRIVER_NAME,
+ .bus = &tee_bus_type,
+ .probe = optee_rng_probe,
+ .remove = optee_rng_remove,
+ },
+};
+
+static int __init optee_rng_mod_init(void)
+{
+ return driver_register(&optee_rng_driver.driver);
+}
+device_initcall(optee_rng_mod_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");
+MODULE_DESCRIPTION("OP-TEE based random number generator driver");
diff --git a/drivers/hw_random/rockchip-rng.c b/drivers/hw_random/rockchip-rng.c
new file mode 100644
index 0000000000..990e5fc111
--- /dev/null
+++ b/drivers/hw_random/rockchip-rng.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rockchip-rng.c Random Number Generator driver for the Rockchip
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd.
+ * Author: Lin Jinhan <troy.lin@rock-chips.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/hw_random.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+#include <linux/bitfield.h>
+#include <linux/mod_devicetable.h>
+#include <of.h>
+#include <of_address.h>
+#include <linux/device.h>
+
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define ROCKCHIP_AUTOSUSPEND_DELAY 100
+#define ROCKCHIP_POLL_PERIOD_US 100
+#define ROCKCHIP_POLL_TIMEOUT_US 10000
+#define RK_MAX_RNG_BYTE (32)
+
+#define CRYPTO_V1_CTRL 0x0008
+#define CRYPTO_V1_RNG_START BIT(8)
+#define CRYPTO_V1_RNG_FLUSH BIT(9)
+#define CRYPTO_V1_TRNG_CTRL 0x0200
+#define CRYPTO_V1_OSC_ENABLE BIT(16)
+#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x) (x)
+#define CRYPTO_V1_TRNG_DOUT_0 0x0204
+
+#define CRYPTO_V2_RNG_CTL 0x0400
+#define CRYPTO_V2_RNG_BIT_LEN GENMASK(5, 4)
+#define CRYPTO_V2_RNG_64_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 0)
+#define CRYPTO_V2_RNG_128_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 1)
+#define CRYPTO_V2_RNG_192_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 2)
+#define CRYPTO_V2_RNG_256_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 3)
+#define CRYPTO_V2_RNG_SOC_RING GENMASK(3, 2)
+#define CRYPTO_V2_RNG_FASTEST_SOC_RING FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 0)
+#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0 FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 1)
+#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1 FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 2)
+#define CRYPTO_V2_RNG_SLOWEST_SOC_RING FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 3)
+#define CRYPTO_V2_RNG_ENABLE BIT(1)
+#define CRYPTO_V2_RNG_START BIT(0)
+#define CRYPTO_V2_RNG_SAMPLE_CNT 0x0404
+#define CRYPTO_V2_RNG_DOUT_0 0x0410
+
+struct rk_rng_soc_data {
+ const char * const *clks;
+ int clks_num;
+ int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait);
+};
+
+struct rk_rng {
+ struct device *dev;
+ struct hwrng rng;
+ void __iomem *mem;
+ struct rk_rng_soc_data *soc_data;
+ u32 clk_num;
+ struct clk_bulk_data *clk_bulks;
+};
+
+static void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset)
+{
+ __raw_writel(val, rng->mem + offset);
+}
+
+static u32 rk_rng_readl(struct rk_rng *rng, u32 offset)
+{
+ return __raw_readl(rng->mem + offset);
+}
+
+static int rk_rng_init(struct hwrng *rng)
+{
+ int ret;
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks);
+ if (ret < 0) {
+ dev_err(rk_rng->dev, "failed to enable clks %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rk_rng_cleanup(struct rk_rng *rk_rng)
+{
+ clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks);
+}
+
+static void rk_rng_read_regs(struct rk_rng *rng, u32 offset, void *buf,
+ size_t size)
+{
+ u32 i, sample;
+
+ for (i = 0; i < size; i += 4) {
+ sample = rk_rng_readl(rng, offset + i);
+ memcpy(buf + i, &sample, sizeof(sample));
+ }
+}
+
+static int rk_rng_v1_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ int ret = 0;
+ u32 reg_ctrl = 0;
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ /* enable osc_ring to get entropy, sample period is set as 100 */
+ reg_ctrl = CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100);
+ rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_TRNG_CTRL);
+
+ reg_ctrl = HIWORD_UPDATE(CRYPTO_V1_RNG_START, CRYPTO_V1_RNG_START, 0);
+
+ rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_CTRL);
+
+ ret = readl_poll_timeout(rk_rng->mem + CRYPTO_V1_CTRL, reg_ctrl,
+ !(reg_ctrl & CRYPTO_V1_RNG_START),
+ ROCKCHIP_POLL_TIMEOUT_US);
+ if (ret < 0)
+ goto out;
+
+ ret = min_t(size_t, max, RK_MAX_RNG_BYTE);
+
+ rk_rng_read_regs(rk_rng, CRYPTO_V1_TRNG_DOUT_0, buf, ret);
+
+out:
+ /* close TRNG */
+ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, CRYPTO_V1_RNG_START, 0),
+ CRYPTO_V1_CTRL);
+
+ return ret;
+}
+
+static int rk_rng_v2_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ int ret = 0;
+ u32 reg_ctrl = 0;
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ /* enable osc_ring to get entropy, sample period is set as 100 */
+ rk_rng_writel(rk_rng, 100, CRYPTO_V2_RNG_SAMPLE_CNT);
+
+ reg_ctrl |= CRYPTO_V2_RNG_256_BIT_LEN;
+ reg_ctrl |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0;
+ reg_ctrl |= CRYPTO_V2_RNG_ENABLE;
+ reg_ctrl |= CRYPTO_V2_RNG_START;
+
+ rk_rng_writel(rk_rng, HIWORD_UPDATE(reg_ctrl, 0xffff, 0),
+ CRYPTO_V2_RNG_CTL);
+
+ ret = readl_poll_timeout(rk_rng->mem + CRYPTO_V2_RNG_CTL, reg_ctrl,
+ !(reg_ctrl & CRYPTO_V2_RNG_START),
+ ROCKCHIP_POLL_TIMEOUT_US);
+ if (ret < 0)
+ goto out;
+
+ ret = min_t(size_t, max, RK_MAX_RNG_BYTE);
+
+ rk_rng_read_regs(rk_rng, CRYPTO_V2_RNG_DOUT_0, buf, ret);
+
+out:
+ /* close TRNG */
+ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), CRYPTO_V2_RNG_CTL);
+
+ return ret;
+}
+
+static const struct rk_rng_soc_data rk_rng_rk3399_soc_data = {
+ .clks_num = 3,
+ .rk_rng_read = rk_rng_v1_read,
+};
+
+static const struct rk_rng_soc_data rk_rng_v1_soc_data = {
+ .clks_num = 2,
+ .rk_rng_read = rk_rng_v1_read,
+};
+
+static const struct rk_rng_soc_data rk_rng_v2_soc_data = {
+ .clks_num = 2,
+ .rk_rng_read = rk_rng_v2_read,
+};
+
+static const struct of_device_id rk_rng_dt_match[] = {
+ {
+ .compatible = "rockchip,rk3399-crypto",
+ .data = (void *)&rk_rng_rk3399_soc_data,
+ },
+ {
+ .compatible = "rockchip,cryptov1-rng",
+ .data = (void *)&rk_rng_v1_soc_data,
+ },
+ {
+ .compatible = "rockchip,cryptov2-rng",
+ .data = (void *)&rk_rng_v2_soc_data,
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, rk_rng_dt_match);
+
+static int rk_rng_probe(struct device *dev)
+{
+ int ret;
+ struct rk_rng *rk_rng;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *match;
+
+ rk_rng = devm_kzalloc(dev, sizeof(struct rk_rng), GFP_KERNEL);
+ if (!rk_rng)
+ return -ENOMEM;
+
+ match = of_match_node(rk_rng_dt_match, np);
+ rk_rng->soc_data = (struct rk_rng_soc_data *)match->data;
+
+ rk_rng->dev = dev;
+ rk_rng->rng.name = "rockchip";
+ rk_rng->rng.init = rk_rng_init;
+ rk_rng->rng.read = rk_rng->soc_data->rk_rng_read;
+
+ rk_rng->clk_num = clk_bulk_get_all(dev, &rk_rng->clk_bulks);
+ if (rk_rng->clk_num < rk_rng->soc_data->clks_num)
+ return dev_err_probe(dev, -EINVAL,
+ "Missing clocks, got %d instead of %d\n",
+ rk_rng->clk_num, rk_rng->soc_data->clks_num);
+
+ ret = device_reset_us(dev, 2);
+ if (ret)
+ return ret;
+
+ rk_rng->mem = of_iomap(dev->device_node, 0);
+ if (IS_ERR(rk_rng->mem))
+ return PTR_ERR(rk_rng->mem);
+
+ dev->priv = rk_rng;
+
+ return hwrng_register(dev, &rk_rng->rng);
+}
+
+static void rk_rng_remove(struct device *dev)
+{
+ rk_rng_cleanup(dev->priv);
+}
+
+static struct driver rk_rng_driver = {
+ .name = "rockchip-rng",
+ .of_match_table = rk_rng_dt_match,
+ .probe = rk_rng_probe,
+ .remove = rk_rng_remove,
+};
+
+device_platform_driver(rk_rng_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP H/W Random Number Generator driver");
+MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hw_random/starfive-vic-rng.c b/drivers/hw_random/starfive-vic-rng.c
index d90b495dd8..329e845197 100644
--- a/drivers/hw_random/starfive-vic-rng.c
+++ b/drivers/hw_random/starfive-vic-rng.c
@@ -72,7 +72,7 @@
#define to_vic_rng(p) container_of(p, struct vic_rng, rng)
struct vic_rng {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct hwrng rng;
};
@@ -170,7 +170,7 @@ static int vic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
return max;
}
-static int vic_rng_probe(struct device_d *dev)
+static int vic_rng_probe(struct device *dev)
{
struct vic_rng *hrng;
struct resource *res;
@@ -195,8 +195,9 @@ static const struct of_device_id vic_rng_dt_ids[] = {
{ .compatible = "starfive,vic-rng" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, vic_rng_dt_ids);
-static struct driver_d vic_rng_driver = {
+static struct driver vic_rng_driver = {
.name = "vic-rng",
.probe = vic_rng_probe,
.of_compatible = vic_rng_dt_ids,
diff --git a/drivers/hw_random/stm32-rng.c b/drivers/hw_random/stm32-rng.c
index 9b28f37ecd..03bc4a5cbf 100644
--- a/drivers/hw_random/stm32-rng.c
+++ b/drivers/hw_random/stm32-rng.c
@@ -104,7 +104,7 @@ static int stm32_rng_init(struct hwrng *hwrng)
return 0;
}
-static void stm32_rng_remove(struct device_d *dev)
+static void stm32_rng_remove(struct device *dev)
{
struct stm32_rng *rng = dev->priv;
@@ -112,7 +112,7 @@ static void stm32_rng_remove(struct device_d *dev)
clk_disable(rng->clk);
}
-static int stm32_rng_probe(struct device_d *dev)
+static int stm32_rng_probe(struct device *dev)
{
struct stm32_rng *rng;
struct resource *res;
@@ -152,8 +152,9 @@ static const struct of_device_id stm32_rng_dt_ids[] = {
{ .compatible = "st,stm32-rng" },
{ /* sentinel */},
};
+MODULE_DEVICE_TABLE(of, stm32_rng_dt_ids);
-static struct driver_d stm32_rng_driver = {
+static struct driver stm32_rng_driver = {
.name = "stm32-rng",
.probe = stm32_rng_probe,
.remove = stm32_rng_remove,
diff --git a/drivers/hw_random/timeriomem-rng.c b/drivers/hw_random/timeriomem-rng.c
new file mode 100644
index 0000000000..8d47058306
--- /dev/null
+++ b/drivers/hw_random/timeriomem-rng.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/char/hw_random/timeriomem-rng.c
+ *
+ * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * Derived from drivers/char/hw_random/omap-rng.c
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Overview:
+ * This driver is useful for platforms that have an IO range that provides
+ * periodic random data from a single IO memory address. All the platform
+ * has to do is provide the address and 'wait time' that new data becomes
+ * available.
+ *
+ * TODO: add support for reading sizes other than 32bits and masking
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <of.h>
+#include <linux/device.h>
+#include <linux/time.h>
+
+struct timeriomem_rng_private {
+ void __iomem *io_base;
+
+ ktime_t period;
+ ktime_t next_read;
+
+ struct hwrng rng_ops;
+};
+
+static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
+ size_t max, bool wait)
+{
+ struct timeriomem_rng_private *priv =
+ container_of(hwrng, struct timeriomem_rng_private, rng_ops);
+ int retval = 0;
+ int period_us = ktime_to_us(priv->period);
+ ktime_t now = ktime_get();
+
+ /*
+ * There may not have been enough time for new data to be generated
+ * since the last request. If the caller doesn't want to wait, let them
+ * bail out. Otherwise, wait for the completion. If the new data has
+ * already been generated, the completion should already be available.
+ */
+ if (ktime_before(now, priv->next_read)) {
+ if (!wait)
+ return 0;
+
+ udelay(ktime_to_us(ktime_sub(priv->next_read, now)));
+ }
+
+ do {
+ /*
+ * After the first read, all additional reads will need to wait
+ * for the RNG to generate new data. Since the period can have
+ * a wide range of values (1us to 1s have been observed), allow
+ * for 1% tolerance in the sleep time rather than a fixed value.
+ */
+ if (retval > 0)
+ udelay(period_us);
+
+ *(u32 *)data = readl(priv->io_base);
+ retval += sizeof(u32);
+ data += sizeof(u32);
+ max -= sizeof(u32);
+ } while (wait && max > sizeof(u32));
+
+ /*
+ * Block any new callers until the RNG has had time to generate new
+ * data.
+ */
+ priv->next_read = ktime_add(ktime_get(), priv->period);
+
+ return retval;
+}
+
+static int timeriomem_rng_probe(struct device *dev)
+{
+ struct timeriomem_rng_private *priv;
+ struct resource *res;
+ int err = 0;
+ int period;
+
+ /* Allocate memory for the device structure (and zero it) */
+ priv = devm_kzalloc(dev,
+ sizeof(struct timeriomem_rng_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->io_base = dev_platform_get_and_ioremap_resource(dev, 0, &res);
+ if (IS_ERR(priv->io_base))
+ return PTR_ERR(priv->io_base);
+
+ if (res->start % 4 != 0 || resource_size(res) < 4) {
+ dev_err(dev,
+ "address must be at least four bytes wide and 32-bit aligned\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(dev->of_node, "period", &period))
+ return dev_err_probe(dev, -EINVAL, "missing period\n");
+
+ priv->period = ns_to_ktime(period * NSEC_PER_USEC);
+
+ priv->rng_ops.name = dev_name(dev);
+ priv->rng_ops.read = timeriomem_rng_read;
+
+ /* Assume random data is already available. */
+ priv->next_read = ktime_get();
+
+ err = hwrng_register(dev, &priv->rng_ops);
+ if (err) {
+ dev_err(dev, "problem registering\n");
+ return err;
+ }
+
+ dev_info(dev, "32bits from 0x%p @ %dus\n",
+ priv->io_base, period);
+
+ return 0;
+}
+
+static const struct of_device_id timeriomem_rng_match[] = {
+ { .compatible = "timeriomem_rng" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, timeriomem_rng_match);
+
+static struct driver timeriomem_rng_driver = {
+ .name = "timeriomem_rng",
+ .of_match_table = timeriomem_rng_match,
+ .probe = timeriomem_rng_probe,
+};
+
+device_platform_driver(timeriomem_rng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 2d6010869d..9b7e5d96f0 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -245,7 +245,7 @@ static int i2c_inb(struct i2c_adapter *i2c_adap)
static int test_bus(struct i2c_adapter *i2c_adap)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
- struct device_d *dev = &i2c_adap->dev;
+ struct device *dev = &i2c_adap->dev;
int scl, sda, ret;
if (adap->pre_xfer) {
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 766aa5edfa..093b12b2ef 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -30,6 +30,13 @@ config I2C_IMX
for many i.MX ARM based SoCs, for MPC85xx and MPC5200 PowerPC based
SoCs.
+config I2C_IMX_LPI2C
+ tristate "IMX Low Power I2C interface"
+ depends on ARCH_IMX || COMPILE_TEST
+ help
+ Say Y here if you want to use the Low Power IIC bus controller
+ on the Freescale i.MX processors.
+
config I2C_DESIGNWARE
bool "Synopsys DesignWare I2C Master driver"
help
@@ -82,4 +89,10 @@ config I2C_CADENCE
Say Y here to include support for the Cadence I2C host controller found
in Zynq UltraScale+ MPSoCs.
+config I2C_EFI
+ bool "EFI I2C Master driver"
+ depends on EFI_PAYLOAD
+ help
+ Say Y here to include support for the EFI I2C Master driver.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a1ab46fb28..30005c2bf8 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,9 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_BCM283X) += i2c-bcm283x.o
+obj-$(CONFIG_I2C_EFI) += i2c-efi.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
lwl-$(CONFIG_I2C_IMX_EARLY) += i2c-imx-early.o
+obj-pbl-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index b100dc6c17..5b1f456187 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -78,11 +78,10 @@ struct at91_twi_pdata {
bool has_dig_filtr;
bool has_adv_dig_filtr;
bool has_ana_filtr;
- bool has_clear_cmd;
};
struct at91_twi_dev {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct clk *clk;
u8 *buf;
@@ -438,6 +437,13 @@ static struct at91_twi_pdata at91sam9x5_config = {
.has_unre_flag = false,
};
+static struct at91_twi_pdata sama5d4_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_hold_field = true,
+ .has_dig_filtr = true,
+};
+
static struct at91_twi_pdata sama5d2_config = {
.clk_max_div = 7,
.clk_offset = 3,
@@ -493,14 +499,21 @@ static struct of_device_id at91_twi_dt_ids[] = {
.compatible = "atmel,at91sam9x5-i2c",
.data = &at91sam9x5_config,
}, {
+ .compatible = "atmel,sama5d4-i2c",
+ .data = &sama5d4_config,
+ }, {
.compatible = "atmel,sama5d2-i2c",
.data = &sama5d2_config,
}, {
+ .compatible = "microchip,sam9x60-i2c",
+ .data = &sama5d2_config,
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, at91_twi_dt_ids);
-static int at91_twi_probe(struct device_d *dev)
+static int at91_twi_probe(struct device *dev)
{
struct resource *iores;
struct at91_twi_dev *i2c_at91;
@@ -546,7 +559,7 @@ static int at91_twi_probe(struct device_d *dev)
i2c_at91->adapter.master_xfer = at91_twi_xfer;
i2c_at91->adapter.dev.parent = dev;
i2c_at91->adapter.nr = dev->id;
- i2c_at91->adapter.dev.device_node = dev->device_node;
+ i2c_at91->adapter.dev.of_node = dev->of_node;
rc = i2c_add_numbered_adapter(&i2c_at91->adapter);
if (rc) {
@@ -565,7 +578,7 @@ out_free:
return rc;
}
-static struct driver_d at91_twi_driver = {
+static struct driver at91_twi_driver = {
.name = "at91-twi",
.probe = at91_twi_probe,
.id_table = at91_twi_devtypes,
diff --git a/drivers/i2c/busses/i2c-bcm283x.c b/drivers/i2c/busses/i2c-bcm283x.c
index fdba3b91bd..b40918932f 100644
--- a/drivers/i2c/busses/i2c-bcm283x.c
+++ b/drivers/i2c/busses/i2c-bcm283x.c
@@ -74,7 +74,7 @@ static inline struct bcm283x_i2c *to_bcm283x_i2c(struct i2c_adapter *adapter)
static inline int bcm283x_i2c_init(struct bcm283x_i2c *bcm_i2c)
{
- struct device_d *dev = &bcm_i2c->adapter.dev;
+ struct device *dev = bcm_i2c->adapter.dev.parent;
u32 mclk_rate, cdiv, redl, fedl;
/*
@@ -130,7 +130,7 @@ static int bcm283x_i2c_msg_xfer(struct bcm283x_i2c *bcm_i2c,
{
int ret;
u32 reg_c, reg_s, reg_dlen, timeout;
- struct device_d *dev = &bcm_i2c->adapter.dev;
+ struct device *dev = &bcm_i2c->adapter.dev;
bool msg_read = (msg->flags & I2C_M_RD) > 0;
bool msg_10bit = (msg->flags & I2C_M_TEN) > 0;
u16 buf_pos = 0;
@@ -266,12 +266,12 @@ out:
return ret;
}
-static int bcm283x_i2c_probe(struct device_d *dev)
+static int bcm283x_i2c_probe(struct device *dev)
{
int ret;
struct resource *iores;
struct bcm283x_i2c *bcm_i2c;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
bcm_i2c = xzalloc(sizeof(*bcm_i2c));
@@ -308,7 +308,7 @@ static int bcm283x_i2c_probe(struct device_d *dev)
bcm_i2c->adapter.master_xfer = bcm283x_i2c_xfer;
bcm_i2c->adapter.nr = dev->id;
bcm_i2c->adapter.dev.parent = dev;
- bcm_i2c->adapter.dev.device_node = np;
+ bcm_i2c->adapter.dev.of_node = np;
ret = bcm283x_i2c_init(bcm_i2c);
if (ret)
@@ -325,8 +325,9 @@ static struct of_device_id bcm283x_i2c_dt_ids[] = {
{ .compatible = "brcm,bcm2711-i2c", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, bcm283x_i2c_dt_ids);
-static struct driver_d bcm283x_i2c_driver = {
+static struct driver bcm283x_i2c_driver = {
.name = "i2c-bcm283x",
.probe = bcm283x_i2c_probe,
.of_compatible = DRV_OF_COMPAT(bcm283x_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 5537efff23..bf9ec30994 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -16,7 +16,7 @@
#include <driver.h>
#include <io.h>
#include <linux/clk.h>
-#include <regmap.h>
+#include <linux/regmap.h>
struct __packed i2c_regs {
u32 control;
@@ -396,9 +396,9 @@ static int cdns_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
return nmsgs;
}
-static int cdns_i2c_probe(struct device_d *dev)
+static int cdns_i2c_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct resource *iores;
struct cdns_i2c *i2c;
u32 bitrate;
@@ -424,7 +424,7 @@ static int cdns_i2c_probe(struct device_d *dev)
i2c->adapter.master_xfer = cdns_i2c_xfer;
i2c->adapter.nr = dev->id;
i2c->adapter.dev.parent = dev;
- i2c->adapter.dev.device_node = np;
+ i2c->adapter.dev.of_node = np;
cdns_i2c_reset_hardware(i2c);
@@ -444,8 +444,9 @@ static const struct of_device_id cdns_i2c_match[] = {
{ .compatible = "cdns,i2c-r1p14" },
{},
};
+MODULE_DEVICE_TABLE(of, cdns_i2c_match);
-static struct driver_d cdns_i2c_driver = {
+static struct driver cdns_i2c_driver = {
.name = "cdns-i2c",
.of_compatible = cdns_i2c_match,
.probe = cdns_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
index 93b7af6623..152b795c37 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware.c
@@ -245,7 +245,7 @@ static void i2c_dw_setup_timings(struct dw_i2c_dev *dw)
u32 ht;
int ret;
- ret = of_property_read_u32(dw->adapter.dev.device_node,
+ ret = of_property_read_u32(dw->adapter.dev.of_node,
"i2c-sda-hold-time-ns", &ht);
if (ret) {
/* Keep previous hold time setting if no one set it */
@@ -504,7 +504,7 @@ static int i2c_dw_xfer(struct i2c_adapter *adapter,
}
-static int i2c_dw_probe(struct device_d *pdev)
+static int i2c_dw_probe(struct device *pdev)
{
struct resource *iores;
struct dw_i2c_dev *dw;
@@ -527,7 +527,7 @@ static int i2c_dw_probe(struct device_d *pdev)
dw->adapter.master_xfer = i2c_dw_xfer;
dw->adapter.nr = pdev->id;
dw->adapter.dev.parent = pdev;
- dw->adapter.dev.device_node = pdev->device_node;
+ dw->adapter.dev.of_node = pdev->of_node;
iores = dev_request_mem_resource(pdev, 0);
if (IS_ERR(iores)) {
@@ -599,8 +599,9 @@ static __maybe_unused struct of_device_id i2c_dw_dt_ids[] = {
{ .compatible = "snps,designware-i2c", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, i2c_dw_dt_ids);
-static struct driver_d i2c_dw_driver = {
+static struct driver i2c_dw_driver = {
.probe = i2c_dw_probe,
.name = "i2c-designware",
.of_compatible = DRV_OF_COMPAT(i2c_dw_dt_ids),
diff --git a/drivers/i2c/busses/i2c-efi.c b/drivers/i2c/busses/i2c-efi.c
new file mode 100644
index 0000000000..5f6cc0eed2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-efi.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EFI I2C master driver
+ *
+ * Copyright (C) 2024 Elrest Solutions Company s.r.o.
+ * Author: Tomas Marek <tomas.marek@elrest.cz>
+ */
+
+#include <common.h>
+#include <i2c/i2c.h>
+#include <driver.h>
+#include <efi.h>
+#include <efi/efi-device.h>
+#include <efi/efi-util.h>
+#include <linux/kernel.h>
+
+/* Define EFI I2C transfer control flags */
+#define EFI_I2C_FLAG_READ 0x00000001
+
+#define EFI_I2C_ADDRESSING_10_BIT 0x80000000
+
+/* The set_bus_frequency() EFI call doesn't work (doesn't alter SPI clock
+ * frequency) if it's parameter is defined on the stack (observed with
+ * American Megatrends EFI Revision 5.19) - let's define it globaly.
+ */
+static unsigned int bus_clock;
+
+struct efi_i2c_capabilities {
+ u32 StructureSizeInBytes;
+ u32 MaximumReceiveBytes;
+ u32 MaximumTransmitBytes;
+ u32 MaximumTotalBytes;
+};
+
+struct efi_i2c_operation {
+ u32 Flags;
+ u32 LengthInBytes;
+ u8 *Buffer;
+};
+
+struct efi_i2c_request_packet {
+ unsigned int OperationCount;
+ struct efi_i2c_operation Operation[];
+};
+
+struct efi_i2c_master_protocol {
+ efi_status_t(EFIAPI * set_bus_frequency)(
+ struct efi_i2c_master_protocol *this,
+ unsigned int *bus_clock
+ );
+ efi_status_t(EFIAPI * reset)(
+ struct efi_i2c_master_protocol *this
+ );
+ efi_status_t(EFIAPI * start_request)(
+ struct efi_i2c_master_protocol *this,
+ unsigned int slave_address,
+ struct efi_i2c_request_packet *request_packet,
+ void *event,
+ efi_status_t *status
+ );
+ struct efi_i2c_capabilities *capabilities;
+};
+
+struct efi_i2c_priv {
+ struct efi_i2c_master_protocol *efi_protocol;
+ struct i2c_adapter adapter;
+};
+
+static inline struct efi_i2c_priv *
+adapter_to_efi_i2c_priv(struct i2c_adapter *a)
+{
+ return container_of(a, struct efi_i2c_priv, adapter);
+}
+
+static efi_status_t efi_i2c_request(
+ struct efi_i2c_request_packet *request,
+ const struct efi_i2c_priv *i2c_priv,
+ const unsigned int slave_address)
+{
+ const struct device *dev = &i2c_priv->adapter.dev;
+ efi_status_t efiret;
+
+ efiret = i2c_priv->efi_protocol->start_request(
+ i2c_priv->efi_protocol,
+ slave_address,
+ request,
+ NULL,
+ NULL
+ );
+
+ if (EFI_ERROR(efiret))
+ dev_err(dev, "I2C operation failed - %s (%lx)\n",
+ efi_strerror(efiret), -efiret);
+
+ return efiret;
+}
+
+static u32 efi_i2c_max_len(const struct efi_i2c_priv *i2c_priv,
+ const struct i2c_msg *msg)
+{
+ const struct efi_i2c_capabilities *capabilities =
+ i2c_priv->efi_protocol->capabilities;
+
+ if (msg->flags & I2C_M_RD)
+ return capabilities->MaximumReceiveBytes;
+ else
+ return capabilities->MaximumTransmitBytes;
+}
+
+static unsigned int efi_i2c_msg_op_cnt(const struct efi_i2c_priv *i2c_priv,
+ const struct i2c_msg *msg)
+{
+ unsigned int max_len;
+
+ max_len = efi_i2c_max_len(i2c_priv, msg);
+
+ return ((u64)msg->len + max_len - 1) / max_len;
+}
+
+static unsigned int efi_i2c_req_op_cnt(
+ const struct efi_i2c_priv *i2c_priv,
+ const struct i2c_msg *msg,
+ const int nmsgs)
+{
+ unsigned int op_cnt = 0;
+ int i;
+
+ for (i = nmsgs; i > 0; i--, msg++)
+ op_cnt += efi_i2c_msg_op_cnt(i2c_priv, msg);
+
+ return op_cnt;
+}
+
+static void i2c_msg_to_efi_op(
+ const struct efi_i2c_priv *i2c_priv,
+ const struct i2c_msg *msg,
+ struct efi_i2c_operation **op)
+{
+ unsigned int max_len = efi_i2c_max_len(i2c_priv, msg);
+ unsigned int remaining = msg->len;
+ u32 flags;
+
+ flags = (msg->flags & I2C_M_RD) ? EFI_I2C_FLAG_READ : 0;
+
+ do {
+ unsigned int len = min(remaining, max_len);
+
+ (*op)->Flags = flags;
+ (*op)->LengthInBytes = len;
+ (*op)->Buffer = msg->buf + (msg->len - remaining);
+ (*op)++;
+
+ remaining -= len;
+ } while (remaining > 0);
+}
+
+static int i2c_msgs_to_efi_transaction(struct i2c_adapter *adapter,
+ struct efi_i2c_operation *op,
+ const struct i2c_msg *msg,
+ const int nmsgs)
+{
+ struct efi_i2c_priv *i2c_priv = adapter_to_efi_i2c_priv(adapter);
+ const struct i2c_msg *msg_tmp;
+ int ret = 0;
+ int i;
+
+ msg_tmp = msg;
+ for (i = nmsgs; i > 0; i--, msg_tmp++) {
+ if (msg_tmp->flags & I2C_M_DATA_ONLY) {
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ if (i > 0 && msg_tmp->addr != msg->addr) {
+ dev_err(&adapter->dev, "Different I2C addresses in one request not supported!\n");
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ i2c_msg_to_efi_op(i2c_priv, msg_tmp, &op);
+ }
+
+ return ret;
+}
+
+static int efi_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msg, int nmsgs)
+{
+ struct efi_i2c_priv *i2c_priv = adapter_to_efi_i2c_priv(adapter);
+ struct efi_i2c_request_packet *request_packet;
+ unsigned int slave_address;
+ efi_status_t efiret;
+ unsigned int op_cnt;
+ int ret = nmsgs;
+ int len;
+
+ op_cnt = efi_i2c_req_op_cnt(i2c_priv, msg, nmsgs);
+
+ len = sizeof(*request_packet) + op_cnt * sizeof(struct efi_i2c_operation);
+ request_packet = malloc(len);
+ if (!request_packet)
+ return -ENOMEM;
+
+ request_packet->OperationCount = op_cnt;
+ ret = i2c_msgs_to_efi_transaction(adapter, request_packet->Operation,
+ msg, nmsgs);
+ if (ret)
+ goto out_free;
+
+ slave_address = msg->addr;
+ if (msg->flags & I2C_M_TEN)
+ slave_address |= EFI_I2C_ADDRESSING_10_BIT;
+
+ efiret = efi_i2c_request(request_packet, i2c_priv, slave_address);
+ if (EFI_ERROR(efiret)) {
+ ret = -efi_errno(efiret);
+ goto out_free;
+ }
+
+ ret = nmsgs;
+
+out_free:
+ free(request_packet);
+
+ return ret;
+}
+
+static int efi_i2c_probe(struct efi_device *efidev)
+{
+ struct i2c_platform_data *pdata;
+ struct efi_i2c_priv *efi_i2c;
+ struct i2c_timings timings;
+ efi_status_t efiret;
+ int ret;
+
+ efi_i2c = xzalloc(sizeof(*efi_i2c));
+
+ efi_i2c->efi_protocol = efidev->protocol;
+
+ efi_i2c->adapter.master_xfer = efi_i2c_xfer;
+ efi_i2c->adapter.nr = efidev->dev.id;
+ efi_i2c->adapter.dev.parent = &efidev->dev;
+ efi_i2c->adapter.dev.of_node = efidev->dev.of_node;
+
+ i2c_parse_fw_timings(&efidev->dev, &timings, true);
+
+ pdata = efidev->dev.platform_data;
+ if (pdata && pdata->bitrate)
+ timings.bus_freq_hz = pdata->bitrate;
+
+ efiret = efi_i2c->efi_protocol->reset(efi_i2c->efi_protocol);
+ if (EFI_ERROR(efiret)) {
+ dev_err(&efidev->dev, "controller reset failed - %ld\n",
+ efiret);
+ ret = -efi_errno(efiret);
+ goto out_free;
+ }
+
+ bus_clock = timings.bus_freq_hz;
+ efiret = efi_i2c->efi_protocol->set_bus_frequency(
+ efi_i2c->efi_protocol,
+ &bus_clock);
+ if (EFI_ERROR(efiret)) {
+ dev_err(&efidev->dev, "I2C clock frequency %u update failed - %s (%lx)\n",
+ timings.bus_freq_hz, efi_strerror(efiret), -efiret);
+ ret = -efi_errno(efiret);
+ goto out_free;
+ }
+
+ dev_dbg(&efidev->dev, "I2C clock frequency %u\n", bus_clock);
+
+ ret = i2c_add_numbered_adapter(&efi_i2c->adapter);
+
+out_free:
+ if (ret < 0)
+ kfree(efi_i2c);
+
+ return ret;
+}
+
+static struct efi_driver efi_i2c_driver = {
+ .driver = {
+ .name = "efi-i2c",
+ },
+ .probe = efi_i2c_probe,
+ .guid = EFI_I2C_MASTER_PROTOCOL_GUID
+};
+device_efi_driver(efi_i2c_driver);
+
+MODULE_AUTHOR("Tomas Marek <tomas.marek@elrest.cz>");
+MODULE_DESCRIPTION("EFI I2C master driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index ad0c4c8f82..3649c8b189 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -126,7 +126,7 @@ static int of_i2c_gpio_probe(struct device_node *np,
return 0;
}
-static int i2c_gpio_probe(struct device_d *dev)
+static int i2c_gpio_probe(struct device *dev)
{
struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
@@ -140,8 +140,8 @@ static int i2c_gpio_probe(struct device_d *dev)
bit_data = &priv->bit_data;
pdata = &priv->pdata;
- if (dev->device_node) {
- ret = of_i2c_gpio_probe(dev->device_node, pdata);
+ if (dev->of_node) {
+ ret = of_i2c_gpio_probe(dev->of_node, pdata);
if (ret)
return ret;
} else {
@@ -193,7 +193,7 @@ static int i2c_gpio_probe(struct device_d *dev)
adap->algo_data = bit_data;
adap->dev.parent = dev;
- adap->dev.device_node = dev->device_node;
+ adap->dev.of_node = dev->of_node;
adap->bus_recovery_info = xzalloc(sizeof(*adap->bus_recovery_info));
adap->bus_recovery_info->scl_gpio = pdata->scl_pin;
adap->bus_recovery_info->sda_gpio = pdata->sda_pin;
@@ -228,8 +228,9 @@ static struct of_device_id i2c_gpio_dt_ids[] = {
{ .compatible = "i2c-gpio", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
-static struct driver_d i2c_gpio_driver = {
+static struct driver i2c_gpio_driver = {
.name = "i2c-gpio",
.probe = i2c_gpio_probe,
.of_compatible = DRV_OF_COMPAT(i2c_gpio_dt_ids),
diff --git a/drivers/i2c/busses/i2c-imx-early.c b/drivers/i2c/busses/i2c-imx-early.c
index 4e0f7e517d..1db48a85e5 100644
--- a/drivers/i2c/busses/i2c-imx-early.c
+++ b/drivers/i2c/busses/i2c-imx-early.c
@@ -90,6 +90,26 @@ static int i2c_fsl_acked(struct fsl_i2c *fsl_i2c)
return i2c_fsl_poll_status(fsl_i2c, 0, I2SR_RXAK);
}
+static void i2c_fsl_settle(struct fsl_i2c *fsl_i2c)
+{
+#ifdef CPU_ARCH_ARMv8
+ udelay(100);
+#else
+ /*
+ * We lack udelay on the 32bit i.MX SoCs, so delay manually: On an
+ * i.MX6 with a 66Mhz I2C peripheral clock one cycle of this loop
+ * takes 1.30us. Let's be generous and round up to 100 cycles. Other
+ * i.MX SoCs do not have a higher peripheral clock, so we should be
+ * safe here as well.
+ */
+
+ volatile int i = 0;
+
+ for (i = 0; i < 100; i++)
+ fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2SR);
+#endif
+}
+
static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
{
unsigned int temp = 0;
@@ -104,7 +124,7 @@ static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
fsl_i2c, FSL_I2C_I2CR);
/* Wait controller to be stable */
- udelay(100);
+ i2c_fsl_settle(fsl_i2c);
/* Start I2C transaction */
temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
@@ -303,6 +323,20 @@ struct pbl_i2c *ls1046_i2c_init(void __iomem *regs)
return &fsl_i2c.i2c;
}
+struct pbl_i2c *imx6_i2c_early_init(void __iomem *regs)
+{
+ fsl_i2c.regs = regs;
+ fsl_i2c.regshift = 2;
+ fsl_i2c.i2cr_ien_opcode = I2CR_IEN_OPCODE_1;
+ fsl_i2c.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C;
+ /* Divider for ~100kHz when coming from the ROM */
+ fsl_i2c.ifdr = 0x36;
+
+ fsl_i2c.i2c.xfer = i2c_fsl_xfer;
+
+ return &fsl_i2c.i2c;
+}
+
struct pbl_i2c *imx8m_i2c_early_init(void __iomem *regs)
{
fsl_i2c.regs = regs;
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
new file mode 100644
index 0000000000..e32bc4fd18
--- /dev/null
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This is i.MX low power i2c controller driver.
+ *
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <of.h>
+#include <gpio.h>
+#include <malloc.h>
+#include <types.h>
+#include <xfuncs.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <pinctrl.h>
+#include <of_gpio.h>
+#include <of_device.h>
+#include <pbl/i2c.h>
+
+#include <io.h>
+#include <i2c/i2c.h>
+
+#define DRIVER_NAME "imx-lpi2c"
+
+#define LPI2C_PARAM 0x04 /* i2c RX/TX FIFO size */
+#define LPI2C_MCR 0x10 /* i2c contrl register */
+#define LPI2C_MSR 0x14 /* i2c status register */
+#define LPI2C_MIER 0x18 /* i2c interrupt enable */
+#define LPI2C_MCFGR0 0x20 /* i2c master configuration */
+#define LPI2C_MCFGR1 0x24 /* i2c master configuration */
+#define LPI2C_MCFGR2 0x28 /* i2c master configuration */
+#define LPI2C_MCFGR3 0x2C /* i2c master configuration */
+#define LPI2C_MCCR0 0x48 /* i2c master clk configuration */
+#define LPI2C_MCCR1 0x50 /* i2c master clk configuration */
+#define LPI2C_MFCR 0x58 /* i2c master FIFO control */
+#define LPI2C_MFSR 0x5C /* i2c master FIFO status */
+#define LPI2C_MTDR 0x60 /* i2c master TX data register */
+#define LPI2C_MRDR 0x70 /* i2c master RX data register */
+
+/* i2c command */
+#define TRAN_DATA 0X00
+#define RECV_DATA 0X01
+#define GEN_STOP 0X02
+#define RECV_DISCARD 0X03
+#define GEN_START 0X04
+#define START_NACK 0X05
+#define START_HIGH 0X06
+#define START_HIGH_NACK 0X07
+
+#define MCR_MEN BIT(0)
+#define MCR_RST BIT(1)
+#define MCR_DOZEN BIT(2)
+#define MCR_DBGEN BIT(3)
+#define MCR_RTF BIT(8)
+#define MCR_RRF BIT(9)
+#define MSR_TDF BIT(0)
+#define MSR_RDF BIT(1)
+#define MSR_SDF BIT(9)
+#define MSR_NDF BIT(10)
+#define MSR_ALF BIT(11)
+#define MSR_MBF BIT(24)
+#define MSR_BBF BIT(25)
+#define MIER_TDIE BIT(0)
+#define MIER_RDIE BIT(1)
+#define MIER_SDIE BIT(9)
+#define MIER_NDIE BIT(10)
+#define MCFGR1_AUTOSTOP BIT(8)
+#define MCFGR1_IGNACK BIT(9)
+#define MRDR_RXEMPTY BIT(14)
+
+#define I2C_CLK_RATIO 2
+#define CHUNK_DATA 256
+
+#define I2C_PM_TIMEOUT 10 /* ms */
+
+enum lpi2c_imx_mode {
+ STANDARD, /* 100+Kbps */
+ FAST, /* 400+Kbps */
+ FAST_PLUS, /* 1.0+Mbps */
+ HS, /* 3.4+Mbps */
+ ULTRA_FAST, /* 5.0+Mbps */
+};
+
+enum lpi2c_imx_pincfg {
+ TWO_PIN_OD,
+ TWO_PIN_OO,
+ TWO_PIN_PP,
+ FOUR_PIN_PP,
+};
+
+struct lpi2c_imx_struct {
+ struct i2c_adapter adapter;
+ struct pbl_i2c pbl_i2c;
+ int num_clks;
+ struct clk_bulk_data *clks;
+ void __iomem *base;
+ __u8 *rx_buf;
+ __u8 *tx_buf;
+ unsigned int msglen;
+ unsigned int delivered;
+ unsigned int bitrate;
+ unsigned int txfifosize;
+ unsigned int rxfifosize;
+ enum lpi2c_imx_mode mode;
+ unsigned long clk_rate;
+};
+
+static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
+ unsigned int enable)
+{
+ writel(enable, lpi2c_imx->base + LPI2C_MIER);
+}
+
+static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp;
+ unsigned int timeout = 500000;
+
+ while (1) {
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+
+ /* check for arbitration lost, clear if set */
+ if (temp & (MSR_ALF | MSR_NDF)) {
+ writel(temp, lpi2c_imx->base + LPI2C_MSR);
+ return -EAGAIN;
+ }
+
+ if (temp & (MSR_BBF | MSR_MBF))
+ break;
+
+ udelay(1);
+ if (!timeout--) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int bitrate = lpi2c_imx->bitrate;
+ enum lpi2c_imx_mode mode;
+
+ if (bitrate < I2C_MAX_FAST_MODE_FREQ)
+ mode = STANDARD;
+ else if (bitrate < I2C_MAX_FAST_MODE_PLUS_FREQ)
+ mode = FAST;
+ else if (bitrate < I2C_MAX_HIGH_SPEED_MODE_FREQ)
+ mode = FAST_PLUS;
+ else if (bitrate < I2C_MAX_ULTRA_FAST_MODE_FREQ)
+ mode = HS;
+ else
+ mode = ULTRA_FAST;
+
+ lpi2c_imx->mode = mode;
+}
+
+static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ unsigned int temp;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MCR);
+ temp |= MCR_RRF | MCR_RTF;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+ writel(0x7f00, lpi2c_imx->base + LPI2C_MSR);
+
+ temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8);
+ writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+
+ return lpi2c_imx_bus_busy(lpi2c_imx);
+}
+
+static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp;
+ unsigned int timeout = 500000;
+
+ writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
+
+ do {
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+ if (temp & MSR_SDF)
+ break;
+
+ udelay(1);
+ if (!timeout--) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
+ break;
+ }
+
+ } while (1);
+}
+
+/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
+static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u8 prescale, filt, sethold, datavd;
+ unsigned int clk_cycle, clkhi, clklo;
+ enum lpi2c_imx_pincfg pincfg;
+ unsigned int temp;
+
+ lpi2c_imx_set_mode(lpi2c_imx);
+
+ if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
+ filt = 0;
+ else
+ filt = 2;
+
+ for (prescale = 0; prescale <= 7; prescale++) {
+ clk_cycle = lpi2c_imx->clk_rate / ((1 << prescale) * lpi2c_imx->bitrate)
+ - 3 - (filt >> 1);
+ clkhi = DIV_ROUND_UP(clk_cycle, I2C_CLK_RATIO + 1);
+ clklo = clk_cycle - clkhi;
+ if (clklo < 64)
+ break;
+ }
+
+ if (prescale > 7)
+ return -EINVAL;
+
+ /* set MCFGR1: PINCFG, PRESCALE, IGNACK */
+ if (lpi2c_imx->mode == ULTRA_FAST)
+ pincfg = TWO_PIN_OO;
+ else
+ pincfg = TWO_PIN_OD;
+ temp = prescale | pincfg << 24;
+
+ if (lpi2c_imx->mode == ULTRA_FAST)
+ temp |= MCFGR1_IGNACK;
+
+ writel(temp, lpi2c_imx->base + LPI2C_MCFGR1);
+
+ /* set MCFGR2: FILTSDA, FILTSCL */
+ temp = (filt << 16) | (filt << 24);
+ writel(temp, lpi2c_imx->base + LPI2C_MCFGR2);
+
+ /* set MCCR: DATAVD, SETHOLD, CLKHI, CLKLO */
+ sethold = clkhi;
+ datavd = clkhi >> 1;
+ temp = datavd << 24 | sethold << 16 | clkhi << 8 | clklo;
+
+ if (lpi2c_imx->mode == HS)
+ writel(temp, lpi2c_imx->base + LPI2C_MCCR1);
+ else
+ writel(temp, lpi2c_imx->base + LPI2C_MCCR0);
+
+ return 0;
+}
+
+static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp;
+ int ret;
+
+ temp = MCR_RST;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+ writel(0, lpi2c_imx->base + LPI2C_MCR);
+
+ ret = lpi2c_imx_config(lpi2c_imx);
+ if (ret)
+ return ret;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MCR);
+ temp |= MCR_MEN;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+
+ return 0;
+}
+
+static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u32 temp;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MCR);
+ temp &= ~MCR_MEN;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+
+ return 0;
+}
+
+static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u32 txcnt;
+ unsigned int timeout = 500000;
+
+ do {
+ txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
+
+ if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n");
+ return -EIO;
+ }
+
+ udelay(1);
+ if (!timeout--) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ } while (txcnt);
+
+ return 0;
+}
+
+static void lpi2c_imx_set_tx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ writel(lpi2c_imx->txfifosize >> 1, lpi2c_imx->base + LPI2C_MFCR);
+}
+
+static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp, remaining;
+
+ remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
+
+ if (remaining > (lpi2c_imx->rxfifosize >> 1))
+ temp = lpi2c_imx->rxfifosize >> 1;
+ else
+ temp = 0;
+
+ writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR);
+}
+
+static int lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int data, remaining;
+ unsigned int timeout = 100000;;
+
+ do {
+ u32 cnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
+ if (cnt == lpi2c_imx->txfifosize) {
+ udelay(1);
+ if (!timeout--)
+ return -EIO;
+ continue;
+ }
+
+ data = lpi2c_imx->tx_buf[lpi2c_imx->delivered++];
+
+ writel(data, lpi2c_imx->base + LPI2C_MTDR);
+ remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
+ } while (remaining);
+
+ return 0;
+}
+
+static int lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int remaining;
+ unsigned int data;
+ unsigned int timeout = 100000;;
+
+ do {
+ data = readl(lpi2c_imx->base + LPI2C_MRDR);
+ if (data & MRDR_RXEMPTY) {
+ udelay(1);
+ if (!timeout--)
+ return -EIO;
+ continue;
+ }
+
+ lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff;
+
+ remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
+ } while (remaining);
+
+ return 0;
+}
+
+static int lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ lpi2c_imx->tx_buf = msgs->buf;
+ lpi2c_imx_set_tx_watermark(lpi2c_imx);
+
+ return lpi2c_imx_write_txfifo(lpi2c_imx);
+}
+
+static int lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ unsigned int temp;
+
+ lpi2c_imx->rx_buf = msgs->buf;
+
+ lpi2c_imx_set_rx_watermark(lpi2c_imx);
+ temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
+ temp |= (RECV_DATA << 8);
+ writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
+
+ return lpi2c_imx_read_rxfifo(lpi2c_imx);
+}
+
+static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = container_of(adapter, struct lpi2c_imx_struct, adapter);
+ unsigned int temp;
+ int i, result;
+
+ result = lpi2c_imx_master_enable(lpi2c_imx);
+ if (result)
+ return result;
+
+ for (i = 0; i < num; i++) {
+ result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
+ if (result)
+ goto disable;
+
+ /* quick smbus */
+ if (num == 1 && msgs[0].len == 0)
+ goto stop;
+
+ lpi2c_imx->rx_buf = NULL;
+ lpi2c_imx->tx_buf = NULL;
+ lpi2c_imx->delivered = 0;
+ lpi2c_imx->msglen = msgs[i].len;
+
+ if (msgs[i].flags & I2C_M_RD)
+ result = lpi2c_imx_read(lpi2c_imx, &msgs[i]);
+ else
+ result = lpi2c_imx_write(lpi2c_imx, &msgs[i]);
+
+ if (result)
+ goto stop;
+
+ if (!(msgs[i].flags & I2C_M_RD)) {
+ result = lpi2c_imx_txfifo_empty(lpi2c_imx);
+ if (result)
+ goto stop;
+ }
+ }
+
+stop:
+ lpi2c_imx_stop(lpi2c_imx);
+
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+ if ((temp & MSR_NDF) && !result)
+ result = -EIO;
+
+disable:
+ lpi2c_imx_master_disable(lpi2c_imx);
+
+ dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
+ (result < 0) ? "error" : "success msg",
+ (result < 0) ? result : num);
+
+ return (result < 0) ? result : num;
+}
+
+#ifdef __PBL__
+
+static int lpi2c_pbl_imx_xfer(struct pbl_i2c *lpi2c, struct i2c_msg *msgs, int num)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = container_of(lpi2c, struct lpi2c_imx_struct, pbl_i2c);
+
+ return lpi2c_imx_xfer(&lpi2c_imx->adapter, msgs, num);
+}
+
+struct pbl_i2c *imx93_i2c_early_init(void __iomem *regs)
+{
+ static struct lpi2c_imx_struct lpi2c;
+ u32 temp;
+
+ lpi2c.base = regs;
+
+ temp = readl(lpi2c.base + LPI2C_PARAM);
+ lpi2c.txfifosize = 1 << (temp & 0x0f);
+ lpi2c.rxfifosize = 1 << ((temp >> 8) & 0x0f);
+ lpi2c.bitrate = 100000;
+ lpi2c.clk_rate = 24000000;
+
+ lpi2c.pbl_i2c.xfer = lpi2c_pbl_imx_xfer;
+
+ return &lpi2c.pbl_i2c;
+}
+
+#else
+
+static const struct of_device_id lpi2c_imx_of_match[] = {
+ { .compatible = "fsl,imx7ulp-lpi2c" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
+
+static int lpi2c_imx_probe(struct device *dev)
+{
+ struct lpi2c_imx_struct *lpi2c_imx;
+ struct resource *iores;
+ unsigned int temp;
+ int ret;
+
+ lpi2c_imx = xzalloc(sizeof(*lpi2c_imx));
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ lpi2c_imx->base = IOMEM(iores->start);
+
+ lpi2c_imx->adapter.nr = -1;
+ lpi2c_imx->adapter.master_xfer = lpi2c_imx_xfer;
+ lpi2c_imx->adapter.dev.parent = dev;
+ lpi2c_imx->adapter.dev.of_node = dev->of_node;
+
+ ret = clk_bulk_get_all(dev, &lpi2c_imx->clks);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "can't get I2C peripheral clock\n");
+ lpi2c_imx->num_clks = ret;
+
+ ret = of_property_read_u32(dev->of_node,
+ "clock-frequency", &lpi2c_imx->bitrate);
+ if (ret)
+ lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
+
+ ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ if (ret)
+ return ret;
+
+ lpi2c_imx->clk_rate = clk_get_rate(lpi2c_imx->clks[0].clk);
+
+ temp = readl(lpi2c_imx->base + LPI2C_PARAM);
+ lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
+ lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
+
+ ret = i2c_add_numbered_adapter(&lpi2c_imx->adapter);
+ if (ret)
+ return ret;
+
+ dev_dbg(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n");
+
+ return 0;
+}
+
+static struct driver lpi2c_imx_driver = {
+ .probe = lpi2c_imx_probe,
+ .name = "i2c-fsl",
+ .of_compatible = DRV_OF_COMPAT(lpi2c_imx_of_match),
+};
+coredevice_platform_driver(lpi2c_imx_driver);
+
+#endif
+
+MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
+MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index a2abfa5b43..f6a67ec067 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -102,6 +102,7 @@ struct fsl_i2c_hwdata {
struct fsl_i2c_struct {
void __iomem *base;
struct clk *clk;
+ struct device *dev;
struct i2c_adapter adapter;
unsigned int disable_delay;
unsigned int ifdr; /* FSL_I2C_IFDR */
@@ -201,7 +202,7 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
static int i2c_fsl_acked(struct i2c_adapter *adapter)
{
- return i2c_fsl_poll_status(adapter, 1, 0, I2SR_RXAK);
+ return i2c_fsl_poll_status(adapter, 5, 0, I2SR_RXAK);
}
static int i2c_fsl_start(struct i2c_adapter *adapter)
@@ -295,7 +296,7 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
* Translate to dfsr = 5 * Frequency / 100,000,000
*/
dfsr = (5 * (i2c_clk / 1000)) / 100000;
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"<%s> requested speed:%d, i2c_clk:%d\n", __func__,
rate, i2c_clk);
if (!dfsr)
@@ -314,12 +315,12 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
fdr = bin_gb | bin_ga;
rate = i2c_clk / est_div;
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x,"
" a:%d, b:%d, speed:%d\n", fdr, est_div,
ga, gb, a, b, rate);
/* Condition 2 not accounted for */
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"Tr <= %d ns\n", (b - 3 * dfsr) *
1000000 / (i2c_clk / 1000));
}
@@ -329,9 +330,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
if (a == 24)
a += 4;
}
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
- dev_dbg(&i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate);
+ dev_dbg(i2c_fsl->dev, "FDR:0x%.2x, speed:%d\n", fdr, rate);
i2c_fsl->ifdr = fdr;
i2c_fsl->dfsrr = dfsr;
}
@@ -368,9 +369,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
(500000U * i2c_clk_div[i].div + (i2c_clk_rate / 2) - 1) /
(i2c_clk_rate / 2);
- dev_dbg(&i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
+ dev_dbg(i2c_fsl->dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
__func__, i2c_clk_rate, div);
- dev_dbg(&i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
+ dev_dbg(i2c_fsl->dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
__func__, i2c_clk_div[i].val, i2c_clk_div[i].div);
}
#endif
@@ -525,15 +526,18 @@ static void i2c_fsl_unprepare_recovery(struct i2c_adapter *adapter)
dev_err(adapter->dev.parent, "pinctrl failed: %s\n", strerror(-ret));
}
-static void i2c_fsl_init_recovery(struct fsl_i2c_struct *i2c_fsl, struct device_d *dev)
+static void i2c_fsl_init_recovery(struct fsl_i2c_struct *i2c_fsl,
+ struct device *dev)
{
- if (!dev->device_node)
+ if (!dev->of_node)
return;
- i2c_fsl->rinfo.sda_gpio = of_get_named_gpio_flags(dev->device_node,
- "sda-gpios", 0, NULL);
- i2c_fsl->rinfo.scl_gpio = of_get_named_gpio_flags(dev->device_node,
- "scl-gpios", 0, NULL);
+ i2c_fsl->rinfo.sda_gpio = of_get_named_gpio_flags(dev->of_node,
+ "sda-gpios", 0,
+ NULL);
+ i2c_fsl->rinfo.scl_gpio = of_get_named_gpio_flags(dev->of_node,
+ "scl-gpios", 0,
+ NULL);
if (!gpio_is_valid(i2c_fsl->rinfo.sda_gpio) ||
!gpio_is_valid(i2c_fsl->rinfo.scl_gpio))
@@ -550,7 +554,7 @@ static void i2c_fsl_init_recovery(struct fsl_i2c_struct *i2c_fsl, struct device_
dev_dbg(dev, "initialized recovery info\n");
}
-static int __init i2c_fsl_probe(struct device_d *pdev)
+static int __init i2c_fsl_probe(struct device *pdev)
{
struct resource *iores;
struct fsl_i2c_struct *i2c_fsl;
@@ -561,6 +565,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
pdata = pdev->platform_data;
i2c_fsl = xzalloc(sizeof(*i2c_fsl));
+ i2c_fsl->dev = pdev;
#ifdef CONFIG_COMMON_CLK
i2c_fsl->clk = clk_get(pdev, NULL);
@@ -585,7 +590,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
i2c_fsl->adapter.master_xfer = i2c_fsl_xfer;
i2c_fsl->adapter.nr = pdev->id;
i2c_fsl->adapter.dev.parent = pdev;
- i2c_fsl->adapter.dev.device_node = pdev->device_node;
+ i2c_fsl->adapter.dev.of_node = pdev->of_node;
iores = dev_request_mem_resource(pdev, 0);
if (IS_ERR(iores)) {
ret = PTR_ERR(iores);
@@ -599,7 +604,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
/* Set up clock divider */
bitrate = 100000;
- of_property_read_u32(pdev->device_node, "clock-frequency", &bitrate);
+ of_property_read_u32(pdev->of_node, "clock-frequency", &bitrate);
if (pdata && pdata->bitrate)
bitrate = pdata->bitrate;
@@ -645,8 +650,9 @@ static __maybe_unused struct of_device_id imx_i2c_dt_ids[] = {
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_i2c_dt_ids);
-static struct driver_d i2c_fsl_driver = {
+static struct driver i2c_fsl_driver = {
.probe = i2c_fsl_probe,
.name = "i2c-fsl",
.of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 765fc9926b..44f1fd4ce6 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -495,6 +495,7 @@ static struct of_device_id mv64xxx_i2c_of_match_table[] = {
{ .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{}
};
+MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
static inline int
mv64xxx_calc_freq(const int tclk, const int n, const int m)
@@ -528,9 +529,9 @@ mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
static int
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
- struct device_d *pd)
+ struct device *pd)
{
- struct device_node *np = pd->device_node;
+ struct device_node *np = pd->of_node;
u32 bus_freq, tclk;
int rc = 0;
u32 prop;
@@ -596,13 +597,13 @@ out:
}
static int
-mv64xxx_i2c_probe(struct device_d *pd)
+mv64xxx_i2c_probe(struct device *pd)
{
struct resource *iores;
struct mv64xxx_i2c_data *drv_data;
int rc;
- if (!pd->device_node)
+ if (!pd->of_node)
return -ENODEV;
drv_data = xzalloc(sizeof(*drv_data));
@@ -625,7 +626,7 @@ mv64xxx_i2c_probe(struct device_d *pd)
drv_data->adapter.master_xfer = mv64xxx_i2c_xfer;
drv_data->adapter.dev.parent = pd;
drv_data->adapter.nr = pd->id;
- drv_data->adapter.dev.device_node = pd->device_node;
+ drv_data->adapter.dev.of_node = pd->of_node;
mv64xxx_i2c_hw_init(drv_data);
@@ -643,7 +644,7 @@ exit_clk:
return rc;
}
-static struct driver_d mv64xxx_i2c_driver = {
+static struct driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.name = "mv64xxx_i2c",
.of_compatible = DRV_OF_COMPAT(mv64xxx_i2c_of_match_table),
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 29a618c8b2..023c9673ba 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -28,8 +28,8 @@
#include <io.h>
#include <i2c/i2c.h>
-#include <mach/generic.h>
-#include <mach/omap3-clock.h>
+#include <mach/omap/generic.h>
+#include <mach/omap/omap3-clock.h>
/* This will be the driver name */
#define DRIVER_NAME "i2c-omap"
@@ -1059,7 +1059,7 @@ static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = {
};
static int __init
-i2c_omap_probe(struct device_d *pdev)
+i2c_omap_probe(struct device *pdev)
{
struct resource *iores;
struct omap_i2c_struct *i2c_omap;
@@ -1091,8 +1091,8 @@ i2c_omap_probe(struct device_d *pdev)
if (pdev->platform_data != NULL) {
speed = *(u32 *)pdev->platform_data;
} else {
- of_property_read_u32(pdev->device_node, "clock-frequency",
- &speed);
+ of_property_read_u32(pdev->of_node, "clock-frequency",
+ &speed);
/* convert DT freq value in Hz into kHz for speed */
speed /= 1000;
}
@@ -1170,7 +1170,7 @@ i2c_omap_probe(struct device_d *pdev)
i2c_omap->adapter.master_xfer = omap_i2c_xfer;
i2c_omap->adapter.nr = pdev->id;
i2c_omap->adapter.dev.parent = pdev;
- i2c_omap->adapter.dev.device_node = pdev->device_node;
+ i2c_omap->adapter.dev.of_node = pdev->of_node;
i2c_omap->adapter.bus_recovery_info = &omap_i2c_bus_recovery_info;
/* i2c device drivers may be active on return from add_adapter() */
@@ -1219,8 +1219,9 @@ static __maybe_unused struct of_device_id omap_i2c_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_i2c_dt_ids);
-static struct driver_d omap_i2c_driver = {
+static struct driver omap_i2c_driver = {
.probe = i2c_omap_probe,
.name = DRIVER_NAME,
.id_table = omap_i2c_ids,
diff --git a/drivers/i2c/busses/i2c-rockchip.c b/drivers/i2c/busses/i2c-rockchip.c
index ca33905335..ce029d148f 100644
--- a/drivers/i2c/busses/i2c-rockchip.c
+++ b/drivers/i2c/busses/i2c-rockchip.c
@@ -15,7 +15,7 @@
#include <io.h>
#include <linux/clk.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/sizes.h>
struct i2c_regs {
@@ -108,7 +108,7 @@ static inline void rk_i2c_get_div(int div, int *divh, int *divl)
*/
static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate)
{
- struct device_d *dev = i2c->adapter.dev.parent;
+ struct device *dev = i2c->adapter.dev.parent;
uint32_t i2c_rate;
int div, divl, divh;
@@ -130,7 +130,7 @@ static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate)
static void rk_i2c_show_regs(struct rk_i2c *i2c)
{
- struct device_d *dev = &i2c->adapter.dev;
+ struct device *dev = &i2c->adapter.dev;
struct i2c_regs *regs = i2c->regs;
int i;
@@ -152,7 +152,7 @@ static void rk_i2c_show_regs(struct rk_i2c *i2c)
static int rk_i2c_send_start_bit(struct rk_i2c *i2c)
{
- struct device_d *dev = &i2c->adapter.dev;
+ struct device *dev = &i2c->adapter.dev;
struct i2c_regs *regs = i2c->regs;
u32 val;
int err;
@@ -176,7 +176,7 @@ static int rk_i2c_send_start_bit(struct rk_i2c *i2c)
static int rk_i2c_send_stop_bit(struct rk_i2c *i2c)
{
- struct device_d *dev = &i2c->adapter.dev;
+ struct device *dev = &i2c->adapter.dev;
struct i2c_regs *regs = i2c->regs;
u32 val;
int err;
@@ -206,7 +206,7 @@ static inline void rk_i2c_disable(struct rk_i2c *i2c)
static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
uchar *buf, uint b_len)
{
- struct device_d *dev = &i2c->adapter.dev;
+ struct device *dev = &i2c->adapter.dev;
struct i2c_regs *regs = i2c->regs;
uchar *pbuf = buf;
uint bytes_remain_len = b_len;
@@ -302,7 +302,7 @@ i2c_exit:
static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
uchar *buf, uint b_len)
{
- struct device_d *dev = &i2c->adapter.dev;
+ struct device *dev = &i2c->adapter.dev;
struct i2c_regs *regs = i2c->regs;
u32 val;
int err;
@@ -379,7 +379,7 @@ static int rockchip_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
int nmsgs)
{
struct rk_i2c *i2c = to_rk_i2c(adapter);
- struct device_d *dev = &adapter->dev;
+ struct device *dev = &adapter->dev;
int i, ret = 0;
dev_dbg(dev, "i2c_xfer: %d messages\n", nmsgs);
@@ -408,9 +408,9 @@ static int rockchip_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
return ret < 0 ? ret : nmsgs;
}
-static int rk_i2c_probe(struct device_d *dev)
+static int rk_i2c_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct resource *iores;
struct rk_i2c *i2c;
unsigned bitrate;
@@ -434,7 +434,7 @@ static int rk_i2c_probe(struct device_d *dev)
i2c->adapter.master_xfer = rockchip_i2c_xfer;
i2c->adapter.nr = dev->id;
i2c->adapter.dev.parent = dev;
- i2c->adapter.dev.device_node = np;
+ i2c->adapter.dev.of_node = np;
/* Set up clock divider */
bitrate = 100000;
@@ -452,8 +452,9 @@ static const struct of_device_id rk_i2c_match[] = {
{ .compatible = "rockchip,rk3399-i2c" },
{},
};
+MODULE_DEVICE_TABLE(of, rk_i2c_match);
-static struct driver_d rk_i2c_driver = {
+static struct driver rk_i2c_driver = {
.name = "rk3x-i2c",
.of_compatible = rk_i2c_match,
.probe = rk_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c
index d412630365..1be52b3dd9 100644
--- a/drivers/i2c/busses/i2c-stm32.c
+++ b/drivers/i2c/busses/i2c-stm32.c
@@ -9,9 +9,11 @@
#include <common.h>
#include <i2c/i2c.h>
#include <init.h>
+#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/reset.h>
+#include <mfd/syscon.h>
/* STM32 I2C registers */
struct __packed stm32_i2c_regs {
@@ -38,6 +40,8 @@ struct __packed stm32_i2c_regs {
/* STM32 I2C control 1 */
#define STM32_I2C_CR1_ANFOFF BIT(12)
+#define STM32_I2C_CR1_DNF_MASK GENMASK(11, 8)
+#define STM32_I2C_CR1_DNF(n) (((n) & 0xf) << 8)
#define STM32_I2C_CR1_ERRIE BIT(7)
#define STM32_I2C_CR1_TCIE BIT(6)
#define STM32_I2C_CR1_STOPIE BIT(5)
@@ -48,7 +52,6 @@ struct __packed stm32_i2c_regs {
#define STM32_I2C_CR1_PE BIT(0)
/* STM32 I2C control 2 */
-#define STM32_I2C_CR2_AUTOEND BIT(25)
#define STM32_I2C_CR2_RELOAD BIT(24)
#define STM32_I2C_CR2_NBYTES_MASK GENMASK(23, 16)
#define STM32_I2C_CR2_NBYTES(n) ((n & 0xff) << 16)
@@ -98,10 +101,8 @@ struct __packed stm32_i2c_regs {
#define STM32_I2C_MAX_LEN 0xff
-#define STM32_I2C_DNF_DEFAULT 0
-#define STM32_I2C_DNF_MAX 16
+#define STM32_I2C_DNF_MAX 15
-#define STM32_I2C_ANALOG_FILTER_ENABLE 1
#define STM32_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
#define STM32_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
@@ -119,10 +120,9 @@ struct __packed stm32_i2c_regs {
#define FAST_PLUS_RATE 1000000
enum stm32_i2c_speed {
- STM32_I2C_SPEED_STANDARD, /* 100 kHz */
- STM32_I2C_SPEED_FAST, /* 400 kHz */
- STM32_I2C_SPEED_FAST_PLUS, /* 1 MHz */
- STM32_I2C_SPEED_END,
+ IC_SPEED_MODE_STANDARD,
+ IC_SPEED_MODE_FAST,
+ IC_SPEED_MODE_FAST_PLUS,
};
/**
@@ -154,20 +154,23 @@ struct stm32_i2c_spec {
/**
* struct stm32_i2c_setup - private I2C timing setup parameters
- * @speed: I2C speed mode (standard, Fast Plus)
- * @speed_freq: actual I2C speed frequency (Hz)
* @clock_src: I2C clock source frequency (Hz)
- * @dnf: Digital filter coefficient (0-16)
+ * @timings: I2C timing information
* @analog_filter: Analog filter delay (On/Off)
- * @timings: I2C timings parameters
*/
struct stm32_i2c_setup {
- enum stm32_i2c_speed speed;
- u32 speed_freq;
u32 clock_src;
+ struct i2c_timings timings;
u8 dnf;
bool analog_filter;
- struct i2c_timings timings;
+};
+
+/**
+ * struct stm32_i2c_data - driver data for I2C configuration by compatible
+ * @fmp_clr_offset: Fast Mode Plus clear register offset from set register
+ */
+struct stm32_i2c_data {
+ u32 fmp_clr_offset;
};
/**
@@ -187,8 +190,32 @@ struct stm32_i2c_timings {
u8 scll;
};
+/**
+ * struct stm32_i2c_priv - private data of the controller
+ * @regs: I2C registers address
+ * @clk: hw i2c clock
+ * @setup: I2C timing setup parameters
+ * @speed: I2C clock frequency of the controller. Standard, Fast or Fast+
+ * @regmap: holds SYSCFG phandle for Fast Mode Plus bit
+ * @regmap_sreg: register address for setting Fast Mode Plus bits
+ * @regmap_creg: register address for clearing Fast Mode Plus bits
+ * @regmap_mask: mask for Fast Mode Plus bits
+ */
+struct stm32_i2c_priv {
+ struct stm32_i2c_regs __iomem *regs;
+ struct clk *clk;
+ struct i2c_adapter adapter;
+ struct stm32_i2c_setup setup;
+ u32 speed;
+ struct regmap *regmap;
+ u32 regmap_sreg;
+ u32 regmap_creg;
+ u32 regmap_mask;
+};
+
static const struct stm32_i2c_spec i2c_specs[] = {
- [STM32_I2C_SPEED_STANDARD] = {
+ /* Standard speed - 100 KHz */
+ [IC_SPEED_MODE_STANDARD] = {
.rate = STANDARD_RATE,
.rate_min = 8000,
.rate_max = 120000,
@@ -200,7 +227,8 @@ static const struct stm32_i2c_spec i2c_specs[] = {
.l_min = 4700,
.h_min = 4000,
},
- [STM32_I2C_SPEED_FAST] = {
+ /* Fast speed - 400 KHz */
+ [IC_SPEED_MODE_FAST] = {
.rate = FAST_RATE,
.rate_min = 320000,
.rate_max = 480000,
@@ -212,7 +240,8 @@ static const struct stm32_i2c_spec i2c_specs[] = {
.l_min = 1300,
.h_min = 600,
},
- [STM32_I2C_SPEED_FAST_PLUS] = {
+ /* Fast Plus Speed - 1 MHz */
+ [IC_SPEED_MODE_FAST_PLUS] = {
.rate = FAST_PLUS_RATE,
.rate_min = 800000,
.rate_max = 1200000,
@@ -226,22 +255,30 @@ static const struct stm32_i2c_spec i2c_specs[] = {
},
};
-struct stm32_i2c {
- struct stm32_i2c_regs __iomem *regs;
- struct clk *clk;
- struct i2c_adapter adapter;
- struct stm32_i2c_setup setup;
+static const struct stm32_i2c_data stm32f7_data = {
+ .fmp_clr_offset = 0x00,
};
-#define to_stm32_i2c(a) container_of(a, struct stm32_i2c, adapter)
-static inline int stm32_i2c_check_device_busy(struct stm32_i2c *priv)
+static const struct stm32_i2c_data stm32mp15_data = {
+ .fmp_clr_offset = 0x40,
+};
+
+static const struct stm32_i2c_data stm32mp13_data = {
+ .fmp_clr_offset = 0x4,
+};
+
+static inline int stm32_i2c_check_device_busy(struct stm32_i2c_priv *priv)
{
u32 status = readl(&priv->regs->isr);
- return status & STM32_I2C_ISR_BUSY;
+
+ if (status & STM32_I2C_ISR_BUSY)
+ return -EBUSY;
+
+ return 0;
}
-static void stm32_i2c_message_start(struct stm32_i2c *i2c_priv,
- struct i2c_msg *msg, bool stop)
+static void stm32_i2c_message_start(struct stm32_i2c_priv *i2c_priv,
+ struct i2c_msg *msg)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
u32 cr2 = readl(&regs->cr2);
@@ -262,9 +299,8 @@ static void stm32_i2c_message_start(struct stm32_i2c *i2c_priv,
cr2 |= STM32_I2C_CR2_SADD7(msg->addr);
}
- /* Set nb bytes to transfer and reload or autoend bits */
- cr2 &= ~(STM32_I2C_CR2_NBYTES_MASK | STM32_I2C_CR2_RELOAD |
- STM32_I2C_CR2_AUTOEND);
+ /* Set nb bytes to transfer and reload (if needed) */
+ cr2 &= ~(STM32_I2C_CR2_NBYTES_MASK | STM32_I2C_CR2_RELOAD);
if (msg->len > STM32_I2C_MAX_LEN) {
cr2 |= STM32_I2C_CR2_NBYTES(STM32_I2C_MAX_LEN);
cr2 |= STM32_I2C_CR2_RELOAD;
@@ -284,8 +320,8 @@ static void stm32_i2c_message_start(struct stm32_i2c *i2c_priv,
* sent is greater than MAX_LEN
*/
-static void stm32_i2c_handle_reload(struct stm32_i2c *i2c_priv,
- struct i2c_msg *msg, bool stop)
+static void stm32_i2c_handle_reload(struct stm32_i2c_priv *i2c_priv,
+ struct i2c_msg *msg)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
u32 cr2 = readl(&regs->cr2);
@@ -302,7 +338,7 @@ static void stm32_i2c_handle_reload(struct stm32_i2c *i2c_priv,
writel(cr2, &regs->cr2);
}
-static int stm32_i2c_wait_flags(struct stm32_i2c *i2c_priv,
+static int stm32_i2c_wait_flags(struct stm32_i2c_priv *i2c_priv,
u32 flags, u32 *status)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
@@ -311,12 +347,12 @@ static int stm32_i2c_wait_flags(struct stm32_i2c *i2c_priv,
*status & flags, USEC_PER_SEC);
}
-static int stm32_i2c_check_end_of_message(struct stm32_i2c *i2c_priv)
+static int stm32_i2c_check_end_of_message(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
u32 mask = STM32_I2C_ISR_ERRORS | STM32_I2C_ISR_NACKF |
STM32_I2C_ISR_STOPF;
- struct device_d *dev = &i2c_priv->adapter.dev;
+ struct device *dev = &i2c_priv->adapter.dev;
u32 status;
int ret;
@@ -362,30 +398,29 @@ static int stm32_i2c_check_end_of_message(struct stm32_i2c *i2c_priv)
setbits_le32(&regs->icr, STM32_I2C_ICR_STOPCF);
/* Clear control register 2 */
- setbits_le32(&regs->cr2, STM32_I2C_CR2_RESET_MASK);
+ clrbits_le32(&regs->cr2, STM32_I2C_CR2_RESET_MASK);
}
return ret;
}
-static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
+static int stm32_i2c_message_xfer(struct stm32_i2c_priv *i2c_priv,
struct i2c_msg *msg, bool stop)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
- int len = msg->len;
- u8 *buf = msg->buf;
u32 status;
u32 mask = msg->flags & I2C_M_RD ? STM32_I2C_ISR_RXNE :
STM32_I2C_ISR_TXIS | STM32_I2C_ISR_NACKF;
- int bytes_to_rw = min(len, STM32_I2C_MAX_LEN);
+ int bytes_to_rw = msg->len > STM32_I2C_MAX_LEN ?
+ STM32_I2C_MAX_LEN : msg->len;
int ret = 0;
/* Add errors */
mask |= STM32_I2C_ISR_ERRORS;
- stm32_i2c_message_start(i2c_priv, msg, stop);
+ stm32_i2c_message_start(i2c_priv, msg);
- while (len) {
+ while (msg->len) {
/*
* Wait until TXIS/NACKF/BERR/ARLO flags or
* RXNE/BERR/ARLO flags are set
@@ -398,29 +433,30 @@ static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
break;
if (status & STM32_I2C_ISR_RXNE) {
- *buf++ = readb(&regs->rxdr);
- len--;
+ *msg->buf++ = readb(&regs->rxdr);
+ msg->len--;
bytes_to_rw--;
}
if (status & STM32_I2C_ISR_TXIS) {
- writeb(*buf++, &regs->txdr);
- len--;
+ writeb(*msg->buf++, &regs->txdr);
+ msg->len--;
bytes_to_rw--;
}
- if (!bytes_to_rw && len) {
+ if (!bytes_to_rw && msg->len) {
/* Wait until TCR flag is set */
mask = STM32_I2C_ISR_TCR;
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
if (ret)
break;
- bytes_to_rw = min(len, STM32_I2C_MAX_LEN);
+ bytes_to_rw = msg->len > STM32_I2C_MAX_LEN ?
+ STM32_I2C_MAX_LEN : msg->len;
mask = msg->flags & I2C_M_RD ? STM32_I2C_ISR_RXNE :
STM32_I2C_ISR_TXIS | STM32_I2C_ISR_NACKF;
- stm32_i2c_handle_reload(i2c_priv, msg, stop);
+ stm32_i2c_handle_reload(i2c_priv, msg);
} else if (!bytes_to_rw) {
/* Wait until TC flag is set */
mask = STM32_I2C_ISR_TC;
@@ -434,9 +470,9 @@ static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
}
}
- /* End of transfer, send stop condition */
- mask = STM32_I2C_CR2_STOP;
- setbits_le32(&regs->cr2, mask);
+ /* End of transfer, send stop condition if appropriate */
+ if (!ret && !(status & (STM32_I2C_ISR_NACKF | STM32_I2C_ISR_ERRORS)))
+ setbits_le32(&regs->cr2, STM32_I2C_CR2_STOP);
return stm32_i2c_check_end_of_message(i2c_priv);
}
@@ -444,16 +480,16 @@ static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
static int stm32_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msg, int nmsgs)
{
- struct stm32_i2c *i2c_priv = to_stm32_i2c(adapter);
- int ret;
- int i;
+ struct stm32_i2c_priv *i2c_priv =
+ container_of(adapter, struct stm32_i2c_priv, adapter);
+ int i, ret;
ret = stm32_i2c_check_device_busy(i2c_priv);
if (ret)
- return -EBUSY;
+ return ret;
- for (i = 0; i < nmsgs; i++) {
- ret = stm32_i2c_message_xfer(i2c_priv, &msg[i], i == nmsgs - 1);
+ for (i = nmsgs; i > 0; i--, msg++) {
+ ret = stm32_i2c_message_xfer(i2c_priv, msg, i == 1);
if (ret)
return ret;
}
@@ -461,30 +497,30 @@ static int stm32_i2c_xfer(struct i2c_adapter *adapter,
return nmsgs;
}
-
-static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
+static int stm32_i2c_compute_solutions(u32 i2cclk,
+ struct stm32_i2c_setup *setup,
+ const struct stm32_i2c_spec *specs,
struct list_head *solutions)
{
struct stm32_i2c_timings *v;
u32 p_prev = STM32_PRESC_MAX;
- u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src);
- u32 af_delay_min = 0, af_delay_max = 0;
+ u32 af_delay_min, af_delay_max;
u16 p, l, a;
int sdadel_min, sdadel_max, scldel_min;
int ret = 0;
- if (setup->analog_filter) {
- af_delay_min = STM32_I2C_ANALOG_FILTER_DELAY_MIN;
- af_delay_max = STM32_I2C_ANALOG_FILTER_DELAY_MAX;
- }
+ af_delay_min = setup->analog_filter ?
+ STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0;
+ af_delay_max = setup->analog_filter ?
+ STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0;
- sdadel_min = i2c_specs[setup->speed].hddat_min + setup->timings.scl_fall_ns -
+ sdadel_min = specs->hddat_min + setup->timings.scl_fall_ns -
af_delay_min - (setup->dnf + 3) * i2cclk;
- sdadel_max = i2c_specs[setup->speed].vddat_max - setup->timings.scl_rise_ns -
+ sdadel_max = specs->vddat_max - setup->timings.scl_rise_ns -
af_delay_max - (setup->dnf + 4) * i2cclk;
- scldel_min = setup->timings.scl_rise_ns + i2c_specs[setup->speed].sudat_min;
+ scldel_min = setup->timings.scl_rise_ns + specs->sudat_min;
if (sdadel_min < 0)
sdadel_min = 0;
@@ -497,13 +533,13 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
/* Compute possible values for PRESC, SCLDEL and SDADEL */
for (p = 0; p < STM32_PRESC_MAX; p++) {
for (l = 0; l < STM32_SCLDEL_MAX; l++) {
- u32 scldel = (l + 1) * (p + 1) * i2cclk;
+ int scldel = (l + 1) * (p + 1) * i2cclk;
if (scldel < scldel_min)
continue;
for (a = 0; a < STM32_SDADEL_MAX; a++) {
- u32 sdadel = (a * (p + 1) + 1) * i2cclk;
+ int sdadel = (a * (p + 1) + 1) * i2cclk;
if (((sdadel >= sdadel_min) &&
(sdadel <= sdadel_max)) &&
@@ -527,36 +563,40 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
}
}
- if (list_empty(solutions))
+ if (list_empty(solutions)) {
+ pr_err("no Prescaler solution\n");
ret = -EPERM;
+ }
return ret;
}
-static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
+static int stm32_i2c_choose_solution(u32 i2cclk,
+ struct stm32_i2c_setup *setup,
+ const struct stm32_i2c_spec *specs,
struct list_head *solutions,
struct stm32_i2c_timings *s)
{
struct stm32_i2c_timings *v;
- u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->speed_freq);
+ u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
+ setup->timings.bus_freq_hz);
u32 clk_error_prev = i2cbus;
- u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src);
u32 clk_min, clk_max;
- u32 af_delay_min = 0;
+ u32 af_delay_min;
u32 dnf_delay;
u32 tsync;
u16 l, h;
bool sol_found = false;
int ret = 0;
- if (setup->analog_filter)
- af_delay_min = STM32_I2C_ANALOG_FILTER_DELAY_MIN;
+ af_delay_min = setup->analog_filter ?
+ STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0;
dnf_delay = setup->dnf * i2cclk;
tsync = af_delay_min + dnf_delay + (2 * i2cclk);
- clk_max = NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
- clk_min = NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;
+ clk_max = NSEC_PER_SEC / specs->rate_min;
+ clk_min = NSEC_PER_SEC / specs->rate_max;
/*
* Among Prescaler possibilities discovered above figures out SCL Low
@@ -574,7 +614,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
for (l = 0; l < STM32_SCLL_MAX; l++) {
u32 tscl_l = (l + 1) * prescaler + tsync;
- if ((tscl_l < i2c_specs[setup->speed].l_min) ||
+ if (tscl_l < specs->l_min ||
(i2cclk >=
((tscl_l - af_delay_min - dnf_delay) / 4))) {
continue;
@@ -583,16 +623,17 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
for (h = 0; h < STM32_SCLH_MAX; h++) {
u32 tscl_h = (h + 1) * prescaler + tsync;
u32 tscl = tscl_l + tscl_h +
- setup->timings.scl_rise_ns +
- setup->timings.scl_fall_ns;
+ setup->timings.scl_rise_ns + setup->timings.scl_fall_ns;
if ((tscl >= clk_min) && (tscl <= clk_max) &&
- (tscl_h >= i2c_specs[setup->speed].h_min) &&
+ (tscl_h >= specs->h_min) &&
(i2cclk < tscl_h)) {
- int clk_error = tscl - i2cbus;
+ u32 clk_error;
- if (clk_error < 0)
- clk_error = -clk_error;
+ if (tscl > i2cbus)
+ clk_error = tscl - i2cbus;
+ else
+ clk_error = i2cbus - tscl;
if (clk_error < clk_error_prev) {
clk_error_prev = clk_error;
@@ -606,61 +647,68 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
}
}
- if (!sol_found)
+ if (!sol_found) {
+ pr_err("no solution at all\n");
ret = -EPERM;
+ }
return ret;
}
-static int stm32_i2c_compute_timing(struct stm32_i2c *i2c_priv,
- struct stm32_i2c_setup *setup,
+static const struct stm32_i2c_spec *get_specs(u32 rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_specs); i++)
+ if (rate <= i2c_specs[i].rate)
+ return &i2c_specs[i];
+
+ /* NOT REACHED */
+ return ERR_PTR(-EINVAL);
+}
+
+static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
struct stm32_i2c_timings *output)
{
- struct device_d *dev = &i2c_priv->adapter.dev;
+ struct device *dev = &i2c_priv->adapter.dev;
+ struct stm32_i2c_setup *setup = &i2c_priv->setup;
+ const struct stm32_i2c_spec *specs;
struct stm32_i2c_timings *v, *_v;
struct list_head solutions;
+ u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src);
int ret;
- if (setup->speed >= STM32_I2C_SPEED_END) {
- dev_err(dev, "speed out of bound {%d/%d}\n",
- setup->speed, STM32_I2C_SPEED_END - 1);
+ specs = get_specs(setup->timings.bus_freq_hz);
+ if (specs == ERR_PTR(-EINVAL)) {
+ dev_err(dev, "speed out of bound {%d}\n",
+ setup->timings.bus_freq_hz);
return -EINVAL;
}
- if ((setup->timings.scl_rise_ns > i2c_specs[setup->speed].rise_max) ||
- (setup->timings.scl_fall_ns > i2c_specs[setup->speed].fall_max)) {
+ if (setup->timings.scl_rise_ns > specs->rise_max ||
+ setup->timings.scl_fall_ns > specs->fall_max) {
dev_err(dev, "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
- setup->timings.scl_rise_ns, i2c_specs[setup->speed].rise_max,
- setup->timings.scl_fall_ns, i2c_specs[setup->speed].fall_max);
+ setup->timings.scl_rise_ns, specs->rise_max,
+ setup->timings.scl_fall_ns, specs->fall_max);
return -EINVAL;
}
+ /* Analog and Digital Filters */
+ setup->dnf = DIV_ROUND_CLOSEST(i2c_priv->setup.timings.digital_filter_width_ns, i2cclk);
if (setup->dnf > STM32_I2C_DNF_MAX) {
dev_err(dev, "DNF out of bound %d/%d\n",
- setup->dnf, STM32_I2C_DNF_MAX);
- return -EINVAL;
- }
-
- if (setup->speed_freq > i2c_specs[setup->speed].rate) {
- dev_err(dev, "Freq {%d/%d}\n",
- setup->speed_freq, i2c_specs[setup->speed].rate);
+ setup->dnf, STM32_I2C_DNF_MAX);
return -EINVAL;
}
INIT_LIST_HEAD(&solutions);
- ret = stm32_i2c_compute_solutions(setup, &solutions);
- if (ret) {
- if (ret == -EPERM)
- dev_err(dev, "No prescaler solution\n");
+ ret = stm32_i2c_compute_solutions(i2cclk, setup, specs, &solutions);
+ if (ret)
goto exit;
- }
- ret = stm32_i2c_choose_solution(setup, &solutions, output);
- if (ret) {
- if (ret == -EPERM)
- dev_err(dev, "no solution at all\n");
+ ret = stm32_i2c_choose_solution(i2cclk, setup, specs, &solutions, output);
+ if (ret)
goto exit;
- }
dev_dbg(dev, "Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n",
output->presc,
@@ -677,16 +725,25 @@ exit:
return ret;
}
-static int stm32_i2c_setup_timing(struct stm32_i2c *i2c_priv,
- enum stm32_i2c_speed speed,
+static u32 get_lower_rate(u32 rate)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(i2c_specs) - 1; i >= 0; i--)
+ if (rate > i2c_specs[i].rate)
+ return i2c_specs[i].rate;
+
+ return i2c_specs[0].rate;
+}
+
+static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
struct stm32_i2c_timings *timing)
{
- struct device_d *dev = &i2c_priv->adapter.dev;
+ struct device *dev = &i2c_priv->adapter.dev;
struct stm32_i2c_setup *setup = &i2c_priv->setup;
int ret = 0;
- setup->speed = speed;
- setup->speed_freq = i2c_specs[setup->speed].rate;
+ setup->timings.bus_freq_hz = i2c_priv->speed;
setup->clock_src = clk_get_rate(i2c_priv->clk);
if (!setup->clock_src) {
@@ -695,15 +752,14 @@ static int stm32_i2c_setup_timing(struct stm32_i2c *i2c_priv,
}
do {
- ret = stm32_i2c_compute_timing(i2c_priv, setup, timing);
+ ret = stm32_i2c_compute_timing(i2c_priv, timing);
if (ret) {
dev_dbg(dev, "failed to compute I2C timings.\n");
- if (speed > STM32_I2C_SPEED_STANDARD) {
- speed--;
- setup->speed = speed;
- setup->speed_freq = i2c_specs[setup->speed].rate;
+ if (setup->timings.bus_freq_hz > STANDARD_RATE) {
+ setup->timings.bus_freq_hz =
+ get_lower_rate(setup->timings.bus_freq_hz);
dev_dbg(dev, "downgrade I2C Speed Freq to (%i)\n",
- i2c_specs[setup->speed].rate);
+ setup->timings.bus_freq_hz);
} else {
break;
}
@@ -715,31 +771,60 @@ static int stm32_i2c_setup_timing(struct stm32_i2c *i2c_priv,
return ret;
}
- dev_dbg(dev, "I2C Speed(%i), Freq(%i), Clk Source(%i)\n",
- setup->speed, setup->speed_freq, setup->clock_src);
+ dev_dbg(dev, "I2C Freq(%i), Clk Source(%i)\n",
+ setup->timings.bus_freq_hz, setup->clock_src);
dev_dbg(dev, "I2C Rise(%i) and Fall(%i) Time\n",
- setup->timings.scl_rise_ns, setup->timings.scl_fall_ns);
+ setup->timings.scl_rise_ns, setup->timings.scl_fall_ns);
dev_dbg(dev, "I2C Analog Filter(%s), DNF(%i)\n",
- setup->analog_filter ? "On" : "Off", setup->dnf);
+ setup->analog_filter ? "On" : "Off", setup->dnf);
+
+ i2c_priv->speed = setup->timings.bus_freq_hz;
return 0;
}
-static int stm32_i2c_hw_config(struct stm32_i2c *i2c_priv,
- enum stm32_i2c_speed speed)
+static int stm32_i2c_write_fm_plus_bits(struct stm32_i2c_priv *i2c_priv)
+{
+ int ret;
+ bool enable = i2c_priv->speed > FAST_RATE;
+
+ /* Optional */
+ if (IS_ERR_OR_NULL(i2c_priv->regmap))
+ return 0;
+
+ if (i2c_priv->regmap_sreg == i2c_priv->regmap_creg)
+ ret = regmap_update_bits(i2c_priv->regmap,
+ i2c_priv->regmap_sreg,
+ i2c_priv->regmap_mask,
+ enable ? i2c_priv->regmap_mask : 0);
+ else
+ ret = regmap_write(i2c_priv->regmap,
+ enable ? i2c_priv->regmap_sreg :
+ i2c_priv->regmap_creg,
+ i2c_priv->regmap_mask);
+
+ return ret;
+}
+
+static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
struct stm32_i2c_timings t;
int ret;
u32 timing = 0;
- ret = stm32_i2c_setup_timing(i2c_priv, speed, &t);
+ ret = stm32_i2c_setup_timing(i2c_priv, &t);
if (ret)
return ret;
/* Disable I2C */
clrbits_le32(&regs->cr1, STM32_I2C_CR1_PE);
+ /* Setup Fast mode plus if necessary */
+ ret = stm32_i2c_write_fm_plus_bits(i2c_priv);
+ if (ret)
+ return ret;
+
/* Timing settings */
timing |= STM32_I2C_TIMINGR_PRESC(t.presc);
timing |= STM32_I2C_TIMINGR_SCLDEL(t.scldel);
@@ -753,44 +838,74 @@ static int stm32_i2c_hw_config(struct stm32_i2c *i2c_priv,
clrbits_le32(&regs->cr1, STM32_I2C_CR1_ANFOFF);
else
setbits_le32(&regs->cr1, STM32_I2C_CR1_ANFOFF);
+
+ /* Program the Digital Filter */
+ clrsetbits_le32(&regs->cr1, STM32_I2C_CR1_DNF_MASK,
+ STM32_I2C_CR1_DNF(i2c_priv->setup.dnf));
+
setbits_le32(&regs->cr1, STM32_I2C_CR1_PE);
return 0;
}
-static int stm32_i2c_set_bus_speed(struct stm32_i2c *i2c_priv, unsigned speed)
+static int stm32_i2c_set_bus_speed(struct stm32_i2c_priv *i2c_priv, unsigned int speed)
{
- struct device_d *parent_dev = i2c_priv->adapter.dev.parent;
- enum stm32_i2c_speed stm32_speed;
- switch (speed) {
- case STANDARD_RATE:
- stm32_speed = STM32_I2C_SPEED_STANDARD;
- break;
- case FAST_RATE:
- stm32_speed = STM32_I2C_SPEED_FAST;
- break;
- case FAST_PLUS_RATE:
- stm32_speed = STM32_I2C_SPEED_FAST_PLUS;
- break;
- default:
- dev_warn(parent_dev, "Speed %d not supported\n", speed);
+ struct device *dev = &i2c_priv->adapter.dev;
+
+ if (speed > FAST_PLUS_RATE) {
+ dev_dbg(dev, "Speed %d not supported\n", speed);
return -EINVAL;
}
- return stm32_i2c_hw_config(i2c_priv, stm32_speed);
+ i2c_priv->speed = speed;
+
+ return stm32_i2c_hw_config(i2c_priv);
+}
+
+static int stm32_of_to_plat(struct device *dev, struct stm32_i2c_priv *i2c_priv)
+{
+ const struct stm32_i2c_data *data;
+ int ret;
+
+ ret = dev_get_drvdata(dev, (const void **)&data);
+ if (ret)
+ return ret;
+
+ if (of_property_read_u32(dev->of_node, "i2c-digital-filter-width-ns",
+ &i2c_priv->setup.timings.digital_filter_width_ns))
+ i2c_priv->setup.timings.digital_filter_width_ns = 0;
+ if (!of_property_read_bool(dev->of_node, "i2c-digital-filter"))
+ i2c_priv->setup.timings.digital_filter_width_ns = 0;
+
+ i2c_priv->setup.analog_filter =
+ of_property_read_bool(dev->of_node, "i2c-analog-filter");
+
+ /* Optional */
+ i2c_priv->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "st,syscfg-fmp");
+ if (!IS_ERR(i2c_priv->regmap)) {
+ u32 fmp[3];
+
+ ret = of_property_read_u32_array(dev->of_node, "st,syscfg-fmp", fmp, 3);
+ if (ret)
+ return ret;
+
+ i2c_priv->regmap_sreg = fmp[1];
+ i2c_priv->regmap_creg = fmp[1] + data->fmp_clr_offset;
+ i2c_priv->regmap_mask = fmp[2];
+ }
+
+ return 0;
}
-static int __init stm32_i2c_probe(struct device_d *dev)
+static int __init stm32_i2c_probe(struct device *dev)
{
struct resource *iores;
- struct stm32_i2c *stm32_i2c;
+ struct stm32_i2c_priv *stm32_i2c;
struct i2c_platform_data *pdata;
- const struct stm32_i2c_setup *setup;
struct i2c_timings *timings;
int ret;
- pdata = dev->platform_data;
-
stm32_i2c = xzalloc(sizeof(*stm32_i2c));
stm32_i2c->clk = clk_get(dev, NULL);
@@ -798,15 +913,20 @@ static int __init stm32_i2c_probe(struct device_d *dev)
return PTR_ERR(stm32_i2c->clk);
clk_enable(stm32_i2c->clk);
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ stm32_i2c->regs = IOMEM(iores->start);
+
ret = device_reset_us(dev, 2);
if (ret)
return ret;
- ret = dev_get_drvdata(dev, (const void **)&setup);
+ ret = stm32_of_to_plat(dev, stm32_i2c);
if (ret)
return ret;
- stm32_i2c->setup = *setup;
timings = &stm32_i2c->setup.timings;
/* We've our own defaults, so don't use the i2c_parse_fw_timings ones */
@@ -823,13 +943,9 @@ static int __init stm32_i2c_probe(struct device_d *dev)
stm32_i2c->adapter.master_xfer = stm32_i2c_xfer;
stm32_i2c->adapter.nr = dev->id;
stm32_i2c->adapter.dev.parent = dev;
- stm32_i2c->adapter.dev.device_node = dev->device_node;
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
-
- stm32_i2c->regs = IOMEM(iores->start);
+ stm32_i2c->adapter.dev.of_node = dev->of_node;
+ pdata = dev->platform_data;
if (pdata && pdata->bitrate)
timings->bus_freq_hz = pdata->bitrate;
@@ -840,18 +956,15 @@ static int __init stm32_i2c_probe(struct device_d *dev)
return i2c_add_numbered_adapter(&stm32_i2c->adapter);
}
-static const struct stm32_i2c_setup stm32f7_setup = {
- .dnf = STM32_I2C_DNF_DEFAULT,
- .analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
-};
-
static __maybe_unused struct of_device_id stm32_i2c_dt_ids[] = {
- { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup, },
- { .compatible = "st,stm32mp15-i2c", .data = &stm32f7_setup},
+ { .compatible = "st,stm32f7-i2c", .data = &stm32f7_data },
+ { .compatible = "st,stm32mp15-i2c", .data = &stm32mp15_data },
+ { .compatible = "st,stm32mp13-i2c", .data = &stm32mp13_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_i2c_dt_ids);
-static struct driver_d stm32_i2c_driver = {
+static struct driver stm32_i2c_driver = {
.probe = stm32_i2c_probe,
.name = "stm32f7-i2c",
.of_compatible = DRV_OF_COMPAT(stm32_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 077b55e175..f86f64f573 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -137,7 +137,7 @@ struct tegra_i2c_hw_feature {
* @bus_clk_rate: current i2c bus clock rate
*/
struct tegra_i2c_dev {
- struct device_d *dev;
+ struct device *dev;
const struct tegra_i2c_hw_feature *hw;
struct i2c_adapter adapter;
struct clk *div_clk;
@@ -594,7 +594,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.clk_divisor_std_fast_mode = 0x19,
};
-static int tegra_i2c_probe(struct device_d *dev)
+static int tegra_i2c_probe(struct device *dev)
{
struct resource *iores;
struct tegra_i2c_dev *i2c_dev;
@@ -627,14 +627,14 @@ static int tegra_i2c_probe(struct device_d *dev)
return PTR_ERR(i2c_dev->rst);
}
- ret = of_property_read_u32(dev->device_node, "clock-frequency",
+ ret = of_property_read_u32(dev->of_node, "clock-frequency",
&i2c_dev->bus_clk_rate);
if (ret)
i2c_dev->bus_clk_rate = 100000; /* default clock rate */
i2c_dev->hw = &tegra20_i2c_hw;
dev_get_drvdata(dev, (const void **)&i2c_dev->hw);
- i2c_dev->is_dvc = of_device_is_compatible(dev->device_node,
+ i2c_dev->is_dvc = of_device_is_compatible(dev->of_node,
"nvidia,tegra20-i2c-dvc");
if (!i2c_dev->hw->has_single_clk_source) {
@@ -655,7 +655,7 @@ static int tegra_i2c_probe(struct device_d *dev)
i2c_dev->adapter.master_xfer = tegra_i2c_xfer;
i2c_dev->adapter.dev.parent = dev;
i2c_dev->adapter.nr = dev->id;
- i2c_dev->adapter.dev.device_node = dev->device_node;
+ i2c_dev->adapter.dev.of_node = dev->of_node;
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
@@ -683,8 +683,9 @@ static __maybe_unused struct of_device_id tegra_i2c_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra_i2c_compatible);
-static struct driver_d tegra_i2c_driver = {
+static struct driver tegra_i2c_driver = {
.name = "tegra-i2c",
.probe = tegra_i2c_probe,
.of_compatible = DRV_OF_COMPAT(tegra_i2c_compatible),
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
index ece483f6f5..f508cf1506 100644
--- a/drivers/i2c/busses/i2c-versatile.c
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -63,7 +63,7 @@ static struct i2c_algo_bit_data i2c_versatile_algo = {
.timeout_ms = 100,
};
-static int i2c_versatile_probe(struct device_d *dev)
+static int i2c_versatile_probe(struct device *dev)
{
struct resource *iores;
struct i2c_versatile *i2c;
@@ -86,7 +86,7 @@ static int i2c_versatile_probe(struct device_d *dev)
i2c->adap.algo_data = &i2c->algo;
i2c->adap.dev.parent = dev;
- i2c->adap.dev.device_node = dev->device_node;
+ i2c->adap.dev.of_node = dev->of_node;
i2c->algo = i2c_versatile_algo;
i2c->algo.data = i2c;
@@ -106,8 +106,9 @@ static struct of_device_id i2c_versatile_match[] = {
{ .compatible = "arm,versatile-i2c", },
{},
};
+MODULE_DEVICE_TABLE(of, i2c_versatile_match);
-static struct driver_d i2c_versatile_driver = {
+static struct driver i2c_versatile_driver = {
.name = "versatile-i2c",
.probe = i2c_versatile_probe,
.of_compatible = DRV_OF_COMPAT(i2c_versatile_match),
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 233d231525..6695ec0411 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -31,7 +31,7 @@ struct i2c_mux_priv {
struct i2c_adapter adap;
struct i2c_adapter *parent;
- struct device_d *mux_dev;
+ struct device *mux_dev;
void *mux_priv;
u32 chan_id;
@@ -58,7 +58,7 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
}
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
- struct device_d *mux_dev,
+ struct device *mux_dev,
void *mux_priv, u32 force_nr, u32 chan_id,
int (*select) (struct i2c_adapter *,
void *, u32),
@@ -95,16 +95,16 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
* Try to populate the mux adapter's device_node, expands to
* nothing if !CONFIG_OFDEVICE.
*/
- if (mux_dev->device_node) {
+ if (mux_dev->of_node) {
struct device_node *child;
u32 reg;
- for_each_child_of_node(mux_dev->device_node, child) {
+ for_each_child_of_node(mux_dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &reg);
if (ret)
continue;
if (chan_id == reg) {
- priv->adap.dev.device_node = child;
+ priv->adap.dev.of_node = child;
break;
}
}
diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index 40590b7d11..5471519045 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -23,6 +23,7 @@
#include <init.h>
#include <of.h>
#include <gpio.h>
+#include <slice.h>
#include <i2c/i2c.h>
@@ -62,6 +63,8 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
uint64_t start;
int ret, try;
+ slice_acquire(&adap->slice);
+
/*
* REVISIT the fault reporting model here is weak:
*
@@ -96,6 +99,8 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
break;
}
+ slice_release(&adap->slice);
+
return ret;
}
EXPORT_SYMBOL(i2c_transfer);
@@ -253,7 +258,7 @@ int i2c_get_sda_gpio_value(struct i2c_adapter *adap)
static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
- struct device_d *dev = &adap->dev;
+ struct device *dev = &adap->dev;
int ret = 0;
ret = gpio_request_one(bri->scl_gpio, GPIOF_IN, "i2c-scl");
@@ -353,13 +358,13 @@ int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
int i2c_recover_bus(struct i2c_adapter *adap)
{
if (!adap->bus_recovery_info)
- return -EOPNOTSUPP;
+ return -EBUSY;
dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
return adap->bus_recovery_info->recover_bus(adap);
}
-static void i2c_info(struct device_d *dev)
+static void i2c_info(struct device *dev)
{
const struct i2c_client *client = to_i2c_client(dev);
@@ -394,7 +399,7 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
client->dev.platform_data = chip->platform_data;
client->dev.id = DEVICE_ID_DYNAMIC;
client->dev.bus = &i2c_bus;
- client->dev.device_node = chip->of_node;
+ client->dev.of_node = chip->of_node;
client->adapter = adapter;
client->addr = chip->addr;
@@ -410,6 +415,9 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
if (chip->of_node)
chip->of_node->dev = &client->dev;
+ dev_dbg(&client->dev, "registered on bus %d, chip->addr 0x%02x\n",
+ adapter->nr, client->addr);
+
return client;
}
@@ -418,10 +426,10 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
struct device_node *n;
/* Only register child devices if the adapter has a node pointer set */
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !adap->dev.device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !adap->dev.of_node)
return;
- for_each_available_child_of_node(adap->dev.device_node, n) {
+ for_each_available_child_of_node(adap->dev.of_node, n) {
struct i2c_board_info info = {};
struct i2c_client *result;
const __be32 *addr;
@@ -439,22 +447,21 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
addr = of_get_property(n, "reg", &len);
if (!addr || (len < sizeof(int))) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
- n->full_name);
+ dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", n);
continue;
}
info.addr = be32_to_cpup(addr);
if (info.addr > (1 << 10) - 1) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, n->full_name);
+ dev_err(&adap->dev, "of_i2c: invalid addr=%x on %pOF\n",
+ info.addr, n);
continue;
}
result = i2c_new_device(adap, &info);
if (!result)
- dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
- n->full_name);
+ dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n",
+ n);
}
}
@@ -472,6 +479,18 @@ int of_i2c_register_devices_by_node(struct device_node *node)
return 0;
}
+static void i2c_hw_rescan(struct device *dev)
+{
+ struct i2c_adapter *adap;
+
+ list_for_each_entry(adap, &i2c_adapter_list, list) {
+ if (dev != adap->dev.parent)
+ continue;
+ of_i2c_register_devices(adap);
+ break;
+ }
+}
+
/**
* i2c_new_dummy - return a new i2c device bound to a dummy driver
* @adapter: the adapter managing the device
@@ -541,7 +560,6 @@ static void scan_boardinfo(struct i2c_adapter *adapter)
continue;
for (n = bi->n_board_info; n > 0; n--, chip++) {
- debug("%s: bus_num: %d, chip->addr 0x%02x\n", __func__, bi->bus_num, chip->addr);
/*
* NOTE: this relies on i2c_new_device to
* issue diagnostics when given bogus inputs
@@ -575,10 +593,10 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
ret = of_device_ensure_probed(node);
if (ret)
- return ERR_PTR(ret);
+ return NULL;
for_each_i2c_adapter(adap)
- if (adap->dev.device_node == node)
+ if (adap->dev.of_node == node)
return adap;
return NULL;
@@ -586,7 +604,7 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
- struct device_d *dev = of_find_device_by_node(node);
+ struct device *dev = of_find_device_by_node(node);
if (!dev)
return NULL;
@@ -610,12 +628,13 @@ int of_i2c_device_enable_and_register_by_alias(const char *alias)
}
-static void i2c_parse_timing(struct device_d *dev, char *prop_name, u32 *cur_val_p,
- u32 def_val, bool use_def)
+static void i2c_parse_timing(struct device *dev, char *prop_name,
+ u32 *cur_val_p,
+ u32 def_val, bool use_def)
{
int ret;
- ret = of_property_read_u32(dev->device_node, prop_name, cur_val_p);
+ ret = of_property_read_u32(dev->of_node, prop_name, cur_val_p);
if (ret && use_def)
*cur_val_p = def_val;
@@ -638,7 +657,8 @@ static void i2c_parse_timing(struct device_d *dev, char *prop_name, u32 *cur_val
* to switch to this function. New drivers almost always should use the defaults.
*/
-void i2c_parse_fw_timings(struct device_d *dev, struct i2c_timings *t, bool use_defaults)
+void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t,
+ bool use_defaults)
{
bool u = use_defaults;
u32 d;
@@ -682,6 +702,7 @@ EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
*/
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
{
+ struct device *hw_dev;
int ret;
if (adapter->nr < 0) {
@@ -705,31 +726,26 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
list_add_tail(&adapter->list, &i2c_adapter_list);
+ slice_init(&adapter->slice, dev_name(&adapter->dev));
+
/* populate children from any i2c device tables */
scan_boardinfo(adapter);
of_i2c_register_devices(adapter);
+ hw_dev = adapter->dev.parent;
+ if (hw_dev && dev_of_node(hw_dev)) {
+ if (!hw_dev->rescan)
+ hw_dev->rescan = i2c_hw_rescan;
+ }
+
return 0;
}
EXPORT_SYMBOL(i2c_add_numbered_adapter);
-static int i2c_probe(struct device_d *dev)
-{
- return dev->driver->probe(dev);
-}
-
-static void i2c_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
struct bus_type i2c_bus = {
.name = "i2c",
.match = device_match_of_modalias,
- .probe = i2c_probe,
- .remove = i2c_remove,
};
static int i2c_bus_init(void)
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index f547527fed..6c21b92860 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -180,7 +180,7 @@ static int pca954x_deselect_chan(struct i2c_adapter *adap,
/*
* I2C init/probing/exit functions
*/
-static int pca954x_probe(struct device_d *dev)
+static int pca954x_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
@@ -199,7 +199,7 @@ static int pca954x_probe(struct device_d *dev)
i2c_set_clientdata(client, data);
- gpio = of_get_named_gpio(dev->device_node, "reset-gpios", 0);
+ gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
if (gpio_is_valid(gpio))
gpio_direction_output(gpio, 1);
@@ -215,7 +215,7 @@ static int pca954x_probe(struct device_d *dev)
if (ret)
goto exit_free;
- idle_disconnect = of_property_read_bool(dev->device_node,
+ idle_disconnect = of_property_read_bool(dev->of_node,
"i2c-mux-idle-disconnect");
data->last_chan = 0; /* force the first selection */
@@ -255,7 +255,7 @@ err:
return ret;
}
-static struct driver_d pca954x_driver = {
+static struct driver pca954x_driver = {
.name = "pca954x",
.probe = pca954x_probe,
.id_table = pca954x_id,
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 52234c0ecc..01e0b722c2 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -12,6 +12,17 @@ config INPUT
config INPUT_MATRIXKMAP
bool
+config INPUT_EVBUG
+ bool "Event debugging"
+ help
+ Say Y here if you have a problem with the input subsystem and
+ want all events (keypresses), to be output to
+ the barebox log. While this is useful for debugging, it's also
+ a security threat - your keypresses include your passwords, of
+ course and could be visible to userspace if pstore is configured.
+
+ If unsure, say N.
+
config KEYBOARD_GPIO
bool "GPIO Buttons"
depends on GENERIC_GPIO
diff --git a/drivers/input/gpio_keys.c b/drivers/input/gpio_keys.c
index 45b3c562f8..c897acf3bd 100644
--- a/drivers/input/gpio_keys.c
+++ b/drivers/input/gpio_keys.c
@@ -29,24 +29,24 @@ struct gpio_keys {
struct gpio_key *buttons;
int nbuttons;
- struct poller_struct poller;
+ struct poller_async poller;
struct input_device input;
- struct device_d *dev;
};
-static inline struct gpio_keys *
-poller_to_gk_pdata(struct poller_struct *poller)
+static void gpio_key_poller(void *data)
{
- return container_of(poller, struct gpio_keys, poller);
-}
-
-static void gpio_key_poller(struct poller_struct *poller)
-{
- struct gpio_keys *gk = poller_to_gk_pdata(poller);
+ struct gpio_keys *gk = data;
struct gpio_key *gb;
int i, val;
for (i = 0; i < gk->nbuttons; i++) {
+ gb = &gk->buttons[i];
+
+ if (gpio_slice_acquired(gb->gpio))
+ goto out;
+ }
+
+ for (i = 0; i < gk->nbuttons; i++) {
gb = &gk->buttons[i];
val = gpio_get_value(gb->gpio);
@@ -59,14 +59,16 @@ static void gpio_key_poller(struct poller_struct *poller)
gb->debounce_start = get_time_ns();
input_report_key_event(&gk->input, gb->code, pressed);
- dev_dbg(gk->dev, "%s gpio(%d) as %d\n",
+ dev_dbg(gk->input.parent, "%s gpio(%d) as %d\n",
pressed ? "pressed" : "released", gb->gpio, gb->code);
gb->previous_state = val;
}
}
+out:
+ poller_call_async(&gk->poller, 10 * MSECOND, gpio_key_poller, gk);
}
-static int gpio_keys_probe_pdata(struct gpio_keys *gk, struct device_d *dev)
+static int gpio_keys_probe_pdata(struct gpio_keys *gk, struct device *dev)
{
struct gpio_keys_platform_data *pdata;
int i;
@@ -92,9 +94,9 @@ static int gpio_keys_probe_pdata(struct gpio_keys *gk, struct device_d *dev)
return 0;
}
-static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device_d *dev)
+static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device *dev)
{
- struct device_node *npkey, *np = dev->device_node;
+ struct device_node *npkey, *np = dev->of_node;
int i = 0, ret;
if (!IS_ENABLED(CONFIG_OFDEVICE) || !IS_ENABLED(CONFIG_OF_GPIO))
@@ -131,16 +133,14 @@ static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device_d *dev)
return 0;
}
-static int __init gpio_keys_probe(struct device_d *dev)
+static int __init gpio_keys_probe(struct device *dev)
{
int ret, i, gpio;
struct gpio_keys *gk;
gk = xzalloc(sizeof(*gk));
- gk->dev = dev;
-
- if (dev->device_node)
+ if (dev->of_node)
ret = gpio_keys_probe_dt(gk, dev);
else
ret = gpio_keys_probe_pdata(gk, dev);
@@ -159,16 +159,17 @@ static int __init gpio_keys_probe(struct device_d *dev)
gk->buttons[i].previous_state = gk->buttons[i].active_low;
}
- gk->poller.func = gpio_key_poller;
-
+ gk->input.parent = dev;
ret = input_device_register(&gk->input);
if (ret)
return ret;
- ret = poller_register(&gk->poller, dev_name(dev));
+ ret = poller_async_register(&gk->poller, dev_name(dev));
if (ret)
return ret;
+ poller_call_async(&gk->poller, 10 * MSECOND, gpio_key_poller, gk);
+
return 0;
}
@@ -176,8 +177,9 @@ static struct of_device_id key_gpio_of_ids[] = {
{ .compatible = "gpio-keys", },
{ }
};
+MODULE_DEVICE_TABLE(of, key_gpio_of_ids);
-static struct driver_d gpio_keys_driver = {
+static struct driver gpio_keys_driver = {
.name = "gpio_keys",
.probe = gpio_keys_probe,
.of_compatible = DRV_OF_COMPAT(key_gpio_of_ids),
diff --git a/drivers/input/imx_keypad.c b/drivers/input/imx_keypad.c
index 9fac770421..e57a884630 100644
--- a/drivers/input/imx_keypad.c
+++ b/drivers/input/imx_keypad.c
@@ -72,7 +72,6 @@
struct imx_keypad {
struct input_device input;
struct clk *clk;
- struct device_d *dev;
void __iomem *mmio_base;
struct poller_struct poller;
@@ -199,10 +198,6 @@ static void imx_keypad_fire_events(struct imx_keypad *keypad,
input_report_key_event(&keypad->input, keypad->keycodes[code],
matrix_volatile_state[col] & (1 << row));
-
- dev_dbg(keypad->dev, "Event code: %d, val: %d",
- keypad->keycodes[code],
- matrix_volatile_state[col] & (1 << row));
}
}
}
@@ -358,7 +353,7 @@ static void imx_keypad_inhibit(struct imx_keypad *keypad)
writew(0xff00, keypad->mmio_base + KPCR);
}
-static int __init imx_keypad_probe(struct device_d *dev)
+static int __init imx_keypad_probe(struct device *dev)
{
struct resource *iores;
struct imx_keypad *keypad;
@@ -367,7 +362,6 @@ static int __init imx_keypad_probe(struct device_d *dev)
keypad = xzalloc(sizeof(struct imx_keypad));
- keypad->dev = dev;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -410,6 +404,7 @@ static int __init imx_keypad_probe(struct device_d *dev)
if (ret)
return ret;
+ keypad->input.parent = dev;
ret = input_device_register(&keypad->input);
if (ret)
return ret;
@@ -421,8 +416,9 @@ static __maybe_unused struct of_device_id imx_keypad_dt_ids[] = {
{ .compatible = "fsl,imx21-kpp", },
{ }
};
+MODULE_DEVICE_TABLE(of, imx_keypad_dt_ids);
-static struct driver_d imx_keypad_driver = {
+static struct driver imx_keypad_driver = {
.name = "imx-kpp",
.probe = imx_keypad_probe,
.of_compatible = DRV_OF_COMPAT(imx_keypad_dt_ids),
diff --git a/drivers/input/input.c b/drivers/input/input.c
index deef5ddd22..1a47929351 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) "input: " fmt
+
#include <common.h>
#include <init.h>
#include <kfifo.h>
@@ -33,6 +35,15 @@ void input_report_key_event(struct input_device *idev, unsigned int code, int va
if (code > KEY_MAX)
return;
+ /*
+ * We don't use pr_debug here as we want to output the message
+ * to the log, even if CONFIG_COMPILE_LOGLEVEL < MSG_DEBUG and
+ * the DEBUG mcro wasn't defined for the file.
+ */
+ if (IS_ENABLED(CONFIG_INPUT_EVBUG))
+ pr_print(MSG_DEBUG, "Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
+ dev_name(idev->parent), EV_KEY, code, value);
+
if (value)
set_bit(code, idev->keys);
else
diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c
index 68dc5d2f06..70551d4a2b 100644
--- a/drivers/input/matrix-keymap.c
+++ b/drivers/input/matrix-keymap.c
@@ -3,25 +3,25 @@
#include <common.h>
#include <input/matrix_keypad.h>
-static int matrix_keypad_parse_of_keymap(struct device_d *dev,
+static int matrix_keypad_parse_of_keymap(struct device *dev,
unsigned int row_shift,
unsigned short *keymap)
{
unsigned int proplen, i, size;
const __be32 *prop;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const char *propname = "linux,keymap";
prop = of_get_property(np, propname, &proplen);
if (!prop) {
- dev_err(dev, "OF: %s property not defined in %s\n",
- propname, np->full_name);
+ dev_err(dev, "OF: %s property not defined in %pOF\n",
+ propname, np);
return -ENOENT;
}
if (proplen % sizeof(u32)) {
- dev_err(dev, "OF: Malformed keycode property %s in %s\n",
- propname, np->full_name);
+ dev_err(dev, "OF: Malformed keycode property %s in %pOF\n",
+ propname, np);
return -EINVAL;
}
@@ -55,13 +55,14 @@ static int matrix_keypad_parse_of_keymap(struct device_d *dev,
* an array of keycodes that is suitable for using in a standard matrix
* keyboard driver that uses row and col as indices.
*/
-int matrix_keypad_build_keymap(struct device_d *dev, const struct matrix_keymap_data *keymap_data,
- unsigned int row_shift,
- unsigned short *keymap)
+int matrix_keypad_build_keymap(struct device *dev,
+ const struct matrix_keymap_data *keymap_data,
+ unsigned int row_shift,
+ unsigned short *keymap)
{
int i;
- if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node)
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node)
return matrix_keypad_parse_of_keymap(dev, row_shift, keymap);
if (!keymap_data)
diff --git a/drivers/input/qt1070.c b/drivers/input/qt1070.c
index 120fec4841..c0fd85b03d 100644
--- a/drivers/input/qt1070.c
+++ b/drivers/input/qt1070.c
@@ -177,7 +177,7 @@ static int qt1070_getc(struct console_device *cdev)
return code;
}
-static int qt1070_pdata_init(struct device_d *dev, struct qt1070_data *data)
+static int qt1070_pdata_init(struct device *dev, struct qt1070_data *data)
{
struct qt1070_platform_data *pdata = dev->platform_data;
int ret;
@@ -207,7 +207,7 @@ err:
return ret;
}
-static int qt1070_probe(struct device_d *dev)
+static int qt1070_probe(struct device *dev)
{
struct console_device *cdev;
struct qt1070_data *data;
@@ -278,7 +278,7 @@ err:
return ret;
}
-static struct driver_d qt1070_driver = {
+static struct driver qt1070_driver = {
.name = "qt1070",
.probe = qt1070_probe,
};
diff --git a/drivers/input/twl6030_pwrbtn.c b/drivers/input/twl6030_pwrbtn.c
index 656316fc34..3e9e01f38b 100644
--- a/drivers/input/twl6030_pwrbtn.c
+++ b/drivers/input/twl6030_pwrbtn.c
@@ -57,7 +57,7 @@ static int twl6030_pwrbtn_getc(struct console_device *cdev)
return code;
}
-static int __init twl6030_pwrbtn_probe(struct device_d *dev)
+static int __init twl6030_pwrbtn_probe(struct device *dev)
{
struct twl6030_pwrbtn_internal_data *idata;
struct twl6030_pwrbtn_platform_data *pdata;
@@ -90,7 +90,7 @@ static int __init twl6030_pwrbtn_probe(struct device_d *dev)
return poller_register(&idata->poller, dev_name(dev));
}
-static struct driver_d twl6030_pwrbtn_driver = {
+static struct driver twl6030_pwrbtn_driver = {
.name = "twl6030_pwrbtn",
.probe = twl6030_pwrbtn_probe,
};
diff --git a/drivers/input/usb_kbd.c b/drivers/input/usb_kbd.c
index d950bd5039..2e75aabf3d 100644
--- a/drivers/input/usb_kbd.c
+++ b/drivers/input/usb_kbd.c
@@ -10,7 +10,7 @@
#include <init.h>
#include <clock.h>
#include <poller.h>
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
#include <string.h>
#include <dma.h>
#include <input/input.h>
@@ -188,6 +188,7 @@ static int usb_kbd_probe(struct usb_device *usbdev,
} else
dev_dbg(&usbdev->dev, "poll keyboard via int ep\n");
+ data->input.parent = &usbdev->dev;
ret = input_device_register(&data->input);
if (ret) {
dev_err(&usbdev->dev, "can't register input\n");
diff --git a/drivers/input/virtio_input.c b/drivers/input/virtio_input.c
index b5430886ab..655a905172 100644
--- a/drivers/input/virtio_input.c
+++ b/drivers/input/virtio_input.c
@@ -32,7 +32,7 @@ static void virtinput_queue_evtbuf(struct virtio_input *vi,
static int virtinput_recv_events(struct virtio_input *vi)
{
- struct device_d *dev = &vi->vdev->dev;
+ struct device *dev = &vi->vdev->dev;
struct virtio_input_event *event;
unsigned int len;
int i = 0;
@@ -216,6 +216,7 @@ static int virtinput_probe(struct virtio_device *vdev)
virtio_device_ready(vdev);
+ vi->idev.parent = &vdev->dev;
err = input_device_register(&vi->idev);
if (err)
goto err_input_register;
diff --git a/drivers/led/led-gpio.c b/drivers/led/led-gpio.c
index 1a5eda0371..c0d14256d3 100644
--- a/drivers/led/led-gpio.c
+++ b/drivers/led/led-gpio.c
@@ -192,20 +192,20 @@ void led_gpio_rgb_unregister(struct gpio_rgb_led *led)
#endif /* CONFIG_LED_GPIO_RGB */
#ifdef CONFIG_LED_GPIO_OF
-static int led_gpio_of_probe(struct device_d *dev)
+static int led_gpio_of_probe(struct device *dev)
{
struct device_node *child;
struct gpio_led *leds;
int num_leds;
int ret = 0, n = 0;
- num_leds = of_get_child_count(dev->device_node);
+ num_leds = of_get_child_count(dev->of_node);
if (num_leds <= 0)
return num_leds;
leds = xzalloc(num_leds * sizeof(struct gpio_led));
- for_each_child_of_node(dev->device_node, child) {
+ for_each_child_of_node(dev->of_node, child) {
struct gpio_led *gled = &leds[n];
const char *default_state;
enum of_gpio_flags flags;
@@ -214,10 +214,7 @@ static int led_gpio_of_probe(struct device_d *dev)
gpio = of_get_named_gpio_flags(child, "gpios", 0, &flags);
if (gpio < 0) {
- if (gpio != -EPROBE_DEFER)
- dev_err(dev, "failed to get gpio for %s: %d\n",
- child->full_name, gpio);
- ret = gpio;
+ ret = dev_err_probe(dev, gpio, "getting gpio for %pOF\n", child);
goto err;
}
@@ -257,8 +254,9 @@ static struct of_device_id led_gpio_of_ids[] = {
{ .compatible = "gpio-leds", },
{ }
};
+MODULE_DEVICE_TABLE(of, led_gpio_of_ids);
-static struct driver_d led_gpio_of_driver = {
+static struct driver led_gpio_of_driver = {
.name = "gpio-leds",
.probe = led_gpio_of_probe,
.of_compatible = DRV_OF_COMPAT(led_gpio_of_ids),
diff --git a/drivers/led/led-pca955x.c b/drivers/led/led-pca955x.c
index aa518fe738..cad4db3799 100644
--- a/drivers/led/led-pca955x.c
+++ b/drivers/led/led-pca955x.c
@@ -325,8 +325,9 @@ static const struct of_device_id of_pca955x_match[] = {
{ .compatible = "nxp,pca9553", .data = &pca9553_chipdef },
{},
};
+MODULE_DEVICE_TABLE(of, of_pca955x_match);
-static int led_pca955x_probe(struct device_d *dev)
+static int led_pca955x_probe(struct device *dev)
{
struct pca955x *pca955x;
struct pca955x_led *pca955x_led;
@@ -365,7 +366,7 @@ static int led_pca955x_probe(struct device_d *dev)
pca955x->client = client;
pca955x->chipdef = chip;
- pdata = led_pca955x_pdata_of_init(dev->device_node, pca955x);
+ pdata = led_pca955x_pdata_of_init(dev->of_node, pca955x);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
@@ -393,7 +394,7 @@ static int led_pca955x_probe(struct device_d *dev)
return err;
}
-static struct driver_d led_pca955x_driver = {
+static struct driver led_pca955x_driver = {
.name = "led-pca955x",
.probe = led_pca955x_probe,
.id_table = led_pca955x_id,
diff --git a/drivers/led/led-pwm.c b/drivers/led/led-pwm.c
index 071e219d73..6f4abf97c8 100644
--- a/drivers/led/led-pwm.c
+++ b/drivers/led/led-pwm.c
@@ -27,24 +27,24 @@ static void led_pwm_set(struct led *led, unsigned int brightness)
pwm_get_state(pwmled->pwm, &state);
- duty = state.period_ns * brightness;
+ duty = state.period * brightness;
do_div(duty, max);
if (pwmled->active_low)
- duty = state.period_ns - duty;
+ duty = state.period - duty;
- state.p_enable = true;
- state.duty_ns = duty;
+ state.enabled = true;
+ state.duty_cycle = duty;
pwm_apply_state(pwmled->pwm, &state);
}
-static int led_pwm_of_probe(struct device_d *dev)
+static int led_pwm_of_probe(struct device *dev)
{
struct device_node *child;
int ret;
- for_each_child_of_node(dev->device_node, child) {
+ for_each_child_of_node(dev->of_node, child) {
struct pwmled *pwmled;
struct pwm_device *pwm;
@@ -80,8 +80,9 @@ static struct of_device_id led_pwm_of_ids[] = {
{ .compatible = "pwm-leds", },
{ }
};
+MODULE_DEVICE_TABLE(of, led_pwm_of_ids);
-static struct driver_d led_pwm_of_driver = {
+static struct driver led_pwm_of_driver = {
.name = "pwm-leds",
.probe = led_pwm_of_probe,
.of_compatible = DRV_OF_COMPAT(led_pwm_of_ids),
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
new file mode 100644
index 0000000000..80a501d235
--- /dev/null
+++ b/drivers/mailbox/Kconfig
@@ -0,0 +1,22 @@
+menuconfig MAILBOX
+ bool "Mailbox Hardware Support"
+ help
+ Mailbox is a framework to control hardware communication between
+ on-chip processors through queued messages and interrupt driven
+ signals. Say Y if your platform supports hardware mailboxes.
+
+if MAILBOX
+
+config TI_MESSAGE_MANAGER
+ tristate "Texas Instruments Message Manager Driver"
+ depends on ARCH_K3
+ default y
+ help
+ An implementation of Message Manager slave driver for Keystone
+ and K3 architecture SoCs from Texas Instruments. Message Manager
+ is a communication entity found on few of Texas Instrument's keystone
+ and K3 architecture SoCs. These may be used for communication between
+ multiple processors within the SoC. Select this driver if your
+ platform has support for the hardware block.
+
+endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
new file mode 100644
index 0000000000..8ec2af96f4
--- /dev/null
+++ b/drivers/mailbox/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MAILBOX) += mailbox.o
+obj-$(CONFIG_TI_MESSAGE_MANAGER) += ti-msgmgr.o
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
new file mode 100644
index 0000000000..edf9481aca
--- /dev/null
+++ b/drivers/mailbox/mailbox.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ */
+
+#define LOG_CATEGORY UCLASS_MAILBOX
+
+#include <common.h>
+#include <mailbox.h>
+#include <deep-probe.h>
+
+int mbox_send(struct mbox_chan *chan, const void *data)
+{
+ const struct mbox_chan_ops *ops = chan->mbox->ops;
+
+ return ops->send(chan, data);
+}
+
+int mbox_recv(struct mbox_chan *chan, void *data, unsigned long timeout_us)
+{
+ const struct mbox_chan_ops *ops = chan->mbox->ops;
+ u64 start;
+ int ret;
+
+ start = get_time_ns();
+
+ for (;;) {
+ ret = ops->recv(chan, data);
+ if (ret != -ENODATA)
+ return ret;
+ if (is_timeout(start, timeout_us * 1000))
+ return -ETIMEDOUT;
+ }
+}
+
+static LIST_HEAD(mbox_cons);
+
+int mbox_controller_register(struct mbox_controller *mbox)
+{
+ list_add_tail(&mbox->node, &mbox_cons);
+
+ return 0;
+}
+
+
+struct mbox_chan *mbox_request_channel(struct device *dev, int index)
+{
+ struct of_phandle_args spec;
+ struct mbox_controller *mbox;
+ struct mbox_chan *chan = ERR_PTR(-ENODEV);
+
+ if (of_parse_phandle_with_args(dev->of_node, "mboxes",
+ "#mbox-cells", index, &spec)) {
+ return ERR_PTR(-ENODEV);
+ }
+
+ of_device_ensure_probed(spec.np);
+
+ list_for_each_entry(mbox, &mbox_cons, node) {
+ if (mbox->dev->of_node != spec.np)
+ continue;
+
+ chan = mbox->of_xlate(mbox, &spec);
+ if (!IS_ERR(chan))
+ break;
+ }
+
+ return chan;
+}
+
+struct mbox_chan *mbox_request_channel_byname(struct device *dev, const char *name)
+{
+ struct device_node *np = dev->of_node;
+ struct property *prop;
+ const char *mbox_name;
+ int index = 0;
+
+ if (!np)
+ return ERR_PTR(-EINVAL);
+
+ if (!of_get_property(np, "mbox-names", NULL)) {
+ return ERR_PTR(-EINVAL);
+ }
+
+ of_property_for_each_string(np, "mbox-names", prop, mbox_name) {
+ if (!strncmp(name, mbox_name, strlen(name)))
+ return mbox_request_channel(dev, index);
+ index++;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c
new file mode 100644
index 0000000000..58ec8697fc
--- /dev/null
+++ b/drivers/mailbox/ti-msgmgr.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments' K3 Secure proxy Driver
+ *
+ * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla <lokeshvutla@ti.com>
+ */
+
+#include <common.h>
+#include <mailbox.h>
+#include <soc/ti/k3-sec-proxy.h>
+
+/* SEC PROXY RT THREAD STATUS */
+#define RT_THREAD_STATUS 0x0
+#define RT_THREAD_THRESHOLD 0x4
+#define RT_THREAD_STATUS_ERROR_SHIFT 31
+#define RT_THREAD_STATUS_ERROR_MASK BIT(31)
+#define RT_THREAD_STATUS_CUR_CNT_SHIFT 0
+#define RT_THREAD_STATUS_CUR_CNT_MASK GENMASK(7, 0)
+
+/* SEC PROXY SCFG THREAD CTRL */
+#define SCFG_THREAD_CTRL 0x1000
+#define SCFG_THREAD_CTRL_DIR_SHIFT 31
+#define SCFG_THREAD_CTRL_DIR_MASK BIT(31)
+
+#define SEC_PROXY_THREAD(base, x) ((base) + (0x1000 * (x)))
+#define THREAD_IS_RX 1
+#define THREAD_IS_TX 0
+
+/**
+ * struct k3_sec_proxy_desc - Description of secure proxy integration.
+ * @thread_count: Number of Threads.
+ * @max_msg_size: Message size in bytes.
+ * @data_start_offset: Offset of the First data register of the thread
+ * @data_end_offset: Offset of the Last data register of the thread
+ * @valid_threads: List of Valid threads that the processor can access
+ * @num_valid_threads: Number of valid threads.
+ */
+struct k3_sec_proxy_desc {
+ u16 thread_count;
+ u16 max_msg_size;
+ u16 data_start_offset;
+ u16 data_end_offset;
+ const u32 *valid_threads;
+ u32 num_valid_threads;
+};
+
+/**
+ * struct k3_sec_proxy_thread - Description of a secure proxy Thread
+ * @id: Thread ID
+ * @data: Thread Data path region for target
+ * @scfg: Secure Config Region for Thread
+ * @rt: RealTime Region for Thread
+ * @rx_buf: Receive buffer data, max message size.
+ */
+struct k3_sec_proxy_thread {
+ u32 id;
+ void __iomem *data;
+ void __iomem *scfg;
+ void __iomem *rt;
+ u32 *rx_buf;
+};
+
+/**
+ * struct k3_sec_proxy_mbox - Description of a Secure Proxy Instance
+ * @chan: Mailbox Channel
+ * @desc: Description of the SoC integration
+ * @chans: Array for valid thread instances
+ * @target_data: Secure Proxy region for Target Data
+ * @scfg: Secure Proxy Region for Secure configuration.
+ * @rt: Secure proxy Region for Real Time Region.
+ */
+struct k3_sec_proxy_mbox {
+ struct device *dev;
+ struct mbox_controller mbox;
+ const struct k3_sec_proxy_desc *desc;
+ struct k3_sec_proxy_thread *k3_chans;
+ void __iomem *target_data;
+ void __iomem *scfg;
+ void __iomem *rt;
+};
+
+static inline u32 sp_readl(void __iomem *addr, unsigned int offset)
+{
+ return readl(addr + offset);
+}
+
+static inline void sp_writel(void __iomem *addr, unsigned int offset, u32 data)
+{
+ writel(data, addr + offset);
+}
+
+/**
+ * k3_sec_proxy_request() - Request for mailbox channel
+ * @chan: Channel Pointer
+ */
+static int k3_sec_proxy_request(struct mbox_chan *chan)
+{
+ dev_dbg(chan->dev, "%s(chan=%p)\n", __func__, chan);
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_free() - Free the mailbox channel
+ * @chan: Channel Pointer
+ */
+static int k3_sec_proxy_free(struct mbox_chan *chan)
+{
+ dev_dbg(chan->dev, "%s(chan=%p)\n", __func__, chan);
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_verify_thread() - Verify thread status before
+ * sending/receiving data.
+ * @spt: pointer to secure proxy thread description
+ * @dir: Direction of the thread
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static inline int k3_sec_proxy_verify_thread(struct mbox_chan *chan, u8 dir)
+{
+ struct k3_sec_proxy_thread *spt = chan->con_priv;
+
+ /* Check for any errors already available */
+ if (sp_readl(spt->rt, RT_THREAD_STATUS) &
+ RT_THREAD_STATUS_ERROR_MASK) {
+ dev_err(chan->dev, "%s: Thread %d is corrupted, cannot send data.\n",
+ __func__, spt->id);
+ return -EINVAL;
+ }
+
+ /* Make sure thread is configured for right direction */
+ if ((sp_readl(spt->scfg, SCFG_THREAD_CTRL)
+ & SCFG_THREAD_CTRL_DIR_MASK) >> SCFG_THREAD_CTRL_DIR_SHIFT != dir) {
+ if (dir)
+ dev_err(chan->dev, "%s: Trying to receive data on tx Thread %d\n",
+ __func__, spt->id);
+ else
+ dev_err(chan->dev, "%s: Trying to send data on rx Thread %d\n",
+ __func__, spt->id);
+ return -EINVAL;
+ }
+
+ /* Check the message queue before sending/receiving data */
+ if (!(sp_readl(spt->rt, RT_THREAD_STATUS) &
+ RT_THREAD_STATUS_CUR_CNT_MASK))
+ return -ENODATA;
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_send() - Send data via mailbox channel
+ * @chan: Channel Pointer
+ * @data: Pointer to k3_sec_proxy_msg
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int k3_sec_proxy_send(struct mbox_chan *chan, const void *data)
+{
+ const struct k3_sec_proxy_msg *msg = (struct k3_sec_proxy_msg *)data;
+ struct mbox_controller *mbox = chan->mbox;
+ struct k3_sec_proxy_mbox *spm = container_of(mbox, struct k3_sec_proxy_mbox, mbox);
+ struct k3_sec_proxy_thread *spt = chan->con_priv;
+ int num_words, trail_bytes, ret;
+ void __iomem *data_reg;
+ u32 *word_data;
+
+ dev_dbg(chan->dev, "%s(chan=%p, data=%p)\n", __func__, chan, data);
+
+ ret = k3_sec_proxy_verify_thread(chan, THREAD_IS_TX);
+ if (ret) {
+ dev_err(chan->dev,
+ "%s: Thread%d verification failed. ret = %d\n",
+ __func__, spt->id, ret);
+ return ret;
+ }
+
+ /* Check the message size. */
+ if (msg->len > spm->desc->max_msg_size)
+ return -EINVAL;
+
+ /* Send the message */
+ data_reg = spt->data + spm->desc->data_start_offset;
+ for (num_words = msg->len / sizeof(u32), word_data = (u32 *)msg->buf;
+ num_words;
+ num_words--, data_reg += sizeof(u32), word_data++)
+ writel(*word_data, data_reg);
+
+ trail_bytes = msg->len % sizeof(u32);
+ if (trail_bytes) {
+ u32 data_trail = *word_data;
+
+ /* Ensure all unused data is 0 */
+ data_trail &= 0xFFFFFFFF >> (8 * (sizeof(u32) - trail_bytes));
+ writel(data_trail, data_reg);
+ data_reg++;
+ }
+
+ /*
+ * 'data_reg' indicates next register to write. If we did not already
+ * write on tx complete reg(last reg), we must do so for transmit
+ */
+ if (data_reg <= (spt->data + spm->desc->data_end_offset))
+ sp_writel(spt->data, spm->desc->data_end_offset, 0);
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_recv() - Receive data via mailbox channel
+ * @chan: Channel Pointer
+ * @data: Pointer to k3_sec_proxy_msg
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int k3_sec_proxy_recv(struct mbox_chan *chan, void *data)
+{
+ struct mbox_controller *mbox = chan->mbox;
+ struct k3_sec_proxy_mbox *spm = container_of(mbox, struct k3_sec_proxy_mbox, mbox);
+ struct k3_sec_proxy_thread *spt = chan->con_priv;
+ struct k3_sec_proxy_msg *msg = data;
+ void __iomem *data_reg;
+ int num_words, ret;
+ u32 *word_data;
+
+ dev_dbg(chan->dev, "%s(chan=%p, data=%p)\n", __func__, chan, data);
+
+ ret = k3_sec_proxy_verify_thread(chan, THREAD_IS_RX);
+ if (ret)
+ return ret;
+
+ msg->len = spm->desc->max_msg_size;
+ msg->buf = spt->rx_buf;
+ data_reg = spt->data + spm->desc->data_start_offset;
+ word_data = spt->rx_buf;
+ for (num_words = spm->desc->max_msg_size / sizeof(u32);
+ num_words;
+ num_words--, data_reg += sizeof(u32), word_data++)
+ *word_data = readl(data_reg);
+
+ return 0;
+}
+
+static struct mbox_chan *k3_sec_proxy_of_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *p)
+{
+ struct k3_sec_proxy_mbox *spm = container_of(mbox, struct k3_sec_proxy_mbox, mbox);
+ int ncells = 1;
+ int req_pid;
+ int i;
+
+ if (p->args_count != ncells) {
+ dev_err(mbox->dev, "Invalid arguments in dt[%d]. Must be %d\n",
+ p->args_count, ncells);
+ return ERR_PTR(-EINVAL);
+ }
+
+ req_pid = p->args[0];
+
+ for (i = 0; i < spm->desc->num_valid_threads; i++) {
+ struct mbox_chan *chan;
+
+ if (spm->k3_chans[i].id == req_pid) {
+ chan = &spm->mbox.chans[i];
+ return chan;
+ }
+ }
+
+ return NULL;
+}
+
+static struct mbox_chan_ops k3_sec_proxy_mbox_ops = {
+ .request = k3_sec_proxy_request,
+ .rfree = k3_sec_proxy_free,
+ .send = k3_sec_proxy_send,
+ .recv = k3_sec_proxy_recv,
+};
+
+/**
+ * k3_sec_proxy_thread_setup - Initialize the parameters for all valid threads
+ * @spm: Mailbox instance for which threads needs to be initialized
+ *
+ * Return: 0 if all went ok, else corresponding error message
+ */
+static int k3_sec_proxy_thread_setup(struct k3_sec_proxy_mbox *spm)
+{
+ struct k3_sec_proxy_thread *spt;
+ int i, ind;
+
+ for (i = 0; i < spm->desc->num_valid_threads; i++) {
+ spt = &spm->k3_chans[i];
+ ind = spm->desc->valid_threads[i];
+ spt->id = ind;
+ spt->data = (void *)SEC_PROXY_THREAD(spm->target_data, ind);
+ spt->scfg = (void *)SEC_PROXY_THREAD(spm->scfg, ind);
+ spt->rt = (void *)SEC_PROXY_THREAD(spm->rt, ind);
+ spt->rx_buf = xzalloc(spm->desc->max_msg_size);
+ }
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_probe() - Basic probe
+ * @dev: corresponding mailbox device
+ *
+ * Return: 0 if all went ok, else corresponding error message
+ */
+static int k3_sec_proxy_probe(struct device *dev)
+{
+ struct k3_sec_proxy_mbox *spm;
+ struct mbox_controller *mbox;
+ struct resource *res;
+ const void *data;
+ int i, ret;
+
+ spm = xzalloc(sizeof(*spm));
+
+ spm->dev = dev;
+
+ res = dev_request_mem_resource_by_name(spm->dev, "target_data");
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ spm->target_data = IOMEM(res->start);
+
+ res = dev_request_mem_resource_by_name(spm->dev, "scfg");
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ spm->scfg = IOMEM(res->start);
+
+ res = dev_request_mem_resource_by_name(spm->dev, "rt");
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ spm->rt = IOMEM(res->start);
+
+ ret = dev_get_drvdata(dev, &data);
+ if (ret)
+ return ret;
+
+ spm->desc = data;
+ spm->k3_chans = xzalloc(spm->desc->num_valid_threads * sizeof(*spm->k3_chans));
+
+ ret = k3_sec_proxy_thread_setup(spm);
+ if (ret) {
+ dev_err(dev, "secure proxy thread setup failed\n");
+ return ret;
+ }
+
+ mbox = &spm->mbox;
+ mbox->dev = dev;
+ mbox->ops = &k3_sec_proxy_mbox_ops;
+ mbox->of_xlate = k3_sec_proxy_of_xlate;
+ mbox->chans = xzalloc(spm->desc->num_valid_threads * sizeof(*mbox->chans));
+ mbox->num_chans = spm->desc->num_valid_threads;
+
+ for (i = 0; i < spm->desc->num_valid_threads; i++) {
+ mbox->chans[i].dev = dev;
+ mbox->chans[i].mbox = mbox;
+ mbox->chans[i].con_priv = &spm->k3_chans[i];
+ }
+
+ ret = mbox_controller_register(mbox);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const u32 am6x_valid_threads[] = { 0, 1, 4, 5, 6, 7, 8, 9, 11, 12, 13, 20, 21, 22, 23 };
+
+static const struct k3_sec_proxy_desc am654_desc = {
+ .thread_count = 90,
+ .max_msg_size = 60,
+ .data_start_offset = 0x4,
+ .data_end_offset = 0x3C,
+ .valid_threads = am6x_valid_threads,
+ .num_valid_threads = ARRAY_SIZE(am6x_valid_threads),
+};
+
+static const struct of_device_id k3_sec_proxy_ids[] = {
+ {
+ .compatible = "ti,am654-secure-proxy",
+ .data = &am654_desc
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, k3_sec_proxy_ids);
+
+static struct driver k3_sec_proxy_driver = {
+ .name = "k3-secure-proxy",
+ .probe = k3_sec_proxy_probe,
+ .of_compatible = DRV_OF_COMPAT(k3_sec_proxy_ids),
+};
+core_platform_driver(k3_sec_proxy_driver);
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 21d53c0c3f..1e8c85643b 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -12,6 +12,13 @@ if MCI
comment "--- Feature list ---"
+config MCI_TUNING
+ bool "EXPERIMENTAL - support MMC tuning for higher speeds"
+ help
+ Say 'y' here if supporting drivers should do tuning to support
+ higher clock speeds than 52 MHz SDR. MMC only; SD-Card max
+ frequency is 50MHz SDR at present.
+
config MCI_STARTUP
bool "Force probe on system start"
help
@@ -58,6 +65,14 @@ config MCI_MMC_GPP_PARTITIONS
comment "--- MCI host drivers ---"
+config MCI_DWC_MSHC
+ bool "Synopsys DesignWare Cores MSHC"
+ depends on HAS_DMA
+ select MCI_SDHCI
+ help
+ This selects support for the Synopsys DesignWare Mobile Storage Host Controller
+ block (DWC_mshc), this provides host support for SD/eMMC interfaces, in SDMA mode.
+
config MCI_DW
bool "Synopsys DesignWare Memory Card Interface"
depends on HAS_DMA
@@ -87,13 +102,6 @@ config MCI_ROCKCHIP_DWCMSHC
Enable this entry to add support for a Rockchip derivation of the
DWCMSHC controller found on some Rockchip SoCs like the RK3568.
-config MCI_S3C
- bool "S3C"
- depends on ARCH_S3C24xx
- help
- Enable this entry to add support to read and write SD cards on a
- Samsung S3C24xx based system.
-
config MCI_BCM283X
bool "MCI support for BCM283X"
depends on ARCH_BCM283X || COMPILE_TEST
@@ -184,6 +192,14 @@ config MCI_ARASAN
Enable this to support SD and MMC card read/write on systems with
the Arasan SD3.0 / SDIO3.0 / eMMC4.51 host controller.
+config MCI_AM654
+ bool "Support for the SDHCI Controller in TI's AM654 SOCs"
+ select MCI_SDHCI
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ support present in TI's AM654 SOCs. The controller supports
+ SD/MMC/SDIO devices.
+
config MCI_SPI
bool "MMC/SD over SPI"
select CRC7
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 55ec97b3fe..d8d7818a48 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MCI) += mci-core.o
+obj-$(CONFIG_MCI_AM654) += am654-sdhci.o
obj-$(CONFIG_MCI_ARASAN) += arasan-sdhci.o
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o atmel_mci_common.o
obj-$(CONFIG_MCI_ATMEL_SDHCI) += atmel-sdhci.o atmel-sdhci-common.o
@@ -14,11 +15,11 @@ pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o imx-esdhc-common.o
obj-$(CONFIG_MCI_MXS) += mxs.o
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_MCI_PXA) += pxamci.o
-obj-$(CONFIG_MCI_S3C) += s3c.o
obj-$(CONFIG_MCI_ROCKCHIP_DWCMSHC) += rockchip-dwcmshc-sdhci.o
obj-$(CONFIG_MCI_TEGRA) += tegra-sdmmc.o
obj-$(CONFIG_MCI_SPI) += mci_spi.o
obj-$(CONFIG_MCI_DW) += dw_mmc.o
+obj-$(CONFIG_MCI_DWC_MSHC) += dwcmshc-sdhci.o
obj-$(CONFIG_MCI_MMCI) += mmci.o
obj-$(CONFIG_MCI_STM32_SDMMC2) += stm32_sdmmc2.o
obj-pbl-$(CONFIG_MCI_SDHCI) += sdhci.o
diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
new file mode 100644
index 0000000000..391b65591c
--- /dev/null
+++ b/drivers/mci/am654-sdhci.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Texas Instruments' K3 SD Host Controller Interface
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <mci.h>
+#include <clock.h>
+#include <errno.h>
+#include <io.h>
+#include <regmap.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include "sdhci.h"
+
+/* CTL_CFG Registers */
+#define CTL_CFG_2 0x14
+
+#define SLOTTYPE_MASK GENMASK(31, 30)
+#define SLOTTYPE_EMBEDDED BIT(30)
+
+/* PHY Registers */
+#define PHY_CTRL1 0x100
+#define PHY_CTRL2 0x104
+#define PHY_CTRL3 0x108
+#define PHY_CTRL4 0x10C
+#define PHY_CTRL5 0x110
+#define PHY_CTRL6 0x114
+#define PHY_STAT1 0x130
+#define PHY_STAT2 0x134
+
+#define IOMUX_ENABLE_SHIFT 31
+#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
+#define OTAPDLYENA_SHIFT 20
+#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
+#define OTAPDLYSEL_SHIFT 12
+#define OTAPDLYSEL_MASK GENMASK(15, 12)
+#define STRBSEL_SHIFT 24
+#define STRBSEL_4BIT_MASK GENMASK(27, 24)
+#define STRBSEL_8BIT_MASK GENMASK(31, 24)
+#define SEL50_SHIFT 8
+#define SEL50_MASK BIT(SEL50_SHIFT)
+#define SEL100_SHIFT 9
+#define SEL100_MASK BIT(SEL100_SHIFT)
+#define FREQSEL_SHIFT 8
+#define FREQSEL_MASK GENMASK(10, 8)
+#define CLKBUFSEL_SHIFT 0
+#define CLKBUFSEL_MASK GENMASK(2, 0)
+#define DLL_TRIM_ICP_SHIFT 4
+#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
+#define DR_TY_SHIFT 20
+#define DR_TY_MASK GENMASK(22, 20)
+#define ENDLL_SHIFT 1
+#define ENDLL_MASK BIT(ENDLL_SHIFT)
+#define DLLRDY_SHIFT 0
+#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
+#define PDB_SHIFT 0
+#define PDB_MASK BIT(PDB_SHIFT)
+#define CALDONE_SHIFT 1
+#define CALDONE_MASK BIT(CALDONE_SHIFT)
+#define RETRIM_SHIFT 17
+#define RETRIM_MASK BIT(RETRIM_SHIFT)
+#define SELDLYTXCLK_SHIFT 17
+#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
+#define SELDLYRXCLK_SHIFT 16
+#define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT)
+#define ITAPDLYSEL_SHIFT 0
+#define ITAPDLYSEL_MASK GENMASK(4, 0)
+#define ITAPDLYENA_SHIFT 8
+#define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT)
+#define ITAPCHGWIN_SHIFT 9
+#define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT)
+
+#define DRIVER_STRENGTH_50_OHM 0x0
+#define DRIVER_STRENGTH_33_OHM 0x1
+#define DRIVER_STRENGTH_66_OHM 0x2
+#define DRIVER_STRENGTH_100_OHM 0x3
+#define DRIVER_STRENGTH_40_OHM 0x4
+
+#define AM654_SDHCI_MIN_FREQ 400000
+#define CLOCK_TOO_SLOW_HZ 50000000
+
+#define MMC_CAP2_HS200 0
+#define MMC_CAP2_HS400 0
+#define MMC_CAP_UHS_SDR104 0
+#define MMC_CAP_UHS_SDR12 0
+#define MMC_CAP_UHS_DDR50 0
+#define MMC_CAP_UHS_SDR25 0
+#define MMC_CAP_UHS_SDR50 0
+
+struct timing_data {
+ const char *otap_binding;
+ const char *itap_binding;
+ u32 capability;
+};
+
+static const struct timing_data td[] = {
+ [MMC_TIMING_LEGACY] = {
+ "ti,otap-del-sel-legacy",
+ "ti,itap-del-sel-legacy",
+ 0
+ },
+ [MMC_TIMING_MMC_HS] = {
+ "ti,otap-del-sel-mmc-hs",
+ "ti,itap-del-sel-mms-hs",
+ MMC_CAP_MMC_HIGHSPEED
+ },
+ [MMC_TIMING_SD_HS] = {
+ "ti,otap-del-sel-sd-hs",
+ "ti,itap-del-sel-sd-hs",
+ MMC_CAP_SD_HIGHSPEED
+ },
+ [MMC_TIMING_UHS_SDR12] = {
+ "ti,otap-del-sel-sdr12",
+ "ti,itap-del-sel-sdr12",
+ MMC_CAP_UHS_SDR12
+ },
+ [MMC_TIMING_UHS_SDR25] = {
+ "ti,otap-del-sel-sdr25",
+ "ti,itap-del-sel-sdr25",
+ MMC_CAP_UHS_SDR25
+ },
+ [MMC_TIMING_UHS_SDR50] = {
+ "ti,otap-del-sel-sdr50",
+ NULL,
+ MMC_CAP_UHS_SDR50
+ },
+ [MMC_TIMING_UHS_SDR104] = {
+ "ti,otap-del-sel-sdr104",
+ NULL,
+ MMC_CAP_UHS_SDR104
+ },
+ [MMC_TIMING_UHS_DDR50] = {
+ "ti,otap-del-sel-ddr50",
+ NULL,
+ MMC_CAP_UHS_DDR50
+ },
+ [MMC_TIMING_MMC_DDR52] = {
+ "ti,otap-del-sel-ddr52",
+ "ti,itap-del-sel-ddr52",
+ MMC_CAP_DDR
+ },
+ [MMC_TIMING_MMC_HS200] = {
+ "ti,otap-del-sel-hs200",
+ NULL,
+ MMC_CAP2_HS200
+ },
+ [MMC_TIMING_MMC_HS400] = {
+ "ti,otap-del-sel-hs400",
+ NULL,
+ MMC_CAP2_HS400
+ },
+};
+
+struct am654_sdhci_plat {
+ struct sdhci sdhci;
+ struct mci_host mci;
+ struct clk *clk;
+ struct clk *clk_ahb;
+ const struct am654_driver_data *soc_data;
+ bool fails_without_test_cd;
+
+ struct regmap *base;
+ bool non_removable;
+ u32 otap_del_sel[ARRAY_SIZE(td)];
+ u32 itap_del_sel[ARRAY_SIZE(td)];
+ u32 trm_icp;
+ u32 drv_strength;
+ u32 strb_sel;
+ u32 clkbuf_sel;
+#define DLL_PRESENT BIT(0)
+#define IOMUX_PRESENT BIT(1)
+#define FREQSEL_2_BIT BIT(2)
+#define STRBSEL_4_BIT BIT(3)
+#define DLL_CALIB BIT(4)
+};
+
+struct am654_driver_data {
+ int (*set_ios_post)(struct am654_sdhci_plat *plat, struct mci_ios *ios);
+ u32 flags;
+};
+
+static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
+ unsigned int speed)
+{
+ int sel50, sel100, freqsel;
+ u32 mask, val;
+ int ret;
+
+ /* Disable delay chain mode */
+ regmap_update_bits(plat->base, PHY_CTRL5,
+ SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
+ if (plat->soc_data->flags & FREQSEL_2_BIT) {
+ switch (speed) {
+ case 200000000:
+ sel50 = 0;
+ sel100 = 0;
+ break;
+ case 100000000:
+ sel50 = 0;
+ sel100 = 1;
+ break;
+ default:
+ sel50 = 1;
+ sel100 = 0;
+ }
+
+ /* Configure PHY DLL frequency */
+ mask = SEL50_MASK | SEL100_MASK;
+ val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+ } else {
+ switch (speed) {
+ case 200000000:
+ freqsel = 0x0;
+ break;
+ default:
+ freqsel = 0x4;
+ }
+ regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+ freqsel << FREQSEL_SHIFT);
+ }
+
+ /* Configure DLL TRIM */
+ mask = DLL_TRIM_ICP_MASK;
+ val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+ /* Configure DLL driver strength */
+ mask |= DR_TY_MASK;
+ val |= plat->drv_strength << DR_TY_SHIFT;
+ regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
+
+ /* Enable DLL */
+ regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
+ 0x1 << ENDLL_SHIFT);
+ /*
+ * Poll for DLL ready. Use a one second timeout.
+ * Works in all experiments done so far
+ */
+ ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
+ val & DLLRDY_MASK, 1000000);
+
+ return ret;
+}
+
+static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
+ u32 itapdly)
+{
+ /* Set ITAPCHGWIN before writing to ITAPDLY */
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+ 1 << ITAPCHGWIN_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+ itapdly << ITAPDLYSEL_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
+ int mode)
+{
+ u32 mask, val;
+
+ val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+ mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+ regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+
+ am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
+}
+
+static int am654_sdhci_set_ios_post(struct am654_sdhci_plat *plat, struct mci_ios *ios)
+{
+ unsigned int speed = ios->clock;
+ int mode = ios->timing;
+ u32 otap_del_sel;
+ u32 mask, val;
+ int ret;
+
+ /* Reset SD Clock Enable */
+ val = sdhci_read16(&plat->sdhci, SDHCI_CLOCK_CONTROL);
+ val &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_write16(&plat->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+ regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, 0);
+
+ /* restart clock */
+ sdhci_set_clock(&plat->sdhci, speed, clk_get_rate(plat->clk));
+
+ /* switch phy back on */
+ otap_del_sel = plat->otap_del_sel[mode];
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ val = (1 << OTAPDLYENA_SHIFT) |
+ (otap_del_sel << OTAPDLYSEL_SHIFT);
+
+ /* Write to STRBSEL for HS400 speed mode */
+ if (mode == MMC_TIMING_MMC_HS400) {
+ if (plat->soc_data->flags & STRBSEL_4_BIT)
+ mask |= STRBSEL_4BIT_MASK;
+ else
+ mask |= STRBSEL_8BIT_MASK;
+
+ val |= plat->strb_sel << STRBSEL_SHIFT;
+ }
+
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+ if (mode > MMC_TIMING_UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
+ ret = am654_sdhci_setup_dll(plat, speed);
+ if (ret)
+ return ret;
+ } else {
+ am654_sdhci_setup_delay_chain(plat, mode);
+ }
+
+ regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK,
+ plat->clkbuf_sel);
+
+ return 0;
+}
+
+static int am654_sdhci_init(struct mci_host *mci, struct device *dev)
+{
+ struct am654_sdhci_plat *plat = container_of(mci, struct am654_sdhci_plat, mci);
+ u32 ctl_cfg_2 = 0;
+ u32 mask, val;
+ int ret;
+
+ ret = sdhci_reset(&plat->sdhci, SDHCI_RESET_ALL);
+ if (ret)
+ return ret;
+
+ if (plat->fails_without_test_cd) {
+ val = sdhci_read8(&plat->sdhci, SDHCI_HOST_CONTROL);
+ val |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
+ sdhci_write8(&plat->sdhci, SDHCI_HOST_CONTROL, val);
+ }
+
+ sdhci_write8(&plat->sdhci, SDHCI_POWER_CONTROL,
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ sdhci_write32(&plat->sdhci, SDHCI_INT_ENABLE, ~0);
+ sdhci_write32(&plat->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+
+ /* Reset OTAP to default value */
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, 0x0);
+
+ if (plat->soc_data->flags & DLL_CALIB) {
+ regmap_read(plat->base, PHY_STAT1, &val);
+ if (~val & CALDONE_MASK) {
+ /* Calibrate IO lines */
+ regmap_update_bits(plat->base, PHY_CTRL1, PDB_MASK,
+ PDB_MASK);
+ ret = regmap_read_poll_timeout(plat->base, PHY_STAT1,
+ val, val & CALDONE_MASK,
+ 20);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* Enable pins by setting IO mux to 0 */
+ if (plat->soc_data->flags & IOMUX_PRESENT)
+ regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0);
+
+ /* Set slot type based on SD or eMMC */
+ if (plat->non_removable)
+ ctl_cfg_2 = SLOTTYPE_EMBEDDED;
+
+ regmap_update_bits(plat->base, CTL_CFG_2, SLOTTYPE_MASK, ctl_cfg_2);
+
+ return 0;
+}
+
+const struct am654_driver_data am654_drv_data = {
+ .flags = DLL_PRESENT | IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data am654_sr1_drv_data = {
+ .flags = IOMUX_PRESENT | FREQSEL_2_BIT | DLL_PRESENT | DLL_CALIB |
+ STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data j721e_8bit_drv_data = {
+ .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static int j721e_4bit_sdhci_set_ios_post(struct am654_sdhci_plat *plat, struct mci_ios *ios)
+{
+ u32 otap_del_sel, mask, val;
+
+ otap_del_sel = plat->otap_del_sel[ios->timing];
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ val = (1 << OTAPDLYENA_SHIFT) | (otap_del_sel << OTAPDLYSEL_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+ regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK,
+ plat->clkbuf_sel);
+
+ return 0;
+}
+
+const struct am654_driver_data j721e_4bit_drv_data = {
+ .set_ios_post = &j721e_4bit_sdhci_set_ios_post,
+ .flags = IOMUX_PRESENT,
+};
+
+static const struct am654_driver_data sdhci_am64_8bit_drvdata = {
+ .set_ios_post = &am654_sdhci_set_ios_post,
+ .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static const struct am654_driver_data sdhci_am64_4bit_drvdata = {
+ .set_ios_post = &j721e_4bit_sdhci_set_ios_post,
+ .flags = IOMUX_PRESENT,
+};
+
+static int sdhci_am654_get_otap_delay(struct am654_sdhci_plat *plat)
+{
+ struct device *dev = plat->mci.hw_dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+ int i;
+
+ /* ti,otap-del-sel-legacy is mandatory */
+ ret = of_property_read_u32(np, "ti,otap-del-sel-legacy",
+ &plat->otap_del_sel[0]);
+ if (ret)
+ return ret;
+ /*
+ * Remove the corresponding capability if an otap-del-sel
+ * value is not found
+ */
+ for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) {
+ ret = of_property_read_u32(np, td[i].otap_binding,
+ &plat->otap_del_sel[i]);
+ if (ret) {
+ dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
+ /*
+ * Remove the corresponding capability
+ * if an otap-del-sel value is not found
+ */
+ plat->mci.host_caps &= ~td[i].capability;
+ }
+
+ if (td[i].itap_binding)
+ of_property_read_u32(np, td[i].itap_binding,
+ &plat->itap_del_sel[i]);
+ }
+
+ return 0;
+}
+
+static void print_error(struct am654_sdhci_plat *host, int cmdidx)
+{
+ dev_dbg(host->mci.hw_dev,
+ "error while transfering data for command %d\n", cmdidx);
+ dev_dbg(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
+}
+
+static int am654_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct am654_sdhci_plat *host = container_of(mci, struct am654_sdhci_plat, mci);
+ u32 command, xfer;
+ int ret;
+ dma_addr_t dma;
+
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
+
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+ ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+ if (ret)
+ goto error;
+
+ sdhci_read_response(&host->sdhci, cmd);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
+
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+
+error:
+ if (ret) {
+ print_error(host, cmd->cmdidx);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ return ret;
+}
+
+static void am654_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct am654_sdhci_plat *plat = container_of(mci, struct am654_sdhci_plat, mci);
+ u32 val;
+
+ if (ios->clock)
+ sdhci_set_clock(&plat->sdhci, ios->clock, plat->sdhci.max_clk);
+
+ sdhci_set_bus_width(&plat->sdhci, ios->bus_width);
+
+ val = sdhci_read8(&plat->sdhci, SDHCI_HOST_CONTROL);
+
+ if (ios->clock > 26000000)
+ val |= SDHCI_CTRL_HISPD;
+ else
+ val &= ~SDHCI_CTRL_HISPD;
+
+ sdhci_write8(&plat->sdhci, SDHCI_HOST_CONTROL, val);
+
+ plat->soc_data->set_ios_post(plat, ios);
+}
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x400,
+};
+
+static int am654_sdhci_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct am654_sdhci_plat *plat;
+ struct mci_host *mci;
+ struct resource *iores;
+ u32 drv_strength;
+ int ret;
+
+ plat = xzalloc(sizeof(*plat));
+
+ ret = dev_get_drvdata(dev, (const void **)&plat->soc_data);
+ if (ret)
+ return ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ plat->sdhci.base = IOMEM(iores->start);
+
+ iores = dev_request_mem_resource(dev, 1);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ plat->base = regmap_init_mmio(dev, IOMEM(iores->start), &regmap_config);
+ if (IS_ERR(plat->base)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(plat->base));
+ return PTR_ERR(plat->base);
+ }
+
+ plat->clk = clk_get(dev, "clk_xin");
+ if (IS_ERR(plat->clk)) {
+ dev_err(dev, "failed to get clock\n");
+ return ret;
+ }
+
+ clk_enable(plat->clk);
+
+ plat->clk_ahb = clk_get(dev, "clk_ahb");
+ if (IS_ERR(plat->clk_ahb)) {
+ dev_err(dev, "failed to get ahb clock\n");
+ return ret;
+ }
+
+ clk_enable(plat->clk_ahb);
+
+ mci = &plat->mci;
+ mci->f_max = clk_get_rate(plat->clk);
+ mci->f_min = 50000000 / 256;
+
+ if (plat->soc_data->flags & DLL_PRESENT) {
+ ret = of_property_read_u32(np, "ti,trm-icp", &plat->trm_icp);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "ti,driver-strength-ohm",
+ &drv_strength);
+ if (ret)
+ return ret;
+
+ switch (drv_strength) {
+ case 50:
+ plat->drv_strength = DRIVER_STRENGTH_50_OHM;
+ break;
+ case 33:
+ plat->drv_strength = DRIVER_STRENGTH_33_OHM;
+ break;
+ case 66:
+ plat->drv_strength = DRIVER_STRENGTH_66_OHM;
+ break;
+ case 100:
+ plat->drv_strength = DRIVER_STRENGTH_100_OHM;
+ break;
+ case 40:
+ plat->drv_strength = DRIVER_STRENGTH_40_OHM;
+ break;
+ default:
+ dev_err(dev, "Invalid driver strength\n");
+ return -EINVAL;
+ }
+ }
+
+ mci->send_cmd = am654_sdhci_send_cmd;
+ mci->set_ios = am654_sdhci_set_ios;
+ mci->init = am654_sdhci_init;
+ mci->hw_dev = dev;
+
+ of_property_read_u32(np, "ti,strobe-sel", &plat->strb_sel);
+ of_property_read_u32(np, "ti,clkbuf-sel", &plat->clkbuf_sel);
+
+ plat->fails_without_test_cd = of_property_read_bool(np, "ti,fails-without-test-cd");
+
+ mci_of_parse(&plat->mci);
+
+ ret = sdhci_am654_get_otap_delay(plat);
+ if (ret)
+ return ret;
+
+ plat->sdhci.mci = mci;
+ sdhci_setup_host(&plat->sdhci);
+
+ dev->priv = plat;
+
+ return mci_register(&plat->mci);
+}
+
+static const struct of_device_id am654_sdhci_ids[] = {
+ {
+ .compatible = "ti,am654-sdhci-5.1",
+ .data = &am654_drv_data,
+ }, {
+ .compatible = "ti,j721e-sdhci-8bit",
+ .data = &j721e_8bit_drv_data,
+ }, {
+ .compatible = "ti,j721e-sdhci-4bit",
+ .data = &j721e_4bit_drv_data,
+ }, {
+ .compatible = "ti,am64-sdhci-8bit",
+ .data = &sdhci_am64_8bit_drvdata,
+ }, {
+ .compatible = "ti,am64-sdhci-4bit",
+ .data = &sdhci_am64_4bit_drvdata,
+ }, {
+ .compatible = "ti,am62-sdhci",
+ .data = &sdhci_am64_4bit_drvdata,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, am654_sdhci_ids);
+
+static struct driver am654_sdhc_driver = {
+ .name = "am654-sdhci",
+ .probe = am654_sdhci_probe,
+ .of_compatible = DRV_OF_COMPAT(am654_sdhci_ids),
+};
+device_platform_driver(am654_sdhc_driver);
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index d45f9184cd..be13954541 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -6,6 +6,7 @@
#include <init.h>
#include <linux/clk.h>
#include <mci.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "sdhci.h"
@@ -30,13 +31,52 @@
#define SDHCI_ARASAN_BUS_WIDTH 4
#define TIMEOUT_VAL 0xE
+#define ZYNQMP_CLK_PHASES 10
+#define ZYNQMP_CLK_PHASE_UHS_SDR104 6
+#define ZYNQMP_CLK_PHASE_HS200 9
+/* Default settings for ZynqMP Clock Phases */
+#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
+#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
+
+/**
+ * struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
+ *
+ * @sdcardclk_hw: Struct for the clock we might provide to a PHY.
+ * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
+ * @sampleclk_hw: Struct for the clock we might provide to a PHY.
+ * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw.
+ * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes
+ * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes
+ * @set_clk_delays: Function pointer for setting Clock Delays
+ * @clk_of_data: Platform specific runtime clock data storage pointer
+ */
+struct sdhci_arasan_clk_data {
+ struct clk_hw sdcardclk_hw;
+ struct clk *sdcardclk;
+ struct clk_hw sampleclk_hw;
+ struct clk *sampleclk;
+ int clk_phase_in[ZYNQMP_CLK_PHASES + 1];
+ int clk_phase_out[ZYNQMP_CLK_PHASES + 1];
+ void (*set_clk_delays)(struct sdhci *host);
+ void *clk_of_data;
+};
+
struct arasan_sdhci_host {
struct mci_host mci;
struct sdhci sdhci;
unsigned int quirks; /* Arasan deviations from spec */
+ const struct clk_ops *sdcardclk_ops;
+ const struct clk_ops *sampleclk_ops;
+ struct sdhci_arasan_clk_data clk_data;
/* Controller does not have CD wired and will not function normally without */
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
#define SDHCI_ARASAN_QUIRK_NO_1_8_V BIT(1)
+/*
+ * Some of the Arasan variations might not have timing requirements
+ * met at 25MHz for Default Speed mode, those controllers work at
+ * 19MHz instead
+ */
+#define SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN BIT(2)
};
static inline
@@ -73,27 +113,64 @@ static int arasan_sdhci_card_write_protected(struct mci_host *mci)
static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask)
{
- sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask);
+ int ret;
- /* wait for reset completion */
- if (wait_on_timeout(100 * MSECOND,
- !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))) {
- dev_err(host->mci.hw_dev, "SDHCI reset timeout\n");
- return -ETIMEDOUT;
- }
+ ret = sdhci_reset(&host->sdhci, mask);
+ if (ret)
+ return ret;
if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
u8 ctrl;
ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_INS;
- sdhci_write8(&host->sdhci, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, ctrl);
}
return 0;
}
-static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
+static void arasan_zynqmp_dll_reset(struct arasan_sdhci_host *host, u32 deviceid)
+{
+ u16 clk;
+
+ clk = sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL);
+ clk &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, clk);
+
+ /* Issue DLL Reset */
+ zynqmp_pm_sd_dll_reset(deviceid, PM_DLL_RESET_PULSE);
+
+ clk = sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL);
+
+ sdhci_enable_clk(&host->sdhci, clk);
+}
+
+static int arasan_zynqmp_execute_tuning(struct mci_host *mci, u32 opcode)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ struct clk_hw *hw = &host->clk_data.sdcardclk_hw;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 device_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 :
+ NODE_SD_1;
+ int err;
+
+ /* ZynqMP SD controller does not perform auto tuning in DDR50 mode */
+ if (mci->timing == MMC_TIMING_UHS_DDR50)
+ return 0;
+
+ arasan_zynqmp_dll_reset(host, device_id);
+
+ err = sdhci_execute_tuning(&host->sdhci, opcode);
+ if (err)
+ return err;
+
+ arasan_zynqmp_dll_reset(host, device_id);
+
+ return 0;
+}
+
+static int arasan_sdhci_init(struct mci_host *mci, struct device *dev)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
int ret;
@@ -113,13 +190,36 @@ static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
+static void arasan_sdhci_set_clock(struct mci_host *mci, unsigned int clock)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ struct sdhci_arasan_clk_data *clk_data = &host->clk_data;
+
+ if (host->quirks & SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN) {
+ /*
+ * Some of the Arasan variations might not have timing
+ * requirements met at 25MHz for Default Speed mode,
+ * those controllers work at 19MHz instead.
+ */
+ if (clock == 25000000)
+ clock = (25000000 * 19) / 25;
+ }
+
+ clk_set_phase(clk_data->sampleclk,
+ clk_data->clk_phase_in[mci->mci->host->timing]);
+ clk_set_phase(clk_data->sdcardclk,
+ clk_data->clk_phase_out[mci->mci->host->timing]);
+
+ sdhci_set_clock(&host->sdhci, clock, host->sdhci.max_clk);
+}
+
static void arasan_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
u16 val;
if (ios->clock)
- sdhci_set_clock(&host->sdhci, ios->clock, host->sdhci.max_clk);
+ arasan_sdhci_set_clock(mci, ios->clock);
sdhci_set_bus_width(&host->sdhci, ios->bus_width);
@@ -133,34 +233,11 @@ static void arasan_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
}
-static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask)
+static void print_error(struct arasan_sdhci_host *host, int cmdidx, int ret)
{
- u64 start = get_time_ns();
- u16 stat;
- u16 error;
-
- do {
- stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
- if (stat & SDHCI_INT_ERROR) {
- error = sdhci_read16(&host->sdhci,
- SDHCI_INT_ERROR_STATUS);
- dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
- error);
- return -EPERM;
- }
+ if (ret == -ETIMEDOUT)
+ return;
- if (is_timeout(start, 1000 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for done\n");
- return -ETIMEDOUT;
- }
- } while ((stat & mask) != mask);
-
- return 0;
-}
-
-static void print_error(struct arasan_sdhci_host *host, int cmdidx)
-{
dev_err(host->mci.hw_dev,
"error while transferring data for command %d\n", cmdidx);
dev_err(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
@@ -173,35 +250,29 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
u32 mask, command, xfer;
+ dma_addr_t dma;
int ret;
- /* Wait for idle before next command */
- mask = SDHCI_CMD_INHIBIT_CMD;
- if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
- mask |= SDHCI_CMD_INHIBIT_DATA;
-
- ret = wait_on_timeout(10 * MSECOND,
- !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
- if (ret) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for idle\n");
+ ret = sdhci_wait_idle(&host->sdhci, cmd, data);
+ if (ret)
return ret;
- }
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
mask = SDHCI_INT_CMD_COMPLETE;
- if (data && data->flags == MMC_DATA_READ)
- mask |= SDHCI_INT_DATA_AVAIL;
if (cmd->resp_type & MMC_RSP_BUSY)
mask |= SDHCI_INT_XFER_COMPLETE;
- sdhci_set_cmd_xfer_mode(&host->sdhci,
- cmd, data, false, &command, &xfer);
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, TIMEOUT_VAL);
- if (data) {
+ if (xfer)
sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ if (data) {
sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE,
SDHCI_DMA_BOUNDARY_512K | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
@@ -209,23 +280,25 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
- ret = arasan_sdhci_wait_for_done(host, mask);
- if (ret == -EPERM)
+ /* CMD19/21 generate _only_ Buffer Read Ready interrupt */
+ if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200)
+ mask = SDHCI_INT_DATA_AVAIL;
+
+ ret = sdhci_wait_for_done(&host->sdhci, mask);
+ if (ret)
goto error;
- else if (ret)
- return ret;
sdhci_read_response(&host->sdhci, cmd);
- sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, mask);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
if (data)
- ret = sdhci_transfer_data_pio(&host->sdhci, data);
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
error:
if (ret) {
- print_error(host, cmd->cmdidx);
- arasan_sdhci_reset(host, BIT(1)); // SDHCI_RESET_CMD
- arasan_sdhci_reset(host, BIT(2)); // SDHCI_RESET_DATA
+ print_error(host, cmd->cmdidx, ret);
+ arasan_sdhci_reset(host, SDHCI_RESET_CMD);
+ arasan_sdhci_reset(host, SDHCI_RESET_DATA);
}
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
@@ -233,9 +306,414 @@ error:
return ret;
}
-static int arasan_sdhci_probe(struct device_d *dev)
+static void sdhci_arasan_set_clk_delays(struct sdhci *host)
+{
+ struct arasan_sdhci_host *arasan_sdhci = sdhci_to_arasan(host);
+ struct mci_host *mci = &arasan_sdhci->mci;
+ struct sdhci_arasan_clk_data *clk_data = &arasan_sdhci->clk_data;
+
+ clk_set_phase(clk_data->sampleclk,
+ clk_data->clk_phase_in[mci->timing]);
+ clk_set_phase(clk_data->sdcardclk,
+ clk_data->clk_phase_out[mci->timing]);
+}
+
+static void arasan_dt_read_clk_phase(struct device *dev,
+ struct sdhci_arasan_clk_data *clk_data,
+ unsigned int timing, const char *prop)
+{
+ struct device_node *np = dev->of_node;
+
+ u32 clk_phase[2] = {0};
+ int ret;
+
+ /*
+ * Read Tap Delay values from DT, if the DT does not contain the
+ * Tap Values then use the pre-defined values.
+ */
+ ret = of_property_read_u32_array(np, prop, &clk_phase[0], 2);
+ if (ret < 0) {
+ dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
+ prop, clk_data->clk_phase_in[timing],
+ clk_data->clk_phase_out[timing]);
+ return;
+ }
+
+ /* The values read are Input and Output Clock Delays in order */
+ clk_data->clk_phase_in[timing] = clk_phase[0];
+ clk_data->clk_phase_out[timing] = clk_phase[1];
+}
+
+/**
+ * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees: The clock phase shift between 0 - 359.
+ *
+ * Set the SD Input Clock Tap Delays for Input path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 node_id = !strcmp(clk_name, "clk_in_sd0") ? NODE_SD_0 : NODE_SD_1;
+ u8 tap_delay, tap_max = 0;
+ int ret;
+
+ /* This is applicable for SDHCI_SPEC_300 and above */
+ if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+ return 0;
+
+ /* Assert DLL Reset */
+ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_ASSERT);
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 120 Taps are available */
+ tap_max = 120;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 60 Taps are available */
+ tap_max = 60;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_INPUT, tap_delay);
+ if (ret)
+ pr_err("Error setting Input Tap Delay\n");
+
+ return ret;
+}
+
+static unsigned long arasan_sampleclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+
+ return host->actual_clock;
+};
+
+/**
+ * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees: The clock phase shift between 0 - 359.
+ *
+ * Set the SD Output Clock Tap Delays for Output path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1;
+ u8 tap_delay, tap_max = 0;
+ int ret;
+
+ /* This is applicable for SDHCI_SPEC_300 and above */
+ if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+ return 0;
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 15 Taps are available */
+ tap_max = 15;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 8 Taps are available */
+ tap_max = 8;
+ break;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_OUTPUT, tap_delay);
+ if (ret)
+ pr_err("Error setting Output Tap Delay\n");
+
+ /* Release DLL Reset */
+ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_RELEASE);
+
+ return ret;
+}
+
+static unsigned long arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+
+ return host->actual_clock;
+};
+
+static const struct clk_ops arasan_sampleclk_ops = {
+ .recalc_rate = arasan_sampleclk_recalc_rate,
+};
+
+static const struct clk_ops arasan_sdcardclk_ops = {
+ .recalc_rate = arasan_sdcardclk_recalc_rate,
+};
+
+static const struct clk_ops zynqmp_sampleclk_ops = {
+ .recalc_rate = arasan_sampleclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sampleclk_set_phase,
+};
+
+static const struct clk_ops zynqmp_sdcardclk_ops = {
+ .recalc_rate = arasan_sdcardclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sdcardclk_set_phase,
+};
+
+/**
+ * arasan_sdhci_register_sampleclk - Register the sampleclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sampleclk(struct arasan_sdhci_host *sdhci_arasan,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data;
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sampleclk_init = {};
+ const char *clk_name;
+ int ret;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 1,
+ &sampleclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ clk_name = __clk_get_name(clk_xin);
+ sampleclk_init.parent_names = &clk_name;
+ sampleclk_init.num_parents = 1;
+ sampleclk_init.ops = sdhci_arasan->sampleclk_ops;
+
+ clk_data->sampleclk_hw.init = &sampleclk_init;
+ clk_data->sampleclk = clk_register(dev, &clk_data->sampleclk_hw);
+ if (IS_ERR(clk_data->sampleclk))
+ return PTR_ERR(clk_data->sampleclk);
+ clk_data->sampleclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ clk_data->sampleclk);
+ if (ret)
+ dev_err(dev, "Failed to add sample clock provider\n");
+
+ return ret;
+}
+
+/**
+ * sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sdcardclk(struct arasan_sdhci_host *sdhci_arasan,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data;
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sdcardclk_init = {};
+ const char *clk_name;
+ int ret;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 0,
+ &sdcardclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ clk_name = __clk_get_name(clk_xin);
+ sdcardclk_init.parent_names = &clk_name;
+ sdcardclk_init.ops = sdhci_arasan->sdcardclk_ops;
+ sdcardclk_init.num_parents = 1;
+
+ clk_data->sdcardclk_hw.init = &sdcardclk_init;
+ clk_data->sdcardclk = clk_register(dev, &clk_data->sdcardclk_hw);
+ if (IS_ERR(clk_data->sdcardclk))
+ return PTR_ERR(clk_data->sdcardclk);
+ clk_data->sdcardclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ clk_data->sdcardclk);
+ if (ret)
+ dev_err(dev, "Failed to add sdcard clock provider\n");
+
+ return ret;
+}
+
+/**
+ * arasan_sdhci_register_sdclk - Register the sdcardclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented. Instead, we'll just create a clock that can
+ * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
+ * framework that we're doing things behind its back. This should be sufficient
+ * to create nice clean device tree bindings and later (if needed) we can try
+ * re-architecting SDHCI if we see some benefit to it.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_sdhci_register_sdclk(struct arasan_sdhci_host *sdhci_arasan,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ u32 num_clks = 0;
+ int ret;
+
+ /* Providing a clock to the PHY is optional; no error if missing */
+ if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
+ return 0;
+
+ ret = arasan_sdhci_register_sdcardclk(sdhci_arasan, clk_xin, dev);
+ if (ret)
+ return ret;
+
+ if (num_clks)
+ return arasan_sdhci_register_sampleclk(sdhci_arasan, clk_xin,
+ dev);
+
+ return 0;
+}
+
+/**
+ * arasan_dt_parse_clk_phases - Read Clock Delay values from DT
+ *
+ * @dev: Pointer to our struct device.
+ * @clk_data: Pointer to the Clock Data structure
+ *
+ * Called at initialization to parse the values of Clock Delays.
+ */
+static void arasan_dt_parse_clk_phases(struct device *dev,
+ struct sdhci_arasan_clk_data *clk_data)
{
- struct device_node *np = dev->device_node;
+ u32 mio_bank = 0;
+ int i;
+
+ /*
+ * This has been kept as a pointer and is assigned a function here.
+ * So that different controller variants can assign their own handling
+ * function.
+ */
+ clk_data->set_clk_delays = sdhci_arasan_set_clk_delays;
+
+ if (of_device_is_compatible(dev->of_node, "xlnx,zynqmp-8.9a")) {
+ u32 zynqmp_iclk_phase[ZYNQMP_CLK_PHASES + 1] = ZYNQMP_ICLK_PHASE;
+ u32 zynqmp_oclk_phase[ZYNQMP_CLK_PHASES + 1] = ZYNQMP_OCLK_PHASE;
+
+ of_property_read_u32(dev->of_node, "xlnx,mio-bank", &mio_bank);
+ if (mio_bank == 2) {
+ zynqmp_oclk_phase[ZYNQMP_CLK_PHASE_UHS_SDR104] = 90;
+ zynqmp_oclk_phase[ZYNQMP_CLK_PHASE_HS200] = 90;
+ }
+
+ for (i = 0; i <= ZYNQMP_CLK_PHASES; i++) {
+ clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i];
+ clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i];
+ }
+ }
+
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
+ "clk-phase-legacy");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
+ "clk-phase-mmc-hs");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
+ "clk-phase-sd-hs");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
+ "clk-phase-uhs-sdr12");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
+ "clk-phase-uhs-sdr25");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
+ "clk-phase-uhs-sdr50");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
+ "clk-phase-uhs-sdr104");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
+ "clk-phase-uhs-ddr50");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
+ "clk-phase-mmc-ddr52");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
+ "clk-phase-mmc-hs200");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
+ "clk-phase-mmc-hs400");
+}
+
+static int arasan_sdhci_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
struct arasan_sdhci_host *arasan_sdhci;
struct clk *clk_xin, *clk_ahb;
struct resource *iores;
@@ -280,6 +758,12 @@ static int arasan_sdhci_probe(struct device_d *dev)
if (of_property_read_bool(np, "no-1-8-v"))
arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_NO_1_8_V;
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
+ if (IS_ENABLED(CONFIG_MCI_TUNING))
+ mci->execute_tuning = arasan_zynqmp_execute_tuning;
+ arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN;
+ }
+
arasan_sdhci->sdhci.base = IOMEM(iores->start);
arasan_sdhci->sdhci.mci = mci;
mci->send_cmd = arasan_sdhci_send_cmd;
@@ -289,9 +773,29 @@ static int arasan_sdhci_probe(struct device_d *dev)
mci->card_write_protected = arasan_sdhci_card_write_protected;
mci->hw_dev = dev;
- mci->f_max = clk_get_rate(clk_xin);
+ /*
+ * clk_rates on ZynqMP are rounded wrong. For HS200 clk_get_rate retunrs
+ * 199999998 instead of 200000000
+ */
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
+ mci->f_max = 200000000;
+ else
+ mci->f_max = clk_get_rate(clk_xin);
+
mci->f_min = 50000000 / 256;
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
+ arasan_sdhci->sdcardclk_ops = &zynqmp_sdcardclk_ops;
+ arasan_sdhci->sampleclk_ops = &zynqmp_sampleclk_ops;
+ } else {
+ arasan_sdhci->sdcardclk_ops = &arasan_sdcardclk_ops;
+ arasan_sdhci->sampleclk_ops = &arasan_sampleclk_ops;
+ }
+
+ arasan_sdhci_register_sdclk(arasan_sdhci, clk_xin, dev);
+
+ arasan_dt_parse_clk_phases(dev, &arasan_sdhci->clk_data);
+
/* parse board supported bus width capabilities */
mci_of_parse(&arasan_sdhci->mci);
@@ -306,8 +810,9 @@ static __maybe_unused struct of_device_id arasan_sdhci_compatible[] = {
{ .compatible = "arasan,sdhci-8.9a" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, arasan_sdhci_compatible);
-static struct driver_d arasan_sdhci_driver = {
+static struct driver arasan_sdhci_driver = {
.name = "arasan-sdhci",
.probe = arasan_sdhci_probe,
.of_compatible = DRV_OF_COMPAT(arasan_sdhci_compatible),
diff --git a/drivers/mci/atmel-mci-regs.h b/drivers/mci/atmel-mci-regs.h
index 045c1a9f38..44db8a9dff 100644
--- a/drivers/mci/atmel-mci-regs.h
+++ b/drivers/mci/atmel-mci-regs.h
@@ -155,7 +155,7 @@ struct atmel_mci_caps {
struct atmel_mci {
struct mci_host mci;
void __iomem *regs;
- struct device_d *hw_dev;
+ struct device *hw_dev;
struct clk *clk;
u32 datasize;
@@ -190,7 +190,7 @@ static inline unsigned int atmci_convert_chksize(unsigned int maxburst)
}
void atmci_common_set_ios(struct atmel_mci *host, struct mci_ios *ios);
-int atmci_reset(struct mci_host *mci, struct device_d *mci_dev);
+int atmci_reset(struct mci_host *mci, struct device *mci_dev);
int atmci_common_request(struct atmel_mci *host, struct mci_cmd *cmd,
struct mci_data *data);
void atmci_get_cap(struct atmel_mci *host);
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
index 129480aa55..082ce842f7 100644
--- a/drivers/mci/atmel-sdhci-common.c
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -13,7 +13,7 @@
#include <mci.h>
#include <linux/bitfield.h>
-#include <mach/early_udelay.h>
+#include <mach/at91/early_udelay.h>
#ifdef __PBL__
#define udelay early_udelay
@@ -39,7 +39,7 @@ static inline struct at91_sdhci *to_priv(struct sdhci *sdhci)
void at91_sdhci_host_capability(struct at91_sdhci *host,
unsigned *voltages)
{
- u16 caps;
+ u32 caps;
caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES);
@@ -89,52 +89,18 @@ exit:
return is_inserted;
}
-static int at91_sdhci_wait_for_done(struct at91_sdhci *host, u32 mask)
-{
- struct sdhci *sdhci = &host->sdhci;
- u32 status;
- int ret;
-
- ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, status,
- (status & mask) == mask || (status & SDHCI_INT_ERROR),
- USEC_PER_SEC);
-
- if (ret < 0) {
- dev_err(host->dev, "SDHCI timeout while waiting for done\n");
- return ret;
- }
-
- if (status & SDHCI_INT_TIMEOUT)
- return -ETIMEDOUT;
-
- if (status & SDHCI_INT_ERROR) {
- dev_err(host->dev, "SDHCI_INT_STATUS: 0x%08x\n", status);
- return -EPERM;
- }
-
- return status & 0xFFFF;
-}
-
int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
struct mci_data *data)
{
unsigned command, xfer;
struct sdhci *sdhci = &host->sdhci;
- u32 mask, state;
+ u32 mask;
int status;
int ret;
- /* Wait for idle before next command */
- mask = SDHCI_CMD_INHIBIT_CMD;
- if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
- mask |= SDHCI_CMD_INHIBIT_DATA;
-
- ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, state,
- !(state & mask), 100 * USEC_PER_MSEC);
- if (ret) {
- dev_err(host->dev, "timeout while waiting for idle\n");
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
return ret;
- }
sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
@@ -158,7 +124,7 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
sdhci_write32(sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
sdhci_write16(sdhci, SDHCI_COMMAND, command);
- status = at91_sdhci_wait_for_done(host, mask);
+ status = sdhci_wait_for_done(&host->sdhci, mask);
if (status < 0)
goto error;
@@ -219,17 +185,12 @@ static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock)
struct sdhci *sdhci = &host->sdhci;
unsigned clk = 0, clk_div;
unsigned reg;
- u32 present_mask, caps, caps_clk_mult;
+ u32 caps, caps_clk_mult;
int ret;
- present_mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
- ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, reg,
- !(reg & present_mask),
- 100 * USEC_PER_MSEC);
- if (ret) {
- dev_warn(host->dev, "Timeout waiting for CMD and DAT Inhibit bits\n");
+ ret = sdhci_wait_idle_data(&host->sdhci, NULL);
+ if (ret)
return ret;
- }
sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, 0);
diff --git a/drivers/mci/atmel-sdhci-pbl.c b/drivers/mci/atmel-sdhci-pbl.c
index 2c5f107abd..f5a7279bff 100644
--- a/drivers/mci/atmel-sdhci-pbl.c
+++ b/drivers/mci/atmel-sdhci-pbl.c
@@ -11,10 +11,10 @@
#include <pbl/bio.h>
#include <mci.h>
#include <debug_ll.h>
-#include <mach/xload.h>
+#include <mach/at91/xload.h>
#include "atmel-sdhci.h"
-#include <mach/early_udelay.h>
+#include <mach/at91/early_udelay.h>
#ifdef __PBL__
#define udelay early_udelay
diff --git a/drivers/mci/atmel-sdhci.c b/drivers/mci/atmel-sdhci.c
index 380a79d33e..c124e736bb 100644
--- a/drivers/mci/atmel-sdhci.c
+++ b/drivers/mci/atmel-sdhci.c
@@ -40,7 +40,7 @@ static void at91_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
at91_sdhci_set_ios(&to_priv(mci)->host, ios);
}
-static int at91_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
+static int at91_sdhci_mci_init(struct mci_host *mci, struct device *dev)
{
struct at91_sdhci_priv *priv = to_priv(mci);
struct sdhci *sdhci = &priv->host.sdhci;
@@ -99,7 +99,7 @@ static int at91_sdhci_card_present(struct mci_host *mci)
return at91_sdhci_is_card_inserted(&to_priv(mci)->host);
}
-static int at91_sdhci_probe(struct device_d *dev)
+static int at91_sdhci_probe(struct device *dev)
{
struct at91_sdhci_priv *priv;
struct resource *iores;
@@ -135,7 +135,7 @@ static int at91_sdhci_probe(struct device_d *dev)
* if SDCAL pin is wrongly connected, we must enable
* the analog calibration cell permanently.
*/
- priv->cal_always_on = of_property_read_bool(dev->device_node,
+ priv->cal_always_on = of_property_read_bool(dev->of_node,
"microchip,sdcal-inverted");
at91_sdhci_mmio_init(&priv->host, IOMEM(iores->start));
@@ -162,8 +162,9 @@ static const struct of_device_id at91_sdhci_dt_match[] = {
{ .compatible = "microchip,sam9x60-sdhci" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, at91_sdhci_dt_match);
-static struct driver_d at91_sdhci_driver = {
+static struct driver at91_sdhci_driver = {
.name = "sdhci-at91",
.of_compatible = DRV_OF_COMPAT(at91_sdhci_dt_match),
.probe = at91_sdhci_probe,
diff --git a/drivers/mci/atmel-sdhci.h b/drivers/mci/atmel-sdhci.h
index 7032294647..8f07de340d 100644
--- a/drivers/mci/atmel-sdhci.h
+++ b/drivers/mci/atmel-sdhci.h
@@ -11,7 +11,7 @@
struct at91_sdhci {
struct sdhci sdhci;
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
u32 caps_max_clock;
};
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
index e676c1bd5c..9021dba0f8 100644
--- a/drivers/mci/atmel_mci.c
+++ b/drivers/mci/atmel_mci.c
@@ -31,7 +31,7 @@ static int atmci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
return atmci_common_request(host, cmd, data);
}
-static void atmci_info(struct device_d *mci_dev)
+static void atmci_info(struct device *mci_dev)
{
struct atmel_mci *host = mci_dev->priv;
@@ -67,11 +67,11 @@ static int atmci_card_present(struct mci_host *mci)
return ret == 0 ? 1 : 0;
}
-static int atmci_probe(struct device_d *hw_dev)
+static int atmci_probe(struct device *hw_dev)
{
struct resource *iores;
struct atmel_mci *host;
- struct device_node *np = hw_dev->device_node;
+ struct device_node *np = hw_dev->of_node;
struct atmel_mci_platform_data *pd = hw_dev->platform_data;
int ret;
@@ -105,8 +105,8 @@ static int atmci_probe(struct device_d *hw_dev)
for_each_child_of_node(np, cnp) {
if (of_property_read_u32(cnp, "reg", &slot_id)) {
- dev_warn(hw_dev, "reg property is missing for %s\n",
- cnp->full_name);
+ dev_warn(hw_dev, "reg property is missing for %pOF\n",
+ cnp);
continue;
}
@@ -151,6 +151,7 @@ static int atmci_probe(struct device_d *hw_dev)
clk_enable(host->clk);
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_PWSDIS);
atmci_writel(host, ATMCI_IDR, ~0UL);
host->bus_hz = clk_get_rate(host->clk);
clk_disable(host->clk);
@@ -193,8 +194,9 @@ static __maybe_unused struct of_device_id atmci_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, atmci_compatible);
-static struct driver_d atmci_driver = {
+static struct driver atmci_driver = {
.name = "atmel_mci",
.probe = atmci_probe,
.of_compatible = DRV_OF_COMPAT(atmci_compatible),
diff --git a/drivers/mci/atmel_mci_common.c b/drivers/mci/atmel_mci_common.c
index 5c9e6f9c4d..7b11e9134e 100644
--- a/drivers/mci/atmel_mci_common.c
+++ b/drivers/mci/atmel_mci_common.c
@@ -68,7 +68,17 @@ static void atmci_set_clk_rate(struct atmel_mci *host,
clock_min, host->bus_hz / (2 * 256));
clkdiv = 255;
}
- host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
+
+ /*
+ * Older Atmels without CLKODD have the block length
+ * in the upper 16 bits of both MCI_MR and MCI_BLKR
+ *
+ * To avoid intermittent zeroing of the block length,
+ * just hardcode 512 here and have atmci_setup_data()
+ * change it as necessary.
+ */
+
+ host->mode_reg = ATMCI_MR_CLKDIV(clkdiv) | ATMCI_BLKLEN(512);
}
dev_dbg(host->hw_dev, "atmel_set_clk_rate: clkIn=%ld clkIos=%d divider=%d\n",
@@ -90,8 +100,8 @@ static int atmci_poll_status(struct atmel_mci *host, u32 mask)
u32 stat;
int ret;
- ret = read_poll_timeout(atmci_readl, stat, (stat & mask), SECOND, host,
- ATMCI_SR);
+ ret = read_poll_timeout(atmci_readl, stat, (stat & mask), USEC_PER_SEC,
+ host, ATMCI_SR);
if (ret < 0) {
dev_err(host->hw_dev, "timeout\n");
host->need_reset = true;
@@ -220,7 +230,7 @@ static void atmci_setup_data(struct atmel_mci *host, struct mci_data *data)
host->data = data;
- dev_dbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n",
+ dev_vdbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n",
nob, blksz);
atmci_writel(host, ATMCI_BLKR, ATMCI_BCNT(nob)
@@ -315,7 +325,7 @@ static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd,
flags |= ATMCI_CMDR_RSPTYP_NONE;
break;
default:
- dev_err(host->hw_dev, "unhandled response type 0x%x\n",
+ dev_dbg(host->hw_dev, "unhandled response type 0x%x\n",
cmd->resp_type);
return -EINVAL;
}
@@ -329,7 +339,7 @@ static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd,
}
/** init the host interface */
-int atmci_reset(struct mci_host *mci, struct device_d *mci_dev)
+int atmci_reset(struct mci_host *mci, struct device *mci_dev)
{
struct atmel_mci *host = to_mci_host(mci);
@@ -437,7 +447,7 @@ void atmci_get_cap(struct atmel_mci *host)
version = atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
host->version = version;
- dev_info(host->hw_dev, "version: 0x%x\n", version);
+ dev_dbg(host->hw_dev, "version: 0x%x\n", version);
host->caps.has_cfg_reg = 0;
host->caps.has_highspeed = 0;
diff --git a/drivers/mci/atmel_mci_pbl.c b/drivers/mci/atmel_mci_pbl.c
index 65d8b3632a..bd4faa4de5 100644
--- a/drivers/mci/atmel_mci_pbl.c
+++ b/drivers/mci/atmel_mci_pbl.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
-#include <mach/xload.h>
+#include <mach/at91/xload.h>
#include <mci.h>
#include "atmel-mci-regs.h"
@@ -106,6 +106,7 @@ int at91_mci_bio_init(struct pbl_bio *bio, void __iomem *base,
else
host->sdc_reg = ATMCI_SDCSEL_SLOT_A;
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_PWSDIS);
atmci_writel(host, ATMCI_DTOR, 0x7f);
atmci_common_set_ios(host, &ios);
diff --git a/drivers/mci/bcm2835-sdhost.c b/drivers/mci/bcm2835-sdhost.c
index 7e19c6eb1e..2b1336a7d3 100644
--- a/drivers/mci/bcm2835-sdhost.c
+++ b/drivers/mci/bcm2835-sdhost.c
@@ -135,7 +135,7 @@ static inline struct bcm2835_host *to_bcm2835_host(struct mci_host *mci)
return container_of(mci, struct bcm2835_host, mci);
}
-static int bcm2835_sdhost_init(struct mci_host *mci, struct device_d *dev)
+static int bcm2835_sdhost_init(struct mci_host *mci, struct device *dev)
{
struct bcm2835_host *host = to_bcm2835_host(mci);
u32 temp;
@@ -579,7 +579,7 @@ static void bcm2835_set_ios(struct mci_host *mci, struct mci_ios *ios)
writel(hcfg, host->regs + SDHCFG);
}
-static int bcm2835_sdhost_probe(struct device_d *dev)
+static int bcm2835_sdhost_probe(struct device *dev)
{
struct bcm2835_host *host;
struct resource *iores;
@@ -619,8 +619,9 @@ static __maybe_unused struct of_device_id bcm2835_sdhost_compatible[] = {
{ .compatible = "brcm,bcm2835-sdhost" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, bcm2835_sdhost_compatible);
-static struct driver_d bcm2835_sdhost_driver = {
+static struct driver bcm2835_sdhost_driver = {
.name = "bcm2835-sdhost",
.probe = bcm2835_sdhost_probe,
.of_compatible = DRV_OF_COMPAT(bcm2835_sdhost_compatible),
diff --git a/drivers/mci/dove-sdhci.c b/drivers/mci/dove-sdhci.c
index 7581511285..d37046ad31 100644
--- a/drivers/mci/dove-sdhci.c
+++ b/drivers/mci/dove-sdhci.c
@@ -57,39 +57,26 @@ static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask)
static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data)
{
- u16 val;
u32 command, xfer;
- u64 start;
int ret;
unsigned int num_bytes = 0;
struct dove_sdhci *host = priv_from_mci_host(mci);
- sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
-
- /* Do not wait for CMD_INHIBIT_DAT on stop commands */
- if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
- val = SDHCI_CMD_INHIBIT_CMD;
- else
- val = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
- /* Wait for bus idle */
- start = get_time_ns();
- while (1) {
- if (!(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & val))
- break;
- if (is_timeout(start, 10 * MSECOND)) {
- dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
- return -ETIMEDOUT;
- }
- }
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
/* setup transfer data */
if (data) {
num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_READ)
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->dest)));
else
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->src)));
sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
@@ -97,10 +84,12 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_device((unsigned long)data->src,
+ dma_sync_single_for_device(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->src)),
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_device((unsigned long)data->dest,
+ dma_sync_single_for_device(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->dest)),
num_bytes, DMA_FROM_DEVICE);
}
@@ -126,11 +115,13 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
if (data) {
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
+ dma_sync_single_for_cpu(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->src)),
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
- num_bytes, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->dest)),
+ num_bytes, DMA_FROM_DEVICE);
ret = dove_sdhci_wait_for_done(host, SDHCI_INT_XFER_COMPLETE);
if (ret) {
@@ -189,6 +180,8 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
case MMC_BUS_WIDTH_4:
val |= SDHCI_CTRL_4BITBUS;
break;
+ case MMC_BUS_WIDTH_1:
+ break;
}
if (ios->clock > 26000000)
@@ -218,7 +211,7 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_CLOCK_CARD_EN);
}
-static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
+static int dove_sdhci_mci_init(struct mci_host *mci, struct device *dev)
{
u64 start;
struct dove_sdhci *host = priv_from_mci_host(mci);
@@ -271,7 +264,7 @@ static void dove_sdhci_set_mci_caps(struct dove_sdhci *host)
host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA;
}
-static int dove_sdhci_probe(struct device_d *dev)
+static int dove_sdhci_probe(struct device *dev)
{
struct dove_sdhci *host;
int ret;
@@ -298,8 +291,9 @@ static struct of_device_id dove_sdhci_dt_ids[] = {
{ .compatible = "marvell,dove-sdhci", },
{ }
};
+MODULE_DEVICE_TABLE(of, dove_sdhci_dt_ids);
-static struct driver_d dove_sdhci_driver = {
+static struct driver dove_sdhci_driver = {
.name = "dove-sdhci",
.probe = dove_sdhci_probe,
.of_compatible = DRV_OF_COMPAT(dove_sdhci_dt_ids),
diff --git a/drivers/mci/dw_mmc.c b/drivers/mci/dw_mmc.c
index 86c4f43e88..c49e839c94 100644
--- a/drivers/mci/dw_mmc.c
+++ b/drivers/mci/dw_mmc.c
@@ -26,7 +26,7 @@
struct dwmci_host {
struct mci_host mci;
- struct device_d *dev;
+ struct device *dev;
struct clk *clk_biu, *clk_ciu;
void *ioaddr;
unsigned int fifo_size_bytes;
@@ -496,7 +496,7 @@ static int dwmci_card_present(struct mci_host *mci)
return 1;
}
-static int dwmci_init(struct mci_host *mci, struct device_d *dev)
+static int dwmci_init(struct mci_host *mci, struct device *dev)
{
struct dwmci_host *host = to_dwmci_host(mci);
uint32_t fifo_size;
@@ -528,8 +528,8 @@ static int dwmci_init(struct mci_host *mci, struct device_d *dev)
/*
* If fifo-depth property is set, use this value
*/
- if (!of_property_read_u32(host->mci.hw_dev->device_node,
- "fifo-depth", &fifo_size)) {
+ if (!of_property_read_u32(host->mci.hw_dev->of_node,
+ "fifo-depth", &fifo_size)) {
host->fifo_size_bytes = fifo_size;
dev_dbg(host->mci.hw_dev, "Using fifo-depth=%u\n",
host->fifo_size_bytes);
@@ -547,7 +547,7 @@ static int dwmci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
-static int dw_mmc_probe(struct device_d *dev)
+static int dw_mmc_probe(struct device *dev)
{
struct reset_control *rst;
struct resource *iores;
@@ -570,7 +570,7 @@ static int dw_mmc_probe(struct device_d *dev)
clk_enable(host->clk_biu);
clk_enable(host->clk_ciu);
- rst = reset_control_get(dev, "reset");
+ rst = reset_control_get_optional(dev, "reset");
if (IS_ERR(rst)) {
dev_warn(dev, "error claiming reset: %pe\n", rst);
} else if (rst) {
@@ -603,22 +603,23 @@ static int dw_mmc_probe(struct device_d *dev)
host->ciu_div = pdata->ciu_div;
host->mci.host_caps &= ~MMC_CAP_BIT_DATA_MASK;
host->mci.host_caps |= pdata->bus_width_caps;
- } else if (dev->device_node) {
- of_property_read_u32(dev->device_node, "dw-mshc-ciu-div",
- &host->ciu_div);
+ } else if (dev->of_node) {
+ of_property_read_u32(dev->of_node, "dw-mshc-ciu-div",
+ &host->ciu_div);
}
/* divider is 0 based in pdata and 1 based in our private struct */
host->ciu_div++;
- if (of_device_is_compatible(dev->device_node,
- "rockchip,rk2928-dw-mshc"))
+ if (of_device_is_compatible(dev->of_node,
+ "rockchip,rk2928-dw-mshc"))
host->pwren_value = 0;
else
host->pwren_value = 1;
- if (of_device_is_compatible(dev->device_node, "starfive,jh7100-dw-mshc"))
- of_property_read_u32(dev->device_node, "clock-frequency", &host->clkrate);
+ if (of_device_is_compatible(dev->of_node, "starfive,jh7100-dw-mshc"))
+ of_property_read_u32(dev->of_node, "clock-frequency",
+ &host->clkrate);
if (!host->clkrate)
host->clkrate = clk_get_rate(host->clk_ciu);
@@ -647,8 +648,9 @@ static __maybe_unused struct of_device_id dw_mmc_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, dw_mmc_compatible);
-static struct driver_d dw_mmc_driver = {
+static struct driver dw_mmc_driver = {
.name = "dw_mmc",
.probe = dw_mmc_probe,
.of_compatible = DRV_OF_COMPAT(dw_mmc_compatible),
diff --git a/drivers/mci/dwcmshc-sdhci.c b/drivers/mci/dwcmshc-sdhci.c
new file mode 100644
index 0000000000..7b367e02ee
--- /dev/null
+++ b/drivers/mci/dwcmshc-sdhci.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2019 Yann Sionneau, Kalray Inc.
+// SPDX-FileCopyrightText: 2023 Jules Maselbas, Kalray Inc.
+
+#include <clock.h>
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <dma.h>
+#include <malloc.h>
+#include <mci.h>
+#include <of_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include "sdhci.h"
+
+#define tx_delay_static_cfg(delay) (delay << 5)
+#define tx_tuning_clk_sel(delay) (delay)
+
+#define DWCMSHC_GPIO_OUT 0x34 /* offset from vendor specific area */
+#define CARD_STATUS_MASK (0x1e00)
+#define CARD_STATUS_TRAN (4 << 9)
+
+static int do_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data);
+
+struct dwcmshc_host {
+ struct mci_host mci;
+ struct sdhci sdhci;
+ int vendor_specific_area;
+ const struct dwcmshc_callbacks *cb;
+};
+
+struct dwcmshc_callbacks {
+ void (*init)(struct mci_host *mci, struct device *dev);
+};
+
+static inline struct dwcmshc_host *priv_from_mci_host(struct mci_host *h)
+{
+ return container_of(h, struct dwcmshc_host, mci);
+}
+
+static void mci_setup_cmd(struct mci_cmd *p, unsigned int cmd, unsigned int arg,
+ unsigned int response)
+{
+ p->cmdidx = cmd;
+ p->cmdarg = arg;
+ p->resp_type = response;
+}
+
+static int do_abort_sequence(struct mci_host *mci, struct mci_cmd *current_cmd)
+{
+ int ret = 0;
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ struct mci_cmd cmd;
+ u64 start;
+
+ mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at first cmd12!\n");
+ goto out;
+ }
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
+ MMC_RSP_R1);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at first cmd13!\n");
+ goto out;
+ }
+
+ if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN)
+ goto out; /* All is OK! */
+
+ mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at second cmd12!\n");
+ goto out;
+ }
+
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
+ MMC_RSP_R1);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at second cmd13!\n");
+ goto out;
+ }
+
+ if ((cmd.response[0] & CARD_STATUS_MASK) != CARD_STATUS_TRAN) {
+ dev_err(host->mci.hw_dev,
+ "Abort sequence failed to put card in TRAN state!\n");
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ /* Perform SW reset if in abort sequence */
+ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET,
+ SDHCI_RESET_DATA | SDHCI_RESET_CMD);
+ start = get_time_ns();
+ while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) != 0) {
+ if (is_timeout(start, 50 * MSECOND)) {
+ dev_err(host->mci.hw_dev,
+ "SDHCI data reset timeout\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ return do_abort_sequence(mci, cmd);
+ return do_send_cmd(mci, cmd, data);
+}
+
+static int do_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ dma_addr_t dma = SDHCI_NO_DMA;
+ u32 mask, command, xfer;
+ int ret;
+
+ /* Do not wait for CMD_INHIBIT_DAT on stop commands */
+ mask = SDHCI_CMD_INHIBIT_CMD;
+ if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
+ mask |= SDHCI_CMD_INHIBIT_DATA;
+
+ /* Wait for bus idle */
+ ret = wait_on_timeout(50 * MSECOND,
+ !(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
+ if (ret) {
+ dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
+ return -ETIMEDOUT;
+ }
+
+ if (data)
+ dev_dbg(host->mci.hw_dev, "cmd %d arg %d bcnt %d bsize %d\n",
+ cmd->cmdidx, cmd->cmdarg, data->blocks, data->blocksize);
+ else
+ dev_dbg(host->mci.hw_dev, "cmd %d arg %d\n", cmd->cmdidx, cmd->cmdarg);
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
+
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+ ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+ if (ret)
+ goto error;
+
+ sdhci_read_response(&host->sdhci, cmd);
+
+ ret = sdhci_transfer_data(&host->sdhci, data, dma);
+error:
+ if (ret) {
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ return ret;
+}
+
+static u16 dwcmshc_get_clock_divider(struct dwcmshc_host *host, u32 reqclk)
+{
+ u16 div;
+
+ for (div = 1; div < SDHCI_MAX_DIV_SPEC_300; div += 2)
+ if ((host->sdhci.max_clk / div) <= reqclk)
+ break;
+ div /= 2;
+
+ return div;
+}
+
+static void dwcmshc_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ u16 val;
+
+ dev_dbg(host->mci.hw_dev,
+ "%s: clock = %u, bus-width = %d, timing = %02x\n",
+ __func__, ios->clock, ios->bus_width, ios->timing);
+
+ /* stop clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+ if (!ios->clock)
+ return;
+
+ /* enable bus power */
+ val = SDHCI_BUS_VOLTAGE_330;
+ sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL, val | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ /* set bus width */
+ sdhci_set_bus_width(&host->sdhci, ios->bus_width);
+
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+ if (ios->clock > 26000000)
+ val |= SDHCI_CTRL_HISPD;
+ else
+ val &= ~SDHCI_CTRL_HISPD;
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
+
+ /* set bus clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+ val = dwcmshc_get_clock_divider(host, ios->clock);
+ val = SDHCI_CLOCK_INT_EN | SDHCI_FREQ_SEL(val) | ((val & 0x300) >> 2);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+ /* wait for internal clock stable */
+ if (wait_on_timeout(20 * MSECOND,
+ sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL)
+ & SDHCI_CLOCK_INT_STABLE)) {
+ dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n");
+ return;
+ }
+
+ /* enable bus clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_CLOCK_CARD_EN);
+}
+
+static int dwcmshc_mci_init(struct mci_host *mci, struct device *dev)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ u16 ctrl2;
+
+ /* reset mshci controller */
+ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
+
+ /* wait for reset completion */
+ if (wait_on_timeout(100 * MSECOND,
+ (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET)
+ & SDHCI_RESET_ALL) == 0)) {
+ dev_err(dev, "SDHCI reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ sdhci_write16(&host->sdhci, SDHCI_INT_ERROR_ENABLE, ~0);
+ sdhci_write16(&host->sdhci, SDHCI_INT_ENABLE, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0);
+
+ sdhci_enable_v4_mode(&host->sdhci);
+
+ dev_dbg(host->mci.hw_dev, "host version4: %s\n",
+ ctrl2 & SDHCI_CTRL_V4_MODE ? "enabled" : "disabled");
+
+ if (host->cb && host->cb->init)
+ host->cb->init(mci, dev);
+
+ return 0;
+}
+
+static int dwcmshc_detect(struct device *dev)
+{
+ struct dwcmshc_host *host = dev->priv;
+
+ return mci_detect_card(&host->mci);
+}
+
+static int dwcmshc_mci_card_present(struct mci_host *mci)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ u32 pstate;
+
+ pstate = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
+ return pstate & SDHCI_CARD_PRESENT;
+}
+
+static void dwcmshc_set_dma_mask(struct device *dev)
+{
+ struct dwcmshc_host *host = dev->priv;
+
+ if (host->sdhci.caps & SDHCI_CAN_64BIT_V4)
+ dma_set_mask(dev, DMA_BIT_MASK(64));
+ else
+ dma_set_mask(dev, DMA_BIT_MASK(32));
+}
+
+static int dwcmshc_probe(struct device *dev)
+{
+ const struct dwcmshc_callbacks *dwcmshc_cb =
+ of_device_get_match_data(dev);
+ struct dwcmshc_host *host;
+ struct resource *iores;
+ struct mci_host *mci;
+ struct clk *clk;
+ int ret;
+
+ host = xzalloc(sizeof(*host));
+ mci = &host->mci;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ ret = PTR_ERR(iores);
+ goto err_mem_req;
+ }
+
+ clk = clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto err_clk_get;
+ }
+ clk_enable(clk);
+
+ host->sdhci.base = IOMEM(iores->start);
+ host->sdhci.mci = mci;
+ host->sdhci.max_clk = clk_get_rate(clk);
+ host->cb = dwcmshc_cb;
+
+ mci->hw_dev = dev;
+ mci->init = dwcmshc_mci_init;
+ mci->set_ios = dwcmshc_mci_set_ios;
+ mci->send_cmd = dwcmshc_mci_send_cmd;
+ mci->card_present = dwcmshc_mci_card_present;
+
+ sdhci_setup_host(&host->sdhci);
+
+ mci->max_req_size = 0x8000;
+ /*
+ * Let's first initialize f_max to the DT clock freq
+ * Then mci_of_parse can override if with the content
+ * of the 'max-frequency' DT property if it is present.
+ * Then we can finish by computing the f_min.
+ */
+ mci->f_max = clk_get_rate(clk);
+ mci_of_parse(&host->mci);
+ mci->f_min = mci->f_max / SDHCI_MAX_DIV_SPEC_300;
+
+ dev->priv = host;
+ dev->detect = dwcmshc_detect;
+
+ dwcmshc_set_dma_mask(dev);
+
+ dev_dbg(host->mci.hw_dev, "host controller version: %u\n",
+ host->sdhci.version);
+
+ host->vendor_specific_area = sdhci_read32(&host->sdhci,
+ SDHCI_P_VENDOR_SPEC_AREA);
+ host->vendor_specific_area &= SDHCI_P_VENDOR_SPEC_AREA_MASK;
+
+ ret = mci_register(&host->mci);
+ if (ret)
+ goto err_register;
+
+ return ret;
+
+err_register:
+ clk_disable(clk);
+ clk_put(clk);
+err_clk_get:
+ release_region(iores);
+err_mem_req:
+ free(host);
+
+ return ret;
+}
+
+static void dwcmshc_coolidgev2_init(struct mci_host *mci, struct device *dev)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+
+ /* configure TX delay to set correct setup/hold for Coolidge V2 */
+ sdhci_write32(&host->sdhci,
+ host->vendor_specific_area + DWCMSHC_GPIO_OUT,
+ tx_delay_static_cfg(0xf) | tx_tuning_clk_sel(4));
+}
+
+struct dwcmshc_callbacks kalray_coolidgev2_callbacks = {
+ .init = dwcmshc_coolidgev2_init,
+};
+
+static struct of_device_id dwcmshc_dt_ids[] = {
+ { .compatible = "snps,dwcmshc-sdhci", },
+ { .compatible = "kalray,coolidge-v2-dwcmshc-sdhci", .data = &kalray_coolidgev2_callbacks },
+ { }
+};
+
+static struct driver dwcmshc_driver = {
+ .name = "dwcmshc-sdhci",
+ .probe = dwcmshc_probe,
+ .of_compatible = DRV_OF_COMPAT(dwcmshc_dt_ids),
+};
+device_platform_driver(dwcmshc_driver);
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 27293e44b7..3d93889143 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -59,19 +59,14 @@ static int esdhc_setup_data(struct fsl_esdhc_host *host, struct mci_data *data,
u32 wml_value;
wml_value = data->blocksize / 4;
+ if (wml_value > 0x80)
+ wml_value = 0x80;
- if (data->flags & MMC_DATA_READ) {
- if (wml_value > 0x10)
- wml_value = 0x10;
-
+ if (data->flags & MMC_DATA_READ)
esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_RD_WML_MASK, wml_value);
- } else {
- if (wml_value > 0x80)
- wml_value = 0x80;
-
+ else
esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_WR_WML_MASK,
wml_value << 16);
- }
host->sdhci.sdma_boundary = 0;
@@ -160,6 +155,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
mixctrl = xfertyp;
/* Keep the bits 22-25 of the register as is */
mixctrl |= (sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL) & (0xF << 22));
+ mixctrl |= mci_timing_is_ddr(host->sdhci.timing) ? MIX_CTRL_DDREN : 0;
sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
}
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index c81eba5b9d..5b1d9a3cf4 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -8,14 +8,20 @@
#include <linux/sizes.h>
#include <asm/sections.h>
#include <asm/cache.h>
-#include <mach/xload.h>
+#include <mach/imx/xload.h>
+#include <firmware.h>
+#include <asm/atf_common.h>
#ifdef CONFIG_ARCH_IMX
-#include <mach/atf.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx7-regs.h>
-#include <mach/imx8mq-regs.h>
-#include <mach/imx8mm-regs.h>
-#include <mach/imx-header.h>
+#include <mach/imx/atf.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx7-regs.h>
+#include <mach/imx/imx8mq-regs.h>
+#include <mach/imx/imx8mm-regs.h>
+#include <mach/imx/imx-header.h>
+#endif
+#ifdef CONFIG_ARCH_LAYERSCAPE
+#include <mach/layerscape/xload.h>
+#include <mach/layerscape/layerscape.h>
#endif
#include "sdhci.h"
#include "imx-esdhc.h"
@@ -101,122 +107,24 @@ static int esdhc_read_blocks(struct fsl_esdhc_host *host, void *dst, size_t len)
}
#ifdef CONFIG_ARCH_IMX
-static int esdhc_search_header(struct fsl_esdhc_host *host,
- struct imx_flash_header_v2 **header_pointer,
- void *buffer, u32 *offset, u32 ivt_offset)
+static int imx_read_blocks(void *dest, size_t len, void *priv)
{
- int ret;
- int i, header_count = 1;
- void *buf = buffer;
- struct imx_flash_header_v2 *hdr;
-
- for (i = 0; i < header_count; i++) {
- ret = esdhc_read_blocks(host, buf,
- *offset + ivt_offset + SECTOR_SIZE);
- if (ret)
- return ret;
-
- hdr = buf + *offset + ivt_offset;
-
- if (!is_imx_flash_header_v2(hdr)) {
- pr_debug("IVT header not found on SD card. "
- "Found tag: 0x%02x length: 0x%04x "
- "version: %02x\n",
- hdr->header.tag, hdr->header.length,
- hdr->header.version);
- return -EINVAL;
- }
-
- if (IS_ENABLED(CONFIG_ARCH_IMX8MQ) &&
- hdr->boot_data.plugin & PLUGIN_HDMI_IMAGE) {
- /*
- * In images that include signed HDMI
- * firmware, first v2 header would be
- * dedicated to that and would not contain any
- * useful for us information. In order for us
- * to pull the rest of the bootloader image
- * in, we need to re-read header from SD/MMC,
- * this time skipping anything HDMI firmware
- * related.
- */
- *offset += hdr->boot_data.size + hdr->header.length;
- header_count++;
- }
- }
- *header_pointer = hdr;
- return 0;
+ return esdhc_read_blocks(priv, dest, len);
}
static int
esdhc_load_image(struct fsl_esdhc_host *host, ptrdiff_t address,
ptrdiff_t entry, u32 offset, u32 ivt_offset, bool start)
{
-
- void *buf = (void *)address;
- struct imx_flash_header_v2 *hdr = NULL;
- int ret, len;
- void __noreturn (*bb)(void);
- unsigned int ofs;
-
- len = imx_image_size();
- len = ALIGN(len, SECTOR_SIZE);
-
- ret = esdhc_search_header(host, &hdr, buf, &offset, ivt_offset);
- if (ret)
- return ret;
-
- pr_debug("Check ok, loading image\n");
-
- ofs = offset + hdr->entry - hdr->boot_data.start;
-
- if (entry != address) {
- /*
- * Passing entry different from address is interpreted
- * as a request to place the image such that its entry
- * point would be exactly at 'entry', that is:
- *
- * buf + ofs = entry
- *
- * solving the above for 'buf' gives us the
- * adjustment that needs to be made:
- *
- * buf = entry - ofs
- *
- */
- if (WARN_ON(entry - ofs < address)) {
- /*
- * We want to make sure we won't try to place
- * the start of the image before the beginning
- * of the memory buffer we were given in
- * address.
- */
- return -EINVAL;
- }
-
- buf = (void *)(entry - ofs);
- }
-
- ret = esdhc_read_blocks(host, buf, offset + len);
- if (ret) {
- pr_err("Loading image failed with %d\n", ret);
- return ret;
- }
-
- pr_debug("Image loaded successfully\n");
-
- if (!start)
- return 0;
-
- bb = buf + ofs;
-
- sync_caches_for_execution();
-
- bb();
+ return imx_load_image(address, entry, offset, ivt_offset, start,
+ SECTOR_SIZE, imx_read_blocks, host);
}
static void imx_esdhc_init(struct fsl_esdhc_host *host,
struct esdhc_soc_data *data)
{
+ u32 mixctrl;
+
data->flags = ESDHC_FLAG_USDHC;
host->socdata = data;
esdhc_populate_sdhci(host);
@@ -226,6 +134,10 @@ static void imx_esdhc_init(struct fsl_esdhc_host *host,
FIELD_PREP(WML_WR_WML_MASK, SECTOR_WML) |
FIELD_PREP(WML_RD_BRST_LEN, 16) |
FIELD_PREP(WML_RD_WML_MASK, SECTOR_WML));
+
+ mixctrl = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+ if (mixctrl & MIX_CTRL_DDREN)
+ host->sdhci.timing = MMC_TIMING_MMC_DDR52;
}
static int imx8m_esdhc_init(struct fsl_esdhc_host *host,
@@ -330,17 +242,17 @@ int imx7_esdhc_start_image(int instance)
/**
* imx8m_esdhc_load_image - Load and optionally start an image from USDHC controller
* @instance: The USDHC controller instance (0..2)
- * @start: Whether to directly start the loaded image
+ * @bl33: Where to load the bl33 barebox image
*
* This uses esdhc_start_image() to load an image from SD/MMC. It is
* assumed that the image is the currently running barebox image (This
* information is used to calculate the length of the image). The
- * image is started afterwards.
+ * image is not started afterwards.
*
- * Return: If successful, this function does not return (if directly started)
- * or 0. A negative error code is returned when this function fails.
+ * Return: If image successfully loaded, returns 0.
+ * A negative error code is returned when this function fails.
*/
-int imx8m_esdhc_load_image(int instance, bool start)
+int imx8m_esdhc_load_image(int instance, void *bl33)
{
struct esdhc_soc_data data;
struct fsl_esdhc_host host = { 0 };
@@ -351,24 +263,24 @@ int imx8m_esdhc_load_image(int instance, bool start)
return ret;
return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR,
- MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K, SZ_1K,
- start);
+ (ptrdiff_t)bl33, SZ_32K, SZ_1K,
+ false);
}
/**
* imx8mp_esdhc_load_image - Load and optionally start an image from USDHC controller
* @instance: The USDHC controller instance (0..2)
- * @start: Whether to directly start the loaded image
+ * @bl33: Where to load the bl33 barebox image
*
* This uses esdhc_start_image() to load an image from SD/MMC. It is
* assumed that the image is the currently running barebox image (This
* information is used to calculate the length of the image). The
- * image is started afterwards.
+ * image is not started afterwards.
*
- * Return: If successful, this function does not return (if directly started)
- * or 0. A negative error code is returned when this function fails.
+ * Return: If image successfully loaded, returns 0.
+ * A negative error code is returned when this function fails.
*/
-int imx8mp_esdhc_load_image(int instance, bool start)
+int imx8mp_esdhc_load_image(int instance, void *bl33)
{
struct esdhc_soc_data data;
struct fsl_esdhc_host host = { 0 };
@@ -382,14 +294,47 @@ int imx8mp_esdhc_load_image(int instance, bool start)
offset = esdhc_bootpart_active(&host)? 0 : SZ_32K;
return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR,
- MX8MQ_ATF_BL33_BASE_ADDR, offset, 0, start);
+ (ptrdiff_t)bl33, offset, 0, false);
}
-int imx8mn_esdhc_load_image(int instance, bool start)
+int imx8mn_esdhc_load_image(int instance, void *bl33)
__alias(imx8mp_esdhc_load_image);
#endif
-#ifdef CONFIG_ARCH_LS1046
+#ifdef CONFIG_ARCH_LAYERSCAPE
+
+static int layerscape_esdhc_load_image(struct fsl_esdhc_host *host, void *adr, unsigned long size,
+ uint32_t div_val)
+{
+ uint32_t val;
+ int ret;
+
+ esdhc_populate_sdhci(host);
+ sdhci_write32(&host->sdhci, IMX_SDHCI_WML, 0);
+
+ /*
+ * The ROM leaves us here with a clock frequency of around 400kHz. Speed
+ * this up a bit. FIXME: The resulting frequency has not yet been verified
+ * to work on all cards.
+ */
+ val = sdhci_read32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
+ val &= ~0x0000fff0;
+ val |= div_val;
+ sdhci_write32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+
+ sdhci_write32(&host->sdhci, ESDHC_DMA_SYSCTL,
+ ESDHC_SYSCTL_DMA_SNOOP | ESDHC_SYSCTL_PERIPHERAL_CLK_SEL);
+
+ ret = esdhc_read_blocks(host, adr, size);
+ if (ret) {
+ pr_err("%s: reading blocks failed with: %d\n", __func__, ret);
+ return ret;
+ }
+
+ sync_caches_for_execution();
+
+ return 0;
+}
/*
* The image on the SD card starts at 0x1000. We reserved 128KiB for the PBL,
@@ -410,46 +355,76 @@ int imx8mn_esdhc_load_image(int instance, bool start)
int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long r2)
{
int ret;
- uint32_t val;
struct esdhc_soc_data data = {
- .flags = ESDHC_FLAG_BIGENDIAN,
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN,
};
struct fsl_esdhc_host host = {
.sdhci.base = IOMEM(0x01560000),
.socdata = &data,
};
- unsigned long sdram = 0x80000000;
+ void *sdram = (void *)0x80000000;
+ unsigned long size = ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512);
void (*barebox)(unsigned long, unsigned long, unsigned long) =
- (void *)(sdram + LS1046A_SD_IMAGE_OFFSET);
+ (sdram + LS1046A_SD_IMAGE_OFFSET);
- esdhc_populate_sdhci(&host);
- sdhci_write32(&host.sdhci, IMX_SDHCI_WML, 0);
+ ret = layerscape_esdhc_load_image(&host, sdram, size, (8 << 8) | (3 << 4));
+ if (ret)
+ return ret;
- /*
- * The ROM leaves us here with a clock frequency of around 400kHz. Speed
- * this up a bit. FIXME: The resulting frequency has not yet been verified
- * to work on all cards.
- */
- val = sdhci_read32(&host.sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
- val &= ~0x0000fff0;
- val |= (8 << 8) | (3 << 4);
- sdhci_write32(&host.sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+ printf("Starting barebox\n");
- sdhci_write32(&host.sdhci, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
+ barebox(r0, r1, r2);
- ret = esdhc_read_blocks(&host, (void *)sdram,
- ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512));
- if (ret) {
- pr_err("%s: reading blocks failed with: %d\n", __func__, ret);
+ return -EINVAL;
+}
+
+static int ls1028a_esdhc_start_image(void __iomem *base, struct dram_regions_info *dram_info)
+{
+ struct esdhc_soc_data data = {
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT,
+ };
+ struct fsl_esdhc_host host = {
+ .sdhci.base = base,
+ .socdata = &data,
+ };
+ void *sdram = (void *)0x80000000;
+ void (*bl31)(void) = (void *)LS1028A_TFA_RESERVED_START;
+ size_t bl31_size;
+ void *bl31_image;
+ struct bl2_to_bl31_params_mem_v2 *params;
+ unsigned long size = ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512);
+ void (*barebox)(unsigned long, unsigned long, unsigned long) =
+ (sdram + LS1046A_SD_IMAGE_OFFSET);
+ int ret;
+
+ ret = layerscape_esdhc_load_image(&host, sdram, size, 8 << 4);
+ if (ret)
return ret;
- }
- sync_caches_for_execution();
+ get_builtin_firmware_ext(ls1028a_bl31_bin, barebox, &bl31_image, &bl31_size);
+ memcpy(bl31, bl31_image, bl31_size);
- printf("Starting barebox\n");
+ /* Setup an initial stack for EL2 */
+ asm volatile("msr sp_el2, %0" : : "r" ((unsigned long)barebox - 16) : "cc");
- barebox(r0, r1, r2);
+ params = bl2_plat_get_bl31_params_v2(0, (uintptr_t)barebox, 0);
+ params->bl31_ep_info.args.arg3 = (unsigned long)dram_info;
+
+ printf("Starting bl31\n");
+
+ bl31_entry_v2((uintptr_t)bl31, &params->bl_params, NULL);
return -EINVAL;
}
+
+int ls1028a_esdhc1_start_image(struct dram_regions_info *dram_info)
+{
+ return ls1028a_esdhc_start_image(IOMEM(0x2140000), dram_info);
+}
+
+int ls1028a_esdhc2_start_image(struct dram_regions_info *dram_info)
+{
+ return ls1028a_esdhc_start_image(IOMEM(0x2150000), dram_info);
+}
+
#endif
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 9dcad3bb5d..fb52c7b893 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -21,6 +21,8 @@
#include <platform_data/mmc-esdhc-imx.h>
#include <gpio.h>
#include <of_device.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx53-regs.h>
#include "sdhci.h"
#include "imx-esdhc.h"
@@ -43,33 +45,35 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
return __esdhc_send_cmd(host, cmd, data);
}
-static void set_sysctl(struct mci_host *mci, u32 clock)
+static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
{
- int div, pre_div;
+ int div, pre_div, ddr_pre_div = 1;
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
int sdhc_clk = clk_get_rate(host->clk);
u32 clk;
unsigned long cur_clock;
- /*
- * With eMMC and imx53 (sdhc_clk=200MHz) a pre_div of 1 results in
- * pre_div=1,div=4 (=50MHz)
- * which is valid and should work, but somehow doesn't.
- * Starting with pre_div=2 gives
- * pre_div=2, div=2 (=50MHz)
- * and works fine.
- */
- pre_div = 2;
+ if (esdhc_is_usdhc(host) && ddr)
+ ddr_pre_div = 2;
+
+ if (esdhc_is_layerscape(host))
+ sdhc_clk >>= 1;
+
+ /* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
+ if (cpu_is_mx53() && host->sdhci.base == (void *)MX53_ESDHC3_BASE_ADDR)
+ pre_div = 2;
+ else
+ pre_div = 1;
if (sdhc_clk == clock)
pre_div = 1;
else if (sdhc_clk / 16 > clock)
for (; pre_div < 256; pre_div *= 2)
- if ((sdhc_clk / pre_div) <= (clock * 16))
+ if ((sdhc_clk / (pre_div * ddr_pre_div)) <= (clock * 16))
break;
for (div = 1; div <= 16; div++)
- if ((sdhc_clk / (div * pre_div)) <= clock)
+ if ((sdhc_clk / (div * pre_div * ddr_pre_div)) <= clock)
break;
cur_clock = sdhc_clk / pre_div / div;
@@ -103,12 +107,70 @@ static void set_sysctl(struct mci_host *mci, u32 clock)
10 * MSECOND);
}
+static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing)
+{
+ u32 mixctrl;
+
+ mixctrl = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+ mixctrl &= ~MIX_CTRL_DDREN;
+
+ switch (timing) {
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ mixctrl |= MIX_CTRL_DDREN;
+ sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+ break;
+ default:
+ sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+ }
+
+ host->sdhci.timing = timing;
+}
+
+static void layerscape_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing)
+{
+ esdhc_clrbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ SYSCTL_CKEN);
+
+ switch (timing) {
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ esdhc_clrsetbits32(host, SDHCI_ACMD12_ERR__HOST_CONTROL2,
+ SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM,
+ FIELD_PREP(SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM, 4));
+ break;
+ default:
+ esdhc_clrbits32(host, SDHCI_ACMD12_ERR__HOST_CONTROL2,
+ SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM);
+ break;
+ }
+
+ esdhc_setbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ SYSCTL_CKEN);
+
+ host->sdhci.timing = timing;
+}
+
static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
+ /*
+ * call esdhc_set_timing() before update the clock rate,
+ * This is because current we support DDR and SDR timing,
+ * Once the DDR_EN bit is set, the card clock will be
+ * divide by 2 automatically. So need to do this before
+ * setting clock rate.
+ */
+ if (host->sdhci.timing != ios->timing) {
+ if (esdhc_is_usdhc(host))
+ usdhc_set_timing(host, ios->timing);
+ else if (esdhc_is_layerscape(host))
+ layerscape_set_timing(host, ios->timing);
+ }
+
/* Set the clock speed */
- set_sysctl(mci, ios->clock);
+ set_sysctl(mci, ios->clock, mci_timing_is_ddr(ios->timing));
/* Set the bus width */
esdhc_clrbits32(host, SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
@@ -186,7 +248,7 @@ static int esdhc_reset(struct fsl_esdhc_host *host)
return 0;
}
-static int esdhc_init(struct mci_host *mci, struct device_d *dev)
+static int esdhc_init(struct mci_host *mci, struct device *dev)
{
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
int ret;
@@ -201,12 +263,13 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
/* RSTA doesn't reset MMC_BOOT register, so manually reset it */
sdhci_write32(&host->sdhci, SDHCI_MMC_BOOT, 0);
- /* Enable cache snooping */
- if (host->socdata->flags & ESDHC_FLAG_CACHE_SNOOPING)
- esdhc_setbits32(host, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
+ if (esdhc_is_layerscape(host))
+ esdhc_setbits32(host, ESDHC_DMA_SYSCTL,
+ ESDHC_SYSCTL_DMA_SNOOP | /* Enable cache snooping */
+ ESDHC_SYSCTL_PERIPHERAL_CLK_SEL);
/* Set the initial clock speed */
- set_sysctl(mci, 400000);
+ set_sysctl(mci, 400000, false);
sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, SDHCI_INT_CMD_COMPLETE |
SDHCI_INT_XFER_COMPLETE | SDHCI_INT_CARD_INT |
@@ -225,7 +288,7 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
return ret;
}
-static int fsl_esdhc_probe(struct device_d *dev)
+static int fsl_esdhc_probe(struct device *dev)
{
struct resource *iores;
struct fsl_esdhc_host *host;
@@ -285,6 +348,9 @@ static int fsl_esdhc_probe(struct device_d *dev)
if (ret)
goto err_clk_disable;
+ if (esdhc_is_usdhc(host) || esdhc_is_layerscape(host))
+ mci->host_caps |= MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR;
+
rate = clk_get_rate(host->clk);
host->mci.f_min = rate >> 12;
if (host->mci.f_min < 200000)
@@ -342,9 +408,13 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
.clkidx = "per",
};
-static struct esdhc_soc_data esdhc_ls_data = {
+static struct esdhc_soc_data esdhc_ls_be_data = {
.flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN |
- ESDHC_FLAG_CACHE_SNOOPING,
+ ESDHC_FLAG_LAYERSCAPE,
+};
+
+static struct esdhc_soc_data esdhc_ls_le_data = {
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_LAYERSCAPE,
};
static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = {
@@ -359,9 +429,11 @@ static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = {
{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx6sx_data },
{ .compatible = "fsl,imx8mn-usdhc", .data = &usdhc_imx6sx_data },
{ .compatible = "fsl,imx8mp-usdhc", .data = &usdhc_imx6sx_data },
- { .compatible = "fsl,ls1046a-esdhc",.data = &esdhc_ls_data },
+ { .compatible = "fsl,ls1028a-esdhc",.data = &esdhc_ls_le_data },
+ { .compatible = "fsl,ls1046a-esdhc",.data = &esdhc_ls_be_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_esdhc_compatible);
static struct platform_device_id imx_esdhc_ids[] = {
{
@@ -375,7 +447,7 @@ static struct platform_device_id imx_esdhc_ids[] = {
}
};
-static struct driver_d fsl_esdhc_driver = {
+static struct driver fsl_esdhc_driver = {
.name = "imx-esdhc",
.probe = fsl_esdhc_probe,
.of_compatible = DRV_OF_COMPAT(fsl_esdhc_compatible),
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index f1685eac06..eff556f2ff 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -39,14 +39,30 @@
#define PIO_TIMEOUT 100000
+#define SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM GENMASK(18, 16) /* Layerscape specific */
+
#define IMX_SDHCI_WML 0x44
#define IMX_SDHCI_MIXCTRL 0x48
+/* Imported from Linux Kernel drivers/mmc/host/sdhci-esdhc-imx.c */
+#define MIX_CTRL_DDREN BIT(3)
+#define MIX_CTRL_DTDSEL_READ BIT(4)
+#define MIX_CTRL_AC23EN BIT(7)
+#define MIX_CTRL_EXE_TUNE BIT(22)
+#define MIX_CTRL_SMPCLK_SEL BIT(23)
+#define MIX_CTRL_AUTO_TUNE_EN BIT(24)
+#define MIX_CTRL_FBCLK_SEL BIT(25)
+#define MIX_CTRL_HS400_EN BIT(26)
+#define MIX_CTRL_HS400_ES BIT(27)
+/* Bits 3 and 6 are not SDHCI standard definitions */
+#define MIX_CTRL_SDHCI_MASK 0xb7
+/* Tuning bits */
+#define MIX_CTRL_TUNING_MASK 0x03c00000
#define IMX_SDHCI_DLL_CTRL 0x60
#define IMX_SDHCI_MIX_CTRL_FBCLK_SEL BIT(25)
#define ESDHC_DMA_SYSCTL 0x40c /* Layerscape specific */
#define ESDHC_SYSCTL_DMA_SNOOP BIT(6)
-
+#define ESDHC_SYSCTL_PERIPHERAL_CLK_SEL BIT(19)
/*
* The CMDTYPE of the CMD register (offset 0xE) should be set to
@@ -89,8 +105,8 @@
#define ESDHC_FLAG_HS400 BIT(9)
/* Need to access registers in bigendian mode */
#define ESDHC_FLAG_BIGENDIAN BIT(10)
-/* Enable cache snooping */
-#define ESDHC_FLAG_CACHE_SNOOPING BIT(11)
+/* Layerscape variant ls1046a, ls1028a, ls1088a, revisit for ls1012a */
+#define ESDHC_FLAG_LAYERSCAPE BIT(11)
struct esdhc_soc_data {
u32 flags;
@@ -100,7 +116,7 @@ struct esdhc_soc_data {
struct fsl_esdhc_host {
struct mci_host mci;
struct clk *clk;
- struct device_d *dev;
+ struct device *dev;
const struct esdhc_soc_data *socdata;
struct sdhci sdhci;
};
@@ -110,6 +126,11 @@ static inline int esdhc_is_usdhc(struct fsl_esdhc_host *data)
return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
}
+static inline int esdhc_is_layerscape(struct fsl_esdhc_host *data)
+{
+ return !!(data->socdata->flags & ESDHC_FLAG_LAYERSCAPE);
+}
+
static inline struct fsl_esdhc_host *sdhci_to_esdhc(struct sdhci *sdhci)
{
return container_of(sdhci, struct fsl_esdhc_host, sdhci);
diff --git a/drivers/mci/imx.c b/drivers/mci/imx.c
index 9910b55ed5..48a3378335 100644
--- a/drivers/mci/imx.c
+++ b/drivers/mci/imx.c
@@ -465,7 +465,7 @@ static void mxcmci_set_ios(struct mci_host *mci, struct mci_ios *ios)
host->clock = ios->clock;
}
-static int mxcmci_init(struct mci_host *mci, struct device_d *dev)
+static int mxcmci_init(struct mci_host *mci, struct device *dev)
{
struct mxcmci_host *host = to_mxcmci(mci);
@@ -486,7 +486,7 @@ static int mxcmci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
-static int mxcmci_probe(struct device_d *dev)
+static int mxcmci_probe(struct device *dev)
{
struct resource *iores;
struct mxcmci_host *host;
@@ -527,8 +527,9 @@ static __maybe_unused struct of_device_id mxcmci_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mxcmci_compatible);
-static struct driver_d mxcmci_driver = {
+static struct driver mxcmci_driver = {
.name = DRIVER_NAME,
.probe = mxcmci_probe,
.of_compatible = DRV_OF_COMPAT(mxcmci_compatible),
diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c
index f092156f9a..3546cc3a32 100644
--- a/drivers/mci/mci-bcm2835.c
+++ b/drivers/mci/mci-bcm2835.c
@@ -30,7 +30,7 @@ static int twoticks_delay;
struct bcm2835_mci_host {
struct mci_host mci;
void __iomem *regs;
- struct device_d *hw_dev;
+ struct device *hw_dev;
int bus_width;
u32 clock;
u32 max_clock;
@@ -118,7 +118,6 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data) {
u32 command, block_data = 0, transfer_mode = 0;
int ret;
- u32 wait_inhibit_mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
struct bcm2835_mci_host *host = to_bcm2835_host(mci);
sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false,
@@ -129,15 +128,9 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
block_data |= data->blocksize;
}
- /* We shouldn't wait for data inihibit for stop commands, even
- though they might use busy signaling */
- if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
- wait_inhibit_mask = SDHCI_CMD_INHIBIT_CMD;
-
- /*Wait for old command*/
- while (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE)
- & wait_inhibit_mask)
- ;
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, 0xFFFFFFFF);
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
@@ -148,7 +141,7 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
command << 16 | transfer_mode);
ret = bcm2835_mci_wait_command_done(host);
- if (ret) {
+ if (ret && ret != -ETIMEDOUT) {
dev_err(host->hw_dev, "Error while executing command %d\n",
cmd->cmdidx);
dev_err(host->hw_dev, "Status: 0x%X, Interrupt: 0x%X\n",
@@ -232,6 +225,13 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL);
switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ sdhci_write32(&host->sdhci,
+ SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
+ (current_val & ~CONTROL0_4DATA) | CONTROL0_8DATA);
+ host->bus_width = 2;
+ dev_dbg(host->hw_dev, "Changing bus width to 8\n");
+ break;
case MMC_BUS_WIDTH_4:
sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
@@ -301,7 +301,7 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
host->bus_width, host->clock);
}
-static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+static int bcm2835_mci_reset(struct mci_host *mci, struct device *mci_dev)
{
struct bcm2835_mci_host *host;
u32 ret = 0;
@@ -353,7 +353,7 @@ static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
return 0;
}
-static int bcm2835_mci_probe(struct device_d *hw_dev)
+static int bcm2835_mci_probe(struct device *hw_dev)
{
struct resource *iores;
struct bcm2835_mci_host *host;
@@ -429,8 +429,9 @@ static __maybe_unused struct of_device_id bcm2835_mci_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, bcm2835_mci_compatible);
-static struct driver_d bcm2835_mci_driver = {
+static struct driver bcm2835_mci_driver = {
.name = "bcm2835_mci",
.probe = bcm2835_mci_probe,
.of_compatible = DRV_OF_COMPAT(bcm2835_mci_compatible),
diff --git a/drivers/mci/mci-bcm2835.h b/drivers/mci/mci-bcm2835.h
index 608764c23d..71448642ad 100644
--- a/drivers/mci/mci-bcm2835.h
+++ b/drivers/mci/mci-bcm2835.h
@@ -10,6 +10,7 @@
#define CONTROL0_HISPEED (1 << 2)
#define CONTROL0_4DATA (1 << 1)
+#define CONTROL0_8DATA (1 << 5)
#define CONTROL1_DATARST (1 << 26)
#define CONTROL1_CMDRST (1 << 25)
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index a1e27b9543..1d383e6449 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -75,6 +75,28 @@ static int mci_send_cmd(struct mci *mci, struct mci_cmd *cmd, struct mci_data *d
}
/**
+ * mci_send_cmd_retry() - send a command to the mmc device, retrying on error
+ *
+ * @dev: device to receive the command
+ * @cmd: command to send
+ * @data: additional data to send/receive
+ * @retries: how many times to retry; mci_send_cmd is always called at least
+ * once
+ * Return: 0 if ok, -ve on error
+ */
+static int mci_send_cmd_retry(struct mci *mci, struct mci_cmd *cmd,
+ struct mci_data *data, unsigned retries)
+{
+ int ret;
+
+ do
+ ret = mci_send_cmd(mci, cmd, data);
+ while (ret && retries--);
+
+ return ret;
+}
+
+/**
* @param p Command definition to setup
* @param cmd Valid SD/MMC command (refer MMC_CMD_* / SD_CMD_*)
* @param arg Argument for the command (optional)
@@ -113,12 +135,105 @@ static int mci_set_blocklen(struct mci *mci, unsigned len)
{
struct mci_cmd cmd;
+ if (mci->host->timing == MMC_TIMING_MMC_DDR52)
+ return 0;
+
mci_setup_cmd(&cmd, MMC_CMD_SET_BLOCKLEN, len, MMC_RSP_R1);
return mci_send_cmd(mci, &cmd, NULL);
}
static void *sector_buf;
+static int mci_send_status(struct mci *mci, unsigned int *status)
+{
+ struct mci_host *host = mci->host;
+ struct mci_cmd cmd;
+ int ret;
+
+ /*
+ * While CMD13 is defined for SPI mode, the reported status bits have
+ * different layout that SD/MMC. We skip supporting this for now.
+ */
+ if (mmc_host_is_spi(host))
+ return -ENOSYS;
+
+ cmd.cmdidx = MMC_CMD_SEND_STATUS;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = mci->rca << 16;
+
+ ret = mci_send_cmd_retry(mci, &cmd, NULL, 4);
+ if (!ret)
+ *status = cmd.response[0];
+
+ return ret;
+}
+
+static int mmc_switch_status_error(struct mci_host *host, u32 status)
+{
+ if (mmc_host_is_spi(host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (R1_STATUS(status))
+ pr_warn("unexpected status %#x after switch\n", status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+/* Caller must hold re-tuning */
+int mci_switch_status(struct mci *mci, bool crc_err_fatal)
+{
+ u32 status;
+ int err;
+
+ err = mci_send_status(mci, &status);
+ if (!crc_err_fatal && err == -EILSEQ)
+ return 0;
+ if (err)
+ return err;
+
+ return mmc_switch_status_error(mci->host, status);
+}
+
+static int mci_poll_until_ready(struct mci *mci, int timeout_ms)
+{
+ unsigned int status;
+ int err, retries = 0;
+
+ while (1) {
+ err = mci_send_status(mci, &status);
+ if (err)
+ return err;
+
+ /*
+ * Some cards mishandle the status bits, so make sure to
+ * check both the busy indication and the card state.
+ */
+ if ((status & R1_READY_FOR_DATA) &&
+ R1_CURRENT_STATE(status) != R1_STATE_PRG)
+ break;
+
+ if (status & R1_STATUS_MASK) {
+ dev_err(&mci->dev, "Status Error: 0x%08x\n", status);
+ return -EIO;
+ }
+
+ if (retries++ == timeout_ms) {
+ dev_err(&mci->dev, "Timeout awaiting card ready\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1000);
+ }
+
+ dev_dbg(&mci->dev, "Ready polling took %ums\n", retries);
+
+ return 0;
+}
+
+
/**
* Write one or several blocks of data to the card
* @param mci_dev MCI instance
@@ -132,28 +247,31 @@ static int mci_block_write(struct mci *mci, const void *src, int blocknum,
{
struct mci_cmd cmd;
struct mci_data data;
- const void *buf;
unsigned mmccmd;
int ret;
+ /*
+ * Quoting eMMC Spec v5.1 (JEDEC Standard No. 84-B51):
+ * Due to legacy reasons, a Device may still treat CMD24/25 during
+ * prg-state (while busy is active) as a legal or illegal command.
+ * A host should not send CMD24/25 while the Device is in the prg
+ * state and busy is active.
+ */
+ ret = mci_poll_until_ready(mci, 1000 /* ms */);
+ if (ret && ret != -ENOSYS)
+ return ret;
+
if (blocks > 1)
mmccmd = MMC_CMD_WRITE_MULTIPLE_BLOCK;
else
mmccmd = MMC_CMD_WRITE_SINGLE_BLOCK;
- if ((unsigned long)src & 0x3) {
- memcpy(sector_buf, src, SECTOR_SIZE);
- buf = sector_buf;
- } else {
- buf = src;
- }
-
mci_setup_cmd(&cmd,
mmccmd,
mci->high_capacity != 0 ? blocknum : blocknum * mci->write_bl_len,
MMC_RSP_R1);
- data.src = buf;
+ data.src = src;
data.blocks = blocks;
data.blocksize = mci->write_bl_len;
data.flags = MMC_DATA_WRITE;
@@ -201,7 +319,8 @@ static int mci_read_block(struct mci *mci, void *dst, int blocknum,
ret = mci_send_cmd(mci, &cmd, &data);
if (ret || blocks > 1) {
- mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+ mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0,
+ IS_SD(mci) ? MMC_RSP_R1b : MMC_RSP_R1);
mci_send_cmd(mci, &cmd, NULL);
}
return ret;
@@ -232,6 +351,15 @@ static int mci_go_idle(struct mci *mci)
return 0;
}
+static int sdio_send_op_cond(struct mci *mci)
+{
+ struct mci_cmd cmd;
+
+ mci_setup_cmd(&cmd, SD_IO_SEND_OP_COND, 0, MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR);
+
+ return mci_send_cmd(mci, &cmd, NULL);
+}
+
/**
* FIXME
* @param mci MCI instance
@@ -344,7 +472,7 @@ static int mmc_send_op_cond(struct mci *mci)
mci->ocr = cmd.response[0];
mci->high_capacity = ((mci->ocr & OCR_HCS) == OCR_HCS);
- mci->rca = 0;
+ mci->rca = 2;
return 0;
}
@@ -388,7 +516,9 @@ int mci_send_ext_csd(struct mci *mci, char *ext_csd)
*/
int mci_switch(struct mci *mci, unsigned index, unsigned value)
{
+ unsigned int status;
struct mci_cmd cmd;
+ int ret;
mci_setup_cmd(&cmd, MMC_CMD_SWITCH,
(MMC_SWITCH_MODE_WRITE_BYTE << 24) |
@@ -396,7 +526,35 @@ int mci_switch(struct mci *mci, unsigned index, unsigned value)
(value << 8),
MMC_RSP_R1b);
- return mci_send_cmd(mci, &cmd, NULL);
+ ret = mci_send_cmd(mci, &cmd, NULL);
+ if (ret)
+ return ret;
+
+ ret = mci_send_status(mci, &status);
+ if (ret)
+ return ret;
+
+ if (status & R1_SWITCH_ERROR)
+ return -EIO;
+
+ return 0;
+}
+
+u8 *mci_get_ext_csd(struct mci *mci)
+{
+ u8 *ext_csd;
+ int ret;
+
+ ext_csd = dma_alloc(512);
+
+ ret = mci_send_ext_csd(mci, ext_csd);
+ if (ret) {
+ printf("Failure to read EXT_CSD register\n");
+ dma_free(ext_csd);
+ return ERR_PTR(-EIO);
+ }
+
+ return ext_csd;
}
static blkcnt_t mci_calc_blk_cnt(blkcnt_t cap, unsigned shift)
@@ -428,7 +586,7 @@ static void mci_part_add(struct mci *mci, uint64_t size,
part->idx = idx;
if (area_type == MMC_BLK_DATA_AREA_MAIN) {
- part->blk.cdev.device_node = mci->host->hw_dev->device_node;
+ cdev_set_of_node(&part->blk.cdev, mci->host->hw_dev->of_node);
part->blk.cdev.flags |= DEVFS_IS_MCI_MAIN_PART_DEV;
}
@@ -508,7 +666,7 @@ static int mmc_change_freq(struct mci *mci)
char cardtype;
int err;
- mci->ext_csd = xmalloc(512);
+ mci->ext_csd = dma_alloc(512);
mci->card_caps = 0;
/* Only version 4 supports high-speed */
@@ -525,7 +683,7 @@ static int mmc_change_freq(struct mci *mci)
cardtype = mci->ext_csd[EXT_CSD_DEVICE_TYPE] & EXT_CSD_CARD_TYPE_MASK;
- err = mci_switch(mci, EXT_CSD_HS_TIMING, 1);
+ err = mci_switch(mci, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
if (err) {
dev_dbg(&mci->dev, "MMC frequency changing failed: %d\n", err);
@@ -546,11 +704,15 @@ static int mmc_change_freq(struct mci *mci)
return 0;
}
- /* High Speed is set, there are two types: 52MHz and 26MHz */
- if (cardtype & EXT_CSD_CARD_TYPE_52)
- mci->card_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ | MMC_CAP_MMC_HIGHSPEED;
- else
- mci->card_caps |= MMC_CAP_MMC_HIGHSPEED;
+ mci->card_caps |= MMC_CAP_MMC_HIGHSPEED;
+
+ /* High Speed is set, there are three types: 26MHz, 52MHz, 52MHz DDR */
+ if (cardtype & EXT_CSD_CARD_TYPE_52) {
+ mci->card_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+ if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V)
+ mci->card_caps |= MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR;
+ }
if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
mci->ext_csd[EXT_CSD_REV] >= 3 && mci->ext_csd[EXT_CSD_BOOT_SIZE_MULT]) {
@@ -734,6 +896,8 @@ static void mci_set_ios(struct mci *mci)
};
host->set_ios(host, &ios);
+
+ host->actual_clock = host->clock;
}
/**
@@ -761,7 +925,7 @@ static void mci_set_clock(struct mci *mci, unsigned clock)
* @param mci MCI instance
* @param width New interface bit width (1, 4 or 8)
*/
-static void mci_set_bus_width(struct mci *mci, unsigned width)
+static void mci_set_bus_width(struct mci *mci, enum mci_bus_width width)
{
struct mci_host *host = mci->host;
@@ -952,7 +1116,7 @@ static void mci_extract_card_dsr_imp_from_csd(struct mci *mci)
mci->dsr_imp = UNSTUFF_BITS(mci->csd, 76, 1);
}
-static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
+static int mmc_compare_ext_csds(struct mci *mci, enum mci_bus_width bus_width)
{
u8 *bw_ext_csd;
int err;
@@ -960,7 +1124,7 @@ static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
if (bus_width == MMC_BUS_WIDTH_1)
return 0;
- bw_ext_csd = xmalloc(512);
+ bw_ext_csd = dma_alloc(512);
err = mci_send_ext_csd(mci, bw_ext_csd);
if (err) {
dev_info(&mci->dev, "mci_send_ext_csd failed with %d\n", err);
@@ -1009,7 +1173,7 @@ static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
0 : -EINVAL;
out:
- free(bw_ext_csd);
+ dma_free(bw_ext_csd);
return err;
}
@@ -1064,34 +1228,71 @@ static int mci_startup_sd(struct mci *mci)
return 0;
}
-static int mci_startup_mmc(struct mci *mci)
+static u32 mci_bus_width_ext_csd_bits(enum mci_bus_width bus_width)
{
- struct mci_host *host = mci->host;
+ switch (bus_width) {
+ case MMC_BUS_WIDTH_8:
+ return EXT_CSD_BUS_WIDTH_8;
+ case MMC_BUS_WIDTH_4:
+ return EXT_CSD_BUS_WIDTH_4;
+ case MMC_BUS_WIDTH_1:
+ default:
+ return EXT_CSD_BUS_WIDTH_1;
+ }
+}
+
+static int mci_mmc_try_bus_width(struct mci *mci, enum mci_bus_width bus_width,
+ enum mci_timing timing)
+{
+ u32 ext_csd_bits;
int err;
- int idx = 0;
- static unsigned ext_csd_bits[] = {
- EXT_CSD_BUS_WIDTH_4,
- EXT_CSD_BUS_WIDTH_8,
- };
- static unsigned bus_widths[] = {
- MMC_BUS_WIDTH_4,
- MMC_BUS_WIDTH_8,
- };
- /* if possible, speed up the transfer */
- if (mci_caps(mci) & MMC_CAP_MMC_HIGHSPEED) {
- if (mci->card_caps & MMC_CAP_MMC_HIGHSPEED_52MHZ)
- mci->tran_speed = 52000000;
- else
- mci->tran_speed = 26000000;
+ ext_csd_bits = mci_bus_width_ext_csd_bits(bus_width);
- host->timing = MMC_TIMING_MMC_HS;
+ if (mci_timing_is_ddr(timing))
+ ext_csd_bits |= EXT_CSD_DDR_FLAG;
+
+ err = mci_switch(mci, EXT_CSD_BUS_WIDTH, ext_csd_bits);
+ if (err < 0)
+ goto out;
+
+ mci->host->timing = timing;
+ mci_set_bus_width(mci, bus_width);
+
+ switch (bus_width) {
+ case MMC_BUS_WIDTH_8:
+ mci->card_caps |= MMC_CAP_8_BIT_DATA;
+ break;
+ case MMC_BUS_WIDTH_4:
+ mci->card_caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ default:
+ break;
}
- mci_set_clock(mci, mci->tran_speed);
+ err = mmc_compare_ext_csds(mci, bus_width);
+ if (err < 0)
+ goto out;
+
+out:
+ dev_dbg(&mci->dev, "Tried buswidth %u%s: %s\n", 1 << bus_width,
+ mci_timing_is_ddr(timing) ? " (DDR)" : "", err ? "failed" : "OK");
+
+ return err ?: bus_width;
+}
+
+static int mci_mmc_select_bus_width(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ int ret;
+ int idx = 0;
+ static enum mci_bus_width bus_widths[] = {
+ MMC_BUS_WIDTH_4,
+ MMC_BUS_WIDTH_8,
+ };
if (!(host->host_caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
- return 0;
+ return MMC_BUS_WIDTH_1;
/*
* Unlike SD, MMC cards dont have a configuration register to notify
@@ -1103,7 +1304,6 @@ static int mci_startup_mmc(struct mci *mci)
idx = 1;
for (; idx >= 0; idx--) {
-
/*
* Host is capable of 8bit transfer, then switch
* the device to work in 8bit transfer mode. If the
@@ -1111,23 +1311,264 @@ static int mci_startup_mmc(struct mci *mci)
* 4bit transfer mode. On success set the corresponding
* bus width on the host.
*/
- err = mci_switch(mci, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]);
- if (err) {
- if (idx == 0)
- dev_warn(&mci->dev, "Changing MMC bus width failed: %d\n", err);
- continue;
- }
+ ret = mci_mmc_try_bus_width(mci, bus_widths[idx], host->timing);
+ if (ret > 0)
+ break;
+ }
- mci_set_bus_width(mci, bus_widths[idx]);
+ return ret;
+}
- err = mmc_compare_ext_csds(mci, bus_widths[idx]);
- if (!err)
- break;
+static int mci_mmc_select_hs_ddr(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ int ret;
+
+ /*
+ * barebox MCI core does not change voltage, so we don't know here
+ * if we should check for the 1.8v or 3.3v mode. Until we support
+ * higher speed modes that require voltage switching like HS200/HS400,
+ * let's just check for either bit.
+ */
+ if (!(mci_caps(mci) & (MMC_CAP_MMC_1_8V_DDR | MMC_CAP_MMC_3_3V_DDR)))
+ return 0;
+
+ ret = mci_mmc_try_bus_width(mci, host->bus_width, MMC_TIMING_MMC_DDR52);
+ if (ret < 0)
+ return mci_mmc_try_bus_width(mci, host->bus_width, MMC_TIMING_MMC_HS);
+
+ /* Block length is fixed to 512 bytes while in DDR mode */
+ mci->read_bl_len = SECTOR_SIZE;
+ mci->write_bl_len = SECTOR_SIZE;
+
+ return 0;
+}
+
+int mci_execute_tuning(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ u32 opcode;
+
+ if (!host->execute_tuning)
+ return 0;
+
+ /* Tuning is only supported for MMC / HS200 */
+ if (mmc_card_hs200(mci))
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ return 0;
+
+ return host->execute_tuning(host, opcode);
+}
+
+int mci_send_abort_tuning(struct mci *mci, u32 opcode)
+{
+ struct mci_cmd cmd = {};
+
+ /*
+ * eMMC specification specifies that CMD12 can be used to stop a tuning
+ * command, but SD specification does not, so do nothing unless it is
+ * eMMC.
+ */
+ if (opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ return 0;
+
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.resp_type = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ return mci_send_cmd(mci, &cmd, NULL);
+}
+EXPORT_SYMBOL_GPL(mci_send_abort_tuning);
+
+static void mmc_select_max_dtr(struct mci *mci)
+{
+ u8 card_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE];
+ u32 caps2 = mci->host->caps2;
+ u32 caps = mci->card_caps;
+ unsigned int hs_max_dtr = 0;
+ unsigned int hs200_max_dtr = 0;
+
+ if ((caps & MMC_CAP_MMC_HIGHSPEED) &&
+ (card_type & EXT_CSD_CARD_TYPE_26)) {
+ hs_max_dtr = MMC_HIGH_26_MAX_DTR;
}
+ if ((caps & MMC_CAP_MMC_HIGHSPEED) &&
+ (card_type & EXT_CSD_CARD_TYPE_52)) {
+ hs_max_dtr = MMC_HIGH_52_MAX_DTR;
+ }
+
+ if ((caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
+ (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) {
+ hs200_max_dtr = MMC_HS200_MAX_DTR;
+ }
+
+ if ((caps2 & MMC_CAP2_HS200_1_2V_SDR) &&
+ (card_type & EXT_CSD_CARD_TYPE_HS200_1_2V)) {
+ hs200_max_dtr = MMC_HS200_MAX_DTR;
+ }
+
+ mci->host->hs200_max_dtr = hs200_max_dtr;
+ mci->host->hs_max_dtr = hs_max_dtr;
+}
+/*
+ * For device supporting HS200 mode, the following sequence
+ * should be done before executing the tuning process.
+ * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported)
+ * 2. switch to HS200 mode
+ * 3. set the clock to > 52Mhz and <=200MHz
+ */
+static int mmc_select_hs200(struct mci *mci)
+{
+ unsigned int old_timing, old_clock;
+ int err = -EINVAL;
+ u8 val;
+
+ /*
+ * Set the bus width(4 or 8) with host's support and
+ * switch to HS200 mode if bus width is set successfully.
+ */
+ /* find out maximum bus width and then try DDR if supported */
+ err = mci_mmc_select_bus_width(mci);
+ if (err > 0) {
+ /* TODO actually set drive strength instead of 0. Currently unsupported. */
+ val = EXT_CSD_TIMING_HS200 | 0 << EXT_CSD_DRV_STR_SHIFT;
+ err = mci_switch(mci, EXT_CSD_HS_TIMING, val);
+ if (err == -EIO)
+ return -EBADMSG;
+ if (err)
+ goto err;
+
+ /*
+ * Bump to HS timing and frequency. Some cards don't handle
+ * SEND_STATUS reliably at the initial frequency.
+ * NB: We can't move to full (HS200) speeds until after we've
+ * successfully switched over.
+ */
+ old_timing = mci->host->timing;
+ old_clock = mci->host->clock;
+
+ mci->host->timing = MMC_TIMING_MMC_HS200;
+ mci_set_ios(mci);
+ mci_set_clock(mci, mci->host->hs_max_dtr);
+
+ err = mci_switch_status(mci, true);
+
+ /*
+ * mmc_select_timing() assumes timing has not changed if
+ * it is a switch error.
+ */
+ if (err == -EBADMSG) {
+ mci->host->clock = old_clock;
+ mci->host->timing = old_timing;
+ mci_set_ios(mci);
+ }
+ }
+err:
+ if (err) {
+ dev_err(&mci->dev, "%s failed, error %d\n", __func__, err);
+ }
return err;
}
+/*
+ * Set the bus speed for the selected speed mode.
+ */
+static void mmc_set_bus_speed(struct mci *mci)
+{
+ unsigned int max_dtr = (unsigned int)-1;
+
+ if (mmc_card_hs200(mci) &&
+ max_dtr > mci->host->hs200_max_dtr)
+ max_dtr = mci->host->hs200_max_dtr;
+ else if (mmc_card_hs(mci) && max_dtr > mci->host->hs_max_dtr)
+ max_dtr = mci->host->hs_max_dtr;
+ else if (max_dtr > mci->tran_speed)
+ max_dtr = mci->tran_speed;
+
+ mci_set_clock(mci, max_dtr);
+}
+
+/*
+ * Activate HS200 or HS400ES mode if supported.
+ */
+int mmc_select_timing(struct mci *mci)
+{
+ unsigned int mmc_avail_type;
+ int err = 0;
+
+ mmc_select_max_dtr(mci);
+
+ mmc_avail_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE] & EXT_CSD_CARD_TYPE_MASK;
+ if (mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
+ err = mmc_select_hs200(mci);
+ if (err == -EBADMSG)
+ mmc_avail_type &= ~EXT_CSD_CARD_TYPE_HS200;
+ else
+ goto out;
+ }
+
+out:
+ if (err && err != -EBADMSG)
+ return err;
+
+ /*
+ * Set the bus speed to the selected bus timing.
+ * If timing is not selected, backward compatible is the default.
+ */
+ mmc_set_bus_speed(mci);
+
+ return 0;
+}
+
+int mmc_hs200_tuning(struct mci *mci)
+{
+ return mci_execute_tuning(mci);
+}
+
+static int mci_startup_mmc(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ int ret = 0;
+
+ /* if possible, speed up the transfer */
+ if (mci_caps(mci) & MMC_CAP_MMC_HIGHSPEED) {
+ if (mci->card_caps & MMC_CAP_MMC_HIGHSPEED_52MHZ)
+ mci->tran_speed = 52000000;
+ else
+ mci->tran_speed = 26000000;
+
+ host->timing = MMC_TIMING_MMC_HS;
+ }
+
+ if (IS_ENABLED(CONFIG_MCI_TUNING)) {
+ /*
+ * Select timing interface
+ */
+ ret = mmc_select_timing(mci);
+ if (ret)
+ return ret;
+
+ if (mmc_card_hs200(mci))
+ ret = mmc_hs200_tuning(mci);
+ }
+
+ if (ret || !IS_ENABLED(CONFIG_MCI_TUNING)) {
+ mci_set_clock(mci, mci->tran_speed);
+
+ /* find out maximum bus width and then try DDR if supported */
+ ret = mci_mmc_select_bus_width(mci);
+ if (ret > MMC_BUS_WIDTH_1 && mci->tran_speed == 52000000)
+ ret = mci_mmc_select_hs_ddr(mci);
+
+ if (ret < 0) {
+ dev_warn(&mci->dev, "Changing MMC bus width failed: %d\n", ret);
+ }
+ }
+
+ return ret;
+}
+
/**
* Scan the given host interfaces and detect connected MMC/SD cards
* @param mci MCI instance
@@ -1551,6 +1992,10 @@ static const char *mci_timing_tostr(unsigned timing)
return "MMC HS";
case MMC_TIMING_SD_HS:
return "SD HS";
+ case MMC_TIMING_MMC_DDR52:
+ return "MMC DDR52";
+ case MMC_TIMING_MMC_HS200:
+ return "HS200";
default:
return "unknown"; /* shouldn't happen */
}
@@ -1558,19 +2003,22 @@ static const char *mci_timing_tostr(unsigned timing)
static void mci_print_caps(unsigned caps)
{
- printf(" capabilities: %s%s%s%s%s\n",
+ printf(" capabilities: %s%s%s%s%s%s%s%s\n",
caps & MMC_CAP_4_BIT_DATA ? "4bit " : "",
caps & MMC_CAP_8_BIT_DATA ? "8bit " : "",
caps & MMC_CAP_SD_HIGHSPEED ? "sd-hs " : "",
caps & MMC_CAP_MMC_HIGHSPEED ? "mmc-hs " : "",
- caps & MMC_CAP_MMC_HIGHSPEED_52MHZ ? "mmc-52MHz " : "");
+ caps & MMC_CAP_MMC_HIGHSPEED_52MHZ ? "mmc-52MHz " : "",
+ caps & MMC_CAP_MMC_3_3V_DDR ? "ddr-3.3v " : "",
+ caps & MMC_CAP_MMC_1_8V_DDR ? "ddr-1.8v " : "",
+ caps & MMC_CAP_MMC_1_2V_DDR ? "ddr-1.2v " : "");
}
/**
* Output some valuable information when the user runs 'devinfo' on an MCI device
* @param mci MCI device instance
*/
-static void mci_info(struct device_d *dev)
+static void mci_info(struct device *dev)
{
struct mci *mci = container_of(dev, struct mci, dev);
struct mci_host *host = mci->host;
@@ -1596,7 +2044,9 @@ static void mci_info(struct device_d *dev)
mci_print_caps(host->host_caps);
printf("Card information:\n");
- printf(" Attached is a %s card\n", IS_SD(mci) ? "SD" : "MMC");
+ printf(" Card type: %s\n", mci->sdio ? "SDIO" : IS_SD(mci) ? "SD" : "MMC");
+ if (mci->sdio)
+ return;
printf(" Version: %s\n", mci_version_string(mci));
printf(" Capacity: %u MiB\n", (unsigned)(mci->capacity >> 20));
@@ -1608,8 +2058,8 @@ static void mci_info(struct device_d *dev)
mci->csd[2], mci->csd[3]);
printf(" Max. transfer speed: %u Hz\n", mci->tran_speed);
mci_print_caps(mci->card_caps);
- printf(" Manufacturer ID: %02X\n", extract_mid(mci));
- printf(" OEM/Application ID: %04X\n", extract_oid(mci));
+ printf(" Manufacturer ID: 0x%02X\n", extract_mid(mci));
+ printf(" OEM/Application ID: 0x%04X\n", extract_oid(mci));
printf(" Product name: '%c%c%c%c%c'\n", mci->cid[0] & 0xff,
(mci->cid[1] >> 24), (mci->cid[1] >> 16) & 0xff,
(mci->cid[1] >> 8) & 0xff, mci->cid[1] & 0xff);
@@ -1691,6 +2141,7 @@ static int mci_register_partition(struct mci_part *part)
*/
part->blk.dev = &mci->dev;
part->blk.ops = &mci_ops;
+ part->blk.type = IS_SD(mci) ? BLK_TYPE_SD : BLK_TYPE_MMC;
rc = blockdevice_register(&part->blk);
if (rc != 0) {
@@ -1699,7 +2150,7 @@ static int mci_register_partition(struct mci_part *part)
}
dev_info(&mci->dev, "registered %s\n", part->blk.cdev.name);
- np = host->hw_dev->device_node;
+ np = host->hw_dev->of_node;
/* create partitions on demand */
switch (part->area_type) {
@@ -1709,7 +2160,7 @@ static int mci_register_partition(struct mci_part *part)
else
partnodename = "boot1-partitions";
- np = of_get_child_by_name(host->hw_dev->device_node,
+ np = of_get_child_by_name(host->hw_dev->of_node,
partnodename);
break;
case MMC_BLK_DATA_AREA_MAIN:
@@ -1720,12 +2171,6 @@ static int mci_register_partition(struct mci_part *part)
return 0;
}
- rc = parse_partition_table(&part->blk);
- if (rc != 0) {
- /* Lack of partition table is unusual, but not a failure */
- dev_warn(&mci->dev, "No partition table found\n");
- }
-
if (np) {
of_parse_partitions(&part->blk.cdev, np);
@@ -1739,6 +2184,47 @@ static int mci_register_partition(struct mci_part *part)
return 0;
}
+static int of_broken_cd_fixup(struct device_node *root, void *ctx)
+{
+ struct mci_host *host = ctx;
+ struct device *hw_dev = host->hw_dev;
+ struct device_node *np;
+ char *name;
+
+ if (!host->broken_cd)
+ return 0;
+
+ name = of_get_reproducible_name(hw_dev->of_node);
+ np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+ if (!np) {
+ dev_warn(hw_dev, "Cannot find nodepath %pOF, cannot fixup\n",
+ hw_dev->of_node);
+ return -EINVAL;
+ }
+
+ of_property_write_bool(np, "cd-gpios", false);
+ of_property_write_bool(np, "broken-cd", true);
+
+ return 0;
+}
+
+static int mci_get_partition_setting_completed(struct mci *mci)
+{
+ u8 *ext_csd;
+ int ret;
+
+ ext_csd = mci_get_ext_csd(mci);
+ if (IS_ERR(ext_csd))
+ return PTR_ERR(ext_csd);
+
+ ret = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
+
+ dma_free(ext_csd);
+
+ return ret;
+}
+
/**
* Probe an MCI card at the given host interface
* @param mci MCI device instance
@@ -1750,10 +2236,13 @@ static int mci_card_probe(struct mci *mci)
int i, rc, disknum, ret;
bool has_bootpart = false;
- if (host->card_present && !host->card_present(host) &&
- !host->non_removable) {
- dev_err(&mci->dev, "no card inserted\n");
- return -ENODEV;
+ if (host->card_present && !host->card_present(host) && !host->non_removable) {
+ if (!host->broken_cd) {
+ dev_err(&mci->dev, "no card inserted\n");
+ return -ENODEV;
+ }
+
+ dev_info(&mci->dev, "no card inserted (ignoring)\n");
}
ret = regulator_enable(host->supply);
@@ -1781,12 +2270,22 @@ static int mci_card_probe(struct mci *mci)
goto on_error;
}
+ if (!(host->caps2 & MMC_CAP2_NO_SDIO)) {
+ rc = sdio_send_op_cond(mci);
+ if (!rc) {
+ mci->ready_for_use = true;
+ mci->sdio = true;
+ dev_info(&mci->dev, "SDIO card detected, ignoring\n");
+ return 0;
+ }
+ }
+
/* Check if this card can handle the "SD Card Physical Layer Specification 2.0" */
- if (!host->no_sd) {
+ if (!(host->caps2 & MMC_CAP2_NO_SD)) {
rc = sd_send_if_cond(mci);
rc = sd_send_op_cond(mci);
}
- if (host->no_sd || rc == -ETIMEDOUT) {
+ if ((host->caps2 & MMC_CAP2_NO_SD) || rc == -ETIMEDOUT) {
/* If SD card initialization was skipped or if it timed out,
* we check for an MMC card */
dev_dbg(&mci->dev, "Card seems to be a MultiMediaCard\n");
@@ -1837,6 +2336,13 @@ static int mci_card_probe(struct mci *mci)
dev_add_param_bool(&mci->dev, "boot_ack",
mci_set_boot_ack, NULL,
&mci->boot_ack_enable, mci);
+
+ ret = mci_get_partition_setting_completed(mci);
+ if (ret < 0)
+ dev_dbg(&mci->dev,
+ "Failed to determine EXT_CSD_PARTITION_SETTING_COMPLETED\n");
+ else
+ dev_add_param_bool_fixed(&mci->dev, "partitioning_completed", ret);
}
dev_dbg(&mci->dev, "SD Card successfully added\n");
@@ -1888,14 +2394,14 @@ int mci_detect_card(struct mci_host *host)
return mci_card_probe(host->mci);
}
-static int mci_detect(struct device_d *dev)
+static int mci_detect(struct device *dev)
{
struct mci *mci = container_of(dev, struct mci, dev);
return mci_detect_card(mci->host);
}
-static int mci_hw_detect(struct device_d *dev)
+static int mci_hw_detect(struct device *dev)
{
struct mci *mci;
@@ -1915,8 +2421,8 @@ static int mci_hw_detect(struct device_d *dev)
int mci_register(struct mci_host *host)
{
struct mci *mci;
- struct device_d *hw_dev;
- struct param_d *param_probe;
+ struct device *hw_dev;
+ struct param_d *param;
int ret;
mci = xzalloc(sizeof(*mci));
@@ -1961,15 +2467,24 @@ int mci_register(struct mci_host *host)
dev_info(hw_dev, "registered as %s\n", dev_name(&mci->dev));
- param_probe = dev_add_param_bool(&mci->dev, "probe",
- mci_set_probe, NULL, &mci->probe, mci);
+ param = dev_add_param_bool(&mci->dev, "probe", mci_set_probe, NULL,
+ &mci->probe, mci);
- if (IS_ERR(param_probe) && PTR_ERR(param_probe) != -ENOSYS) {
- ret = PTR_ERR(param_probe);
+ if (IS_ERR(param) && PTR_ERR(param) != -ENOSYS) {
+ ret = PTR_ERR(param);
dev_dbg(&mci->dev, "Failed to add 'probe' parameter to the MCI device\n");
goto err_unregister;
}
+ param = dev_add_param_bool(&mci->dev, "broken_cd", NULL, NULL,
+ &host->broken_cd, mci);
+
+ if (IS_ERR(param) && PTR_ERR(param) != -ENOSYS) {
+ ret = PTR_ERR(param);
+ dev_dbg(&mci->dev, "Failed to add 'broken_cd' parameter to the MCI device\n");
+ goto err_unregister;
+ }
+
if (IS_ENABLED(CONFIG_MCI_INFO))
mci->dev.info = mci_info;
@@ -1977,6 +2492,9 @@ int mci_register(struct mci_host *host)
if (IS_ENABLED(CONFIG_MCI_STARTUP))
mci_card_probe(mci);
+ if (!(host->caps2 & MMC_CAP2_NO_SD) && dev_of_node(host->hw_dev))
+ of_register_fixup(of_broken_cd_fixup, host);
+
list_add_tail(&mci->list, &mci_list);
return 0;
@@ -2043,14 +2561,40 @@ void mci_of_parse_node(struct mci_host *host,
}
}
+ host->broken_cd = of_property_read_bool(np, "broken-cd");
host->non_removable = of_property_read_bool(np, "non-removable");
- host->no_sd = of_property_read_bool(np, "no-sd");
host->disable_wp = of_property_read_bool(np, "disable-wp");
+
+ if (of_property_read_bool(np, "full-pwr-cycle"))
+ host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
+ if (of_property_read_bool(np, "full-pwr-cycle-in-suspend"))
+ host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND;
+ if (of_property_read_bool(np, "no-sdio"))
+ host->caps2 |= MMC_CAP2_NO_SDIO;
+ if (of_property_read_bool(np, "no-sd"))
+ host->caps2 |= MMC_CAP2_NO_SD;
+ if (of_property_read_bool(np, "no-mmc"))
+ host->caps2 |= MMC_CAP2_NO_MMC;
+ if (IS_ENABLED(CONFIG_MCI_TUNING)) {
+ if (of_property_read_bool(np, "mmc-hs200-1_8v"))
+ host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+ if (of_property_read_bool(np, "mmc-hs200-1_2v"))
+ host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-1_8v"))
+ host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-1_2v"))
+ host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe"))
+ host->caps2 |= MMC_CAP2_HS400_ES;
+ if (of_property_read_bool(np, "no-mmc-hs400"))
+ host->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V |
+ MMC_CAP2_HS400_ES);
+ }
}
void mci_of_parse(struct mci_host *host)
{
- return mci_of_parse_node(host, host->hw_dev->device_node);
+ return mci_of_parse_node(host, host->hw_dev->of_node);
}
struct mci *mci_get_device_by_name(const char *name)
diff --git a/drivers/mci/mci_spi.c b/drivers/mci/mci_spi.c
index 6ae2824edd..ad743d19d9 100644
--- a/drivers/mci/mci_spi.c
+++ b/drivers/mci/mci_spi.c
@@ -19,7 +19,7 @@
#include <crc.h>
#include <crc7.h>
#include <of.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#define to_spi_host(mci) container_of(mci, struct mmc_spi_host, mci)
#define spi_setup(spi) spi->master->setup(spi)
@@ -48,8 +48,8 @@
struct mmc_spi_host {
struct mci_host mci;
struct spi_device *spi;
- struct device_d *dev;
- int detect_pin;
+ struct device *dev;
+ struct gpio_desc *detect_pin;
/* for bulk data transfers */
struct spi_transfer t_tx;
@@ -314,7 +314,7 @@ static void mmc_spi_set_ios(struct mci_host *mci, struct mci_ios *ios)
}
}
-static int mmc_spi_init(struct mci_host *mci, struct device_d *mci_dev)
+static int mmc_spi_init(struct mci_host *mci, struct device *mci_dev)
{
struct mmc_spi_host *host = to_spi_host(mci);
mmc_spi_readbytes(host, 10, NULL);
@@ -360,15 +360,15 @@ static int spi_mci_card_present(struct mci_host *mci)
int ret;
/* No gpio, assume card is present */
- if (!gpio_is_valid(host->detect_pin))
+ if (IS_ERR_OR_NULL(host->detect_pin))
return 1;
- ret = gpio_get_value(host->detect_pin);
+ ret = gpiod_get_value(host->detect_pin);
return ret == 0 ? 1 : 0;
}
-static int spi_mci_probe(struct device_d *dev)
+static int spi_mci_probe(struct device *dev)
{
struct device_node *np = dev_of_node(dev);
struct spi_device *spi = (struct spi_device *)dev->type_data;
@@ -434,11 +434,12 @@ static int spi_mci_probe(struct device_d *dev)
host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
host->mci.host_caps = MMC_CAP_SPI;
- host->detect_pin = -EINVAL;
if (np) {
host->mci.devname = xstrdup(of_alias_get(np));
- host->detect_pin = gpiod_get(dev, NULL, GPIOD_IN);
+ host->detect_pin = gpiod_get_optional(dev, NULL, GPIOD_IN);
+ if (IS_ERR(host->detect_pin))
+ dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n");
}
mci_register(&host->mci);
@@ -450,8 +451,9 @@ static __maybe_unused struct of_device_id spi_mci_compatible[] = {
{ .compatible = "mmc-spi-slot" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, spi_mci_compatible);
-static struct driver_d spi_mci_driver = {
+static struct driver spi_mci_driver = {
.name = "spi_mci",
.probe = spi_mci_probe,
.of_compatible = DRV_OF_COMPAT(spi_mci_compatible),
diff --git a/drivers/mci/mmci.c b/drivers/mci/mmci.c
index 2edc37c681..a16deba854 100644
--- a/drivers/mci/mmci.c
+++ b/drivers/mci/mmci.c
@@ -96,7 +96,7 @@ static struct variant_data variant_ux500v2 = {
struct mmci_host {
struct mci_host mci;
void __iomem *base;
- struct device_d *hw_dev;
+ struct device *hw_dev;
struct mmci_platform_data *plat;
struct clk *clk;
unsigned long mclk;
@@ -434,7 +434,7 @@ static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_dat
}
/* MMC uses open drain drivers in the enumeration phase */
-static int mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+static int mci_reset(struct mci_host *mci, struct device *mci_dev)
{
struct mmci_host *host = to_mci_host(mci);
struct variant_data *variant = host->variant;
@@ -551,8 +551,8 @@ static int mmci_of_parse(struct device_node *np,
static int mmci_probe(struct amba_device *dev, const struct amba_id *id)
{
- struct device_d *hw_dev = &dev->dev;
- struct device_node *np = hw_dev->device_node;
+ struct device *hw_dev = &dev->dev;
+ struct device_node *np = hw_dev->of_node;
struct mmci_platform_data *plat = hw_dev->platform_data;
struct variant_data *variant = id->data;
u32 sdi_u32;
diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c
index 5d6511f958..6883b78d5c 100644
--- a/drivers/mci/mxs.c
+++ b/drivers/mci/mxs.c
@@ -27,9 +27,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <asm/bitops.h>
-#include <mach/mci.h>
-#include <mach/clock.h>
-#include <mach/ssp.h>
+#include <mach/mxs/mci.h>
+#include <mach/mxs/ssp.h>
#define CLOCKRATE_MIN (1 * 1000 * 1000)
#define CLOCKRATE_MAX (480 * 1000 * 1000)
@@ -433,7 +432,7 @@ static unsigned mxs_mci_setup_clock_speed(struct mxs_mci_host *mxs_mci, unsigned
* @param mci_dev MCI device instance
* @return 0 on success, negative value else
*/
-static int mxs_mci_initialize(struct mci_host *host, struct device_d *mci_dev)
+static int mxs_mci_initialize(struct mci_host *host, struct device *mci_dev)
{
struct mxs_mci_host *mxs_mci = to_mxs_mci(host);
@@ -517,7 +516,7 @@ static void mxs_mci_set_ios(struct mci_host *host, struct mci_ios *ios)
const unsigned char bus_width[3] = { 1, 4, 8 };
-static void mxs_mci_info(struct device_d *hw_dev)
+static void mxs_mci_info(struct device *hw_dev)
{
struct mxs_mci_host *mxs_mci = hw_dev->priv;
@@ -529,7 +528,7 @@ static void mxs_mci_info(struct device_d *hw_dev)
printf("\n");
}
-static int mxs_mci_probe(struct device_d *hw_dev)
+static int mxs_mci_probe(struct device *hw_dev)
{
struct resource *iores;
struct mxs_mci_platform_data *pd = hw_dev->platform_data;
@@ -598,8 +597,9 @@ static __maybe_unused struct of_device_id mxs_mmc_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mxs_mmc_compatible);
-static struct driver_d mxs_mci_driver = {
+static struct driver mxs_mci_driver = {
.name = "mxs_mci",
.probe = mxs_mci_probe,
.of_compatible = DRV_OF_COMPAT(mxs_mmc_compatible),
diff --git a/drivers/mci/omap_hsmmc.c b/drivers/mci/omap_hsmmc.c
index 61a1f46805..41d5a62f32 100644
--- a/drivers/mci/omap_hsmmc.c
+++ b/drivers/mci/omap_hsmmc.c
@@ -12,12 +12,12 @@
#include <io.h>
#include <linux/err.h>
-#include <mach/omap_hsmmc.h>
+#include <mach/omap/omap_hsmmc.h>
#if defined(CONFIG_MFD_TWL6030) && \
defined(CONFIG_MCI_OMAP_HSMMC) && \
defined(CONFIG_ARCH_OMAP4)
-#include <mach/omap4_twl6030_mmc.h>
+#include <mach/omap/omap4_twl6030_mmc.h>
#endif
struct hsmmc {
@@ -170,7 +170,7 @@ static struct omap_mmc_driver_data omap4_data = {
struct omap_hsmmc {
struct mci_host mci;
- struct device_d *dev;
+ struct device *dev;
struct hsmmc *base;
void __iomem *iobase;
};
@@ -207,7 +207,7 @@ static int mmc_init_stream(struct omap_hsmmc *hsmmc)
return 0;
}
-static int mmc_init_setup(struct mci_host *mci, struct device_d *dev)
+static int mmc_init_setup(struct mci_host *mci, struct device *dev)
{
struct omap_hsmmc *hsmmc = to_hsmmc(mci);
struct hsmmc *mmc_base = hsmmc->base;
@@ -568,7 +568,7 @@ static void mmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
}
-static int omap_mmc_probe(struct device_d *dev)
+static int omap_mmc_probe(struct device *dev)
{
struct resource *iores;
struct omap_hsmmc *hsmmc;
@@ -647,8 +647,9 @@ static __maybe_unused struct of_device_id omap_mmc_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_mmc_dt_ids);
-static struct driver_d omap_mmc_driver = {
+static struct driver omap_mmc_driver = {
.name = "omap-hsmmc",
.probe = omap_mmc_probe,
.id_table = omap_mmc_ids,
diff --git a/drivers/mci/pxamci.c b/drivers/mci/pxamci.c
index 1ec871afbf..5df1ef5cb6 100644
--- a/drivers/mci/pxamci.c
+++ b/drivers/mci/pxamci.c
@@ -15,9 +15,9 @@
#include <mci.h>
#include <linux/err.h>
-#include <mach/clock.h>
-#include <mach/mci_pxa2xx.h>
-#include <mach/pxa-regs.h>
+#include <mach/pxa/clock.h>
+#include <mach/pxa/mci_pxa2xx.h>
+#include <mach/pxa/pxa-regs.h>
#include "pxamci.h"
#define DRIVER_NAME "pxa-mmc"
@@ -26,7 +26,7 @@
#define TX_TIMEOUT (250 * MSECOND)
#define CMD_TIMEOUT (100 * MSECOND)
-static void clk_enable(void)
+static void mmc_clk_enable(void)
{
CKEN |= CKEN_MMC;
}
@@ -319,7 +319,7 @@ static void pxamci_set_ios(struct mci_host *mci, struct mci_ios *ios)
mmc_writel(host->clkrt, MMC_CLKRT);
}
-static int pxamci_init(struct mci_host *mci, struct device_d *dev)
+static int pxamci_init(struct mci_host *mci, struct device *dev)
{
struct pxamci_host *host = to_pxamci(mci);
@@ -328,13 +328,13 @@ static int pxamci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
-static int pxamci_probe(struct device_d *dev)
+static int pxamci_probe(struct device *dev)
{
struct resource *iores;
struct pxamci_host *host;
int gpio_power = -1;
- clk_enable();
+ mmc_clk_enable();
host = xzalloc(sizeof(*host));
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
@@ -375,7 +375,7 @@ static int pxamci_probe(struct device_d *dev)
return 0;
}
-static struct driver_d pxamci_driver = {
+static struct driver pxamci_driver = {
.name = DRIVER_NAME,
.probe = pxamci_probe,
};
diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c
index 164f662552..f503dbae65 100644
--- a/drivers/mci/rockchip-dwcmshc-sdhci.c
+++ b/drivers/mci/rockchip-dwcmshc-sdhci.c
@@ -25,21 +25,32 @@
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
+#define DECMSHC_EMMC_DLL_CMDOUT 0x810
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
#define DWCMSHC_EMMC_DLL_START BIT(0)
+#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
+#define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9)
#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29
#define DWCMSHC_EMMC_DLL_START_POINT 16
#define DWCMSHC_EMMC_DLL_INC 8
+#define DWCMSHC_EMMC_DLL_BYPASS BIT(24)
#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
-#define DLL_TXCLK_TAPNUM_DEFAULT 0x8
-#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
+#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
+#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
+#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
-#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
-#define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9)
+#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
+#define DLL_STRBIN_DELAY_NUM_OFFSET 16
+#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16
#define DLL_RXCLK_NO_INVERTER 1
#define DLL_RXCLK_INVERTER 0
-#define DWCMSHC_ENHANCED_STROBE BIT(8)
+#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
+#define DLL_RXCLK_ORI_GATE BIT(31)
+#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
+#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
+#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
+
#define DLL_LOCK_WO_TMOUT(x) \
((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
@@ -87,26 +98,12 @@ static int rk_sdhci_card_present(struct mci_host *mci)
return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT);
}
-static int rk_sdhci_reset(struct rk_sdhci_host *host, u8 mask)
-{
- sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask);
-
- /* wait for reset completion */
- if (wait_on_timeout(100 * MSECOND,
- !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))){
- dev_err(host->mci.hw_dev, "SDHCI reset timeout\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int rk_sdhci_init(struct mci_host *mci, struct device_d *dev)
+static int rk_sdhci_init(struct mci_host *mci, struct device *dev)
{
struct rk_sdhci_host *host = to_rk_sdhci_host(mci);
int ret;
- ret = rk_sdhci_reset(host, SDHCI_RESET_ALL);
+ ret = sdhci_reset(&host->sdhci, SDHCI_RESET_ALL);
if (ret)
return ret;
@@ -151,8 +148,32 @@ static void rk_sdhci_set_clock(struct rk_sdhci_host *host, unsigned int clock)
sdhci_set_clock(&host->sdhci, clock, clk_get_rate(host->clks[CLK_CORE].clk));
- if (clock <= 400000)
+ /* Disable cmd conflict check */
+ extra = sdhci_read32(&host->sdhci, DWCMSHC_HOST_CTRL3);
+ extra &= ~BIT(0);
+ sdhci_write32(&host->sdhci, DWCMSHC_HOST_CTRL3, extra);
+
+ if (clock <= 52000000) {
+ /*
+ * Disable DLL and reset both of sample and drive clock.
+ * The bypass bit and start bit need to be set if DLL is not locked.
+ */
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL,
+ DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START);
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_RXCLK, DLL_RXCLK_ORI_GATE);
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_TXCLK, 0);
+ sdhci_write32(&host->sdhci, DECMSHC_EMMC_DLL_CMDOUT, 0);
+ /*
+ * Before switching to hs400es mode, the driver will enable
+ * enhanced strobe first. PHY needs to configure the parameters
+ * of enhanced strobe first.
+ */
+ extra = DWCMSHC_EMMC_DLL_DLYENA |
+ DLL_STRBIN_DELAY_NUM_SEL |
+ DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_STRBIN, extra);
return;
+ }
/* Reset DLL */
sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, BIT(1));
@@ -172,11 +193,6 @@ static void rk_sdhci_set_clock(struct rk_sdhci_host *host, unsigned int clock)
return;
}
- /* Disable cmd conflict check */
- extra = sdhci_read32(&host->sdhci, DWCMSHC_HOST_CTRL3);
- extra &= ~BIT(0);
- sdhci_write32(&host->sdhci, DWCMSHC_HOST_CTRL3, extra);
-
extra = 0x1 << 16 | /* tune clock stop en */
0x2 << 17 | /* pre-change delay */
0x3 << 19; /* post-change delay */
@@ -216,34 +232,11 @@ static void rk_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
}
-static int rk_sdhci_wait_for_done(struct rk_sdhci_host *host, u32 mask)
-{
- u64 start = get_time_ns();
- u16 stat;
-
- do {
- stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
- if (stat & SDHCI_INT_ERROR) {
- dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
- sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS));
- return -EPERM;
- }
-
- if (is_timeout(start, 1000 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for done\n");
- return -ETIMEDOUT;
- }
- } while ((stat & mask) != mask);
-
- return 0;
-}
-
static void print_error(struct rk_sdhci_host *host, int cmdidx)
{
- dev_err(host->mci.hw_dev,
+ dev_dbg(host->mci.hw_dev,
"error while transfering data for command %d\n", cmdidx);
- dev_err(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
+ dev_dbg(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
}
@@ -252,23 +245,13 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data)
{
struct rk_sdhci_host *host = to_rk_sdhci_host(mci);
- u32 mask, command, xfer;
+ u32 command, xfer;
int ret;
dma_addr_t dma;
- /* Wait for idle before next command */
- mask = SDHCI_CMD_INHIBIT_CMD;
- if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
- mask |= SDHCI_CMD_INHIBIT_DATA;
-
- ret = wait_on_timeout(10 * MSECOND,
- !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
-
- if (ret) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for idle\n");
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
return ret;
- }
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
@@ -285,11 +268,9 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
- ret = rk_sdhci_wait_for_done(host, SDHCI_INT_CMD_COMPLETE);
- if (ret == -EPERM)
+ ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+ if (ret)
goto error;
- else if (ret)
- return ret;
sdhci_read_response(&host->sdhci, cmd);
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
@@ -299,15 +280,15 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
error:
if (ret) {
print_error(host, cmd->cmdidx);
- rk_sdhci_reset(host, BIT(1)); /* SDHCI_RESET_CMD */
- rk_sdhci_reset(host, BIT(2)); /* SDHCI_RESET_DATA */
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
}
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
return ret;
}
-static int rk_sdhci_probe(struct device_d *dev)
+static int rk_sdhci_probe(struct device *dev)
{
struct rk_sdhci_host *host;
struct resource *iores;
@@ -365,11 +346,14 @@ static __maybe_unused struct of_device_id rk_sdhci_compatible[] = {
{
.compatible = "rockchip,rk3568-dwcmshc"
}, {
+ .compatible = "rockchip,rk3588-dwcmshc"
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, rk_sdhci_compatible);
-static struct driver_d rk_sdhci_driver = {
+static struct driver rk_sdhci_driver = {
.name = "rk3568-dwcmshc-sdhci",
.probe = rk_sdhci_probe,
.of_compatible = DRV_OF_COMPAT(rk_sdhci_compatible),
diff --git a/drivers/mci/s3c.c b/drivers/mci/s3c.c
deleted file mode 100644
index 1de57a608f..0000000000
--- a/drivers/mci/s3c.c
+++ /dev/null
@@ -1,761 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: 2010 Juergen Beisert <juergen@kreuzholzen.de>
-// SPDX-FileCopyrightText: 2004-2006 Thomas Kleffel <tk@maintech.de>, maintech GmbH
-// SPDX-FileCopyrightText: 2008 Simtec Electronics <ben-linux@fluff.org>
-// SPDX-FileCopyrightText: 2006 OpenMoko, Inc (Harald Welte <laforge@openmoko.org>)
-// SPDX-FileCopyrightText: 2005 Thomas Kleffel
-
-/*
- * This code is partially based on Linux and u-boot sources, among others the
- * u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c.
- */
-
-/**
- * @file
- * @brief MCI card host interface for S3C2440 CPU
- */
-
-/* #define DEBUG */
-
-#include <common.h>
-#include <init.h>
-#include <mci.h>
-#include <errno.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/err.h>
-#include <mach/s3c-mci.h>
-#include <mach/s3c-generic.h>
-#include <mach/s3c-iomap.h>
-
-#define GET_HOST_DATA(x) (x->priv)
-#define GET_MCI_PDATA(x) (x->platform_data)
-
-#define SDICON 0x0
-# define SDICON_SDRESET (1 << 8)
-# define SDICON_MMCCLOCK (1 << 5) /* this is a clock type SD or MMC style WTF? */
-# define SDICON_BYTEORDER (1 << 4)
-# define SDICON_SDIOIRQ (1 << 3)
-# define SDICON_RWAITEN (1 << 2)
-# define SDICON_FIFORESET (1 << 1) /* reserved bit on 2440 ????? */
-# define SDICON_CLKEN (1 << 0) /* enable/disable external clock */
-
-#define SDIPRE 0x4
-
-#define SDICMDARG 0x8
-
-#define SDICMDCON 0xc
-# define SDICMDCON_ABORT (1 << 12)
-# define SDICMDCON_WITHDATA (1 << 11)
-# define SDICMDCON_LONGRSP (1 << 10)
-# define SDICMDCON_WAITRSP (1 << 9)
-# define SDICMDCON_CMDSTART (1 << 8)
-# define SDICMDCON_SENDERHOST (1 << 6)
-# define SDICMDCON_INDEX (0x3f)
-
-#define SDICMDSTAT 0x10
-# define SDICMDSTAT_CRCFAIL (1 << 12)
-# define SDICMDSTAT_CMDSENT (1 << 11)
-# define SDICMDSTAT_CMDTIMEOUT (1 << 10)
-# define SDICMDSTAT_RSPFIN (1 << 9)
-# define SDICMDSTAT_XFERING (1 << 8)
-# define SDICMDSTAT_INDEX (0xff)
-
-#define SDIRSP0 0x14
-#define SDIRSP1 0x18
-#define SDIRSP2 0x1C
-#define SDIRSP3 0x20
-
-#define SDITIMER 0x24
-#define SDIBSIZE 0x28
-
-#define SDIDCON 0x2c
-# define SDIDCON_DS_BYTE (0 << 22)
-# define SDIDCON_DS_HALFWORD (1 << 22)
-# define SDIDCON_DS_WORD (2 << 22)
-# define SDIDCON_IRQPERIOD (1 << 21)
-# define SDIDCON_TXAFTERRESP (1 << 20)
-# define SDIDCON_RXAFTERCMD (1 << 19)
-# define SDIDCON_BUSYAFTERCMD (1 << 18)
-# define SDIDCON_BLOCKMODE (1 << 17)
-# define SDIDCON_WIDEBUS (1 << 16)
-# define SDIDCON_DMAEN (1 << 15)
-# define SDIDCON_STOP (0 << 14)
-# define SDIDCON_DATSTART (1 << 14)
-# define SDIDCON_DATMODE (3 << 12)
-# define SDIDCON_BLKNUM (0xfff)
-# define SDIDCON_XFER_READY (0 << 12)
-# define SDIDCON_XFER_CHKSTART (1 << 12)
-# define SDIDCON_XFER_RXSTART (2 << 12)
-# define SDIDCON_XFER_TXSTART (3 << 12)
-
-#define SDIDCNT 0x30
-# define SDIDCNT_BLKNUM_SHIFT 12
-
-#define SDIDSTA 0x34
-# define SDIDSTA_RDYWAITREQ (1 << 10)
-# define SDIDSTA_SDIOIRQDETECT (1 << 9)
-# define SDIDSTA_FIFOFAIL (1 << 8) /* reserved on 2440 */
-# define SDIDSTA_CRCFAIL (1 << 7)
-# define SDIDSTA_RXCRCFAIL (1 << 6)
-# define SDIDSTA_DATATIMEOUT (1 << 5)
-# define SDIDSTA_XFERFINISH (1 << 4)
-# define SDIDSTA_BUSYFINISH (1 << 3)
-# define SDIDSTA_SBITERR (1 << 2) /* reserved on 2410a/2440 */
-# define SDIDSTA_TXDATAON (1 << 1)
-# define SDIDSTA_RXDATAON (1 << 0)
-
-#define SDIFSTA 0x38
-# define SDIFSTA_FIFORESET (1<<16)
-# define SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
-# define SDIFSTA_TFDET (1<<13)
-# define SDIFSTA_RFDET (1<<12)
-# define SDIFSTA_TFHALF (1<<11)
-# define SDIFSTA_TFEMPTY (1<<10)
-# define SDIFSTA_RFLAST (1<<9)
-# define SDIFSTA_RFFULL (1<<8)
-# define SDIFSTA_RFHALF (1<<7)
-# define SDIFSTA_COUNTMASK (0x7f)
-
-#define SDIIMSK 0x3C
-# define SDIIMSK_RESPONSECRC (1<<17)
-# define SDIIMSK_CMDSENT (1<<16)
-# define SDIIMSK_CMDTIMEOUT (1<<15)
-# define SDIIMSK_RESPONSEND (1<<14)
-# define SDIIMSK_READWAIT (1<<13)
-# define SDIIMSK_SDIOIRQ (1<<12)
-# define SDIIMSK_FIFOFAIL (1<<11)
-# define SDIIMSK_CRCSTATUS (1<<10)
-# define SDIIMSK_DATACRC (1<<9)
-# define SDIIMSK_DATATIMEOUT (1<<8)
-# define SDIIMSK_DATAFINISH (1<<7)
-# define SDIIMSK_BUSYFINISH (1<<6)
-# define SDIIMSK_SBITERR (1<<5) /* reserved 2440/2410a */
-# define SDIIMSK_TXFIFOHALF (1<<4)
-# define SDIIMSK_TXFIFOEMPTY (1<<3)
-# define SDIIMSK_RXFIFOLAST (1<<2)
-# define SDIIMSK_RXFIFOFULL (1<<1)
-# define SDIIMSK_RXFIFOHALF (1<<0)
-
-#define SDIDATA 0x40
-
-struct s3c_mci_host {
- struct mci_host host;
- void __iomem *base;
- int bus_width:2; /* 0 = 1 bit, 1 = 4 bit, 2 = 8 bit */
- unsigned clock; /* current clock in Hz */
- unsigned data_size; /* data transfer in bytes */
-};
-
-#define to_s3c_host(h) container_of(h, struct s3c_mci_host, host)
-
-/**
- * Finish a request
- * @param hw_dev Host interface instance
- *
- * Just a little bit paranoia.
- */
-static void s3c_finish_request(struct s3c_mci_host *host_data)
-{
- /* TODO ensure the engines are stopped */
-}
-
-/**
- * Setup a new clock frequency on this MCI bus
- * @param hw_dev Host interface instance
- * @param nc New clock value in Hz (can be 0)
- * @return New clock value (may differ from 'nc')
- */
-static unsigned s3c_setup_clock_speed(struct s3c_mci_host *host_data, unsigned nc)
-{
- unsigned clock;
- uint32_t mci_psc;
-
- if (nc == 0)
- return 0;
-
- clock = s3c_get_pclk();
- /* Calculate the required prescaler value to get the requested frequency */
- mci_psc = (clock + (nc >> 2)) / nc;
-
- if (mci_psc > 256) {
- mci_psc = 256;
- pr_warning("SD/MMC clock might be too high!\n");
- }
-
- writel(mci_psc - 1, host_data->base + SDIPRE);
-
- return clock / mci_psc;
-}
-
-/**
- * Reset the MCI engine (the hard way)
- * @param hw_dev Host interface instance
- *
- * This will reset everything in all registers of this unit!
- */
-static void s3c_mci_reset(struct s3c_mci_host *host_data)
-{
- /* reset the hardware */
- writel(SDICON_SDRESET, host_data->base + SDICON);
- /* wait until reset it finished */
- while (readl(host_data->base + SDICON) & SDICON_SDRESET)
- ;
-}
-
-/**
- * Initialize hard and software
- * @param hw_dev Host interface instance
- * @param mci_dev MCI device instance (might be NULL)
- */
-static int s3c_mci_initialize(struct s3c_mci_host *host_data, struct device_d *mci_dev)
-{
- s3c_mci_reset(host_data);
-
- /* restore last settings */
- host_data->clock = s3c_setup_clock_speed(host_data, host_data->clock);
- writel(0x007FFFFF, host_data->base + SDITIMER);
- writel(SDICON_MMCCLOCK, host_data->base + SDICON);
- writel(512, host_data->base + SDIBSIZE);
-
- return 0;
-}
-
-/**
- * Prepare engine's bits for the next command transfer
- * @param cmd_flags MCI's command flags
- * @param data_flags MCI's data flags
- * @return Register bits for this transfer
- */
-static uint32_t s3c_prepare_command_setup(unsigned cmd_flags, unsigned data_flags)
-{
- uint32_t reg;
-
- /* source (=host) */
- reg = SDICMDCON_SENDERHOST;
-
- if (cmd_flags & MMC_RSP_PRESENT) {
- reg |= SDICMDCON_WAITRSP;
- pr_debug("Command with response\n");
- }
- if (cmd_flags & MMC_RSP_136) {
- reg |= SDICMDCON_LONGRSP;
- pr_debug("Command with long response\n");
- }
- if (cmd_flags & MMC_RSP_CRC)
- ; /* FIXME */
- if (cmd_flags & MMC_RSP_BUSY)
- ; /* FIXME */
- if (cmd_flags & MMC_RSP_OPCODE)
- ; /* FIXME */
- if (data_flags != 0)
- reg |= SDICMDCON_WITHDATA;
-
- return reg;
-}
-
-/**
- * Prepare engine's bits for the next data transfer
- * @param hw_dev Host interface device instance
- * @param data_flags MCI's data flags
- * @return Register bits for this transfer
- */
-static uint32_t s3c_prepare_data_setup(struct s3c_mci_host *host_data, unsigned data_flags)
-{
- uint32_t reg = SDIDCON_BLOCKMODE; /* block mode only is supported */
-
- if (host_data->bus_width == 1)
- reg |= SDIDCON_WIDEBUS;
-
- /* enable any kind of data transfers on demand only */
- if (data_flags & MMC_DATA_WRITE)
- reg |= SDIDCON_TXAFTERRESP | SDIDCON_XFER_TXSTART;
-
- if (data_flags & MMC_DATA_READ)
- reg |= SDIDCON_RXAFTERCMD | SDIDCON_XFER_RXSTART;
-
- /* TODO: Support more than the 2440 CPU */
- reg |= SDIDCON_DS_WORD | SDIDCON_DATSTART;
-
- return reg;
-}
-
-/**
- * Terminate a current running transfer
- * @param hw_dev Host interface device instance
- * @return 0 on success
- *
- * Note: Try to stop a running transfer. This should not happen, as all
- * transfers must complete in this driver. But who knows... ;-)
- */
-static int s3c_terminate_transfer(struct s3c_mci_host *host_data)
-{
- unsigned stoptries = 3;
-
- while (readl(host_data->base + SDIDSTA) & (SDIDSTA_TXDATAON | SDIDSTA_RXDATAON)) {
- pr_debug("Transfer still in progress.\n");
-
- writel(SDIDCON_STOP, host_data->base + SDIDCON);
- s3c_mci_initialize(host_data, NULL);
-
- if ((stoptries--) == 0) {
- pr_warning("Cannot stop the engine!\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-/**
- * Setup registers for data transfer
- * @param hw_dev Host interface device instance
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- */
-static int s3c_prepare_data_transfer(struct s3c_mci_host *host_data, struct mci_data *data)
-{
- uint32_t reg;
-
- writel(data->blocksize, host_data->base + SDIBSIZE);
- reg = s3c_prepare_data_setup(host_data, data->flags);
- reg |= data->blocks & SDIDCON_BLKNUM;
- writel(reg, host_data->base + SDIDCON);
- writel(0x007FFFFF, host_data->base + SDITIMER);
-
- return 0;
-}
-
-/**
- * Send a command and receive the response
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- */
-static int s3c_send_command(struct s3c_mci_host *host_data, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- uint32_t reg, t1;
- int rc;
-
- writel(0x007FFFFF, host_data->base + SDITIMER);
-
- /* setup argument */
- writel(cmd->cmdarg, host_data->base + SDICMDARG);
-
- /* setup command and transfer characteristic */
- reg = s3c_prepare_command_setup(cmd->resp_type, data != NULL ? data->flags : 0);
- reg |= cmd->cmdidx & SDICMDCON_INDEX;
-
- /* run the command right now */
- writel(reg | SDICMDCON_CMDSTART, host_data->base + SDICMDCON);
- t1 = readl(host_data->base + SDICMDSTAT);
- /* wait until command is done */
- while (1) {
- reg = readl(host_data->base + SDICMDSTAT);
- /* done? */
- if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (reg & SDICMDSTAT_RSPFIN) {
- writel(SDICMDSTAT_RSPFIN,
- host_data->base + SDICMDSTAT);
- rc = 0;
- break;
- }
- } else {
- if (reg & SDICMDSTAT_CMDSENT) {
- writel(SDICMDSTAT_CMDSENT,
- host_data->base + SDICMDSTAT);
- rc = 0;
- break;
- }
- }
- /* timeout? */
- if (reg & SDICMDSTAT_CMDTIMEOUT) {
- writel(SDICMDSTAT_CMDTIMEOUT,
- host_data->base + SDICMDSTAT);
- rc = -ETIMEDOUT;
- break;
- }
- }
-
- if ((rc == 0) && (cmd->resp_type & MMC_RSP_PRESENT)) {
- cmd->response[0] = readl(host_data->base + SDIRSP0);
- cmd->response[1] = readl(host_data->base + SDIRSP1);
- cmd->response[2] = readl(host_data->base + SDIRSP2);
- cmd->response[3] = readl(host_data->base + SDIRSP3);
- }
- /* do not disable the clock! */
- return rc;
-}
-
-/**
- * Clear major registers prior a new transaction
- * @param hw_dev Host interface device instance
- * @return 0 on success
- *
- * FIFO clear is only necessary on 2440, but doesn't hurt on 2410
- */
-static int s3c_prepare_engine(struct s3c_mci_host *host_data)
-{
- int rc;
-
- rc = s3c_terminate_transfer(host_data);
- if (rc != 0)
- return rc;
-
- writel(-1, host_data->base + SDICMDSTAT);
- writel(-1, host_data->base + SDIDSTA);
- writel(-1, host_data->base + SDIFSTA);
-
- return 0;
-}
-
-/**
- * Handle MCI commands without data
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @return 0 on success
- *
- * This functions handles the following MCI commands:
- * - "broadcast command (BC)" without a response
- * - "broadcast commands with response (BCR)"
- * - "addressed command (AC)" with response, but without data
- */
-static int s3c_mci_std_cmds(struct s3c_mci_host *host_data, struct mci_cmd *cmd)
-{
- int rc;
-
- rc = s3c_prepare_engine(host_data);
- if (rc != 0)
- return 0;
-
- return s3c_send_command(host_data, cmd, NULL);
-}
-
-/**
- * Read one block of data from the FIFO
- * @param hw_dev Host interface device instance
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- */
-static int s3c_mci_read_block(struct s3c_mci_host *host_data, struct mci_data *data)
-{
- uint32_t *p;
- unsigned cnt, data_size;
-
-#define READ_REASON_TO_FAIL (SDIDSTA_CRCFAIL | SDIDSTA_RXCRCFAIL | SDIDSTA_DATATIMEOUT)
-
- p = (uint32_t*)data->dest;
- data_size = data->blocksize * data->blocks;
-
- while (data_size > 0) {
-
- /* serious error? */
- if (readl(host_data->base + SDIDSTA) & READ_REASON_TO_FAIL) {
- pr_err("Failed while reading data\n");
- return -EIO;
- }
-
- /* now check the FIFO status */
- if (readl(host_data->base + SDIFSTA) & SDIFSTA_FIFOFAIL) {
- pr_err("Data loss due to FIFO overflow when reading\n");
- return -EIO;
- }
-
- /* we only want to read full words */
- cnt = (readl(host_data->base + SDIFSTA) & SDIFSTA_COUNTMASK) >> 2;
-
- /* read one chunk of data from the FIFO */
- while (cnt--) {
- *p = readl(host_data->base + SDIDATA);
- p++;
- if (data_size >= 4)
- data_size -= 4;
- else {
- data_size = 0;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/**
- * Write one block of data into the FIFO
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- *
- * We must ensure data in the FIFO when the command phase changes into the
- * data phase. To ensure this, the FIFO gets filled first, then the command.
- */
-static int s3c_mci_write_block(struct s3c_mci_host *host_data, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- const uint32_t *p = (const uint32_t*)data->src;
- unsigned cnt, data_size;
- uint32_t reg;
-
-#define WRITE_REASON_TO_FAIL (SDIDSTA_CRCFAIL | SDIDSTA_DATATIMEOUT)
-
- data_size = data->blocksize * data->blocks;
- /*
- * With high clock rates we must fill the FIFO as early as possible
- * Its size is 16 words. We assume its empty, when this function is
- * entered.
- */
- cnt = 16;
- while (cnt--) {
- writel(*p, host_data->base + SDIDATA);
- p++;
- if (data_size >= 4)
- data_size -= 4;
- else {
- data_size = 0;
- break;
- }
- }
-
- /* data is now in place and waits for transmitt. Start the command right now */
- s3c_send_command(host_data, cmd, data);
-
- if ((reg = readl(host_data->base + SDIFSTA)) & SDIFSTA_FIFOFAIL) {
- pr_err("Command fails immediatly due to FIFO underrun when writing %08X\n",
- reg);
- return -EIO;
- }
-
- while (data_size > 0) {
-
- if (readl(host_data->base + SDIDSTA) & WRITE_REASON_TO_FAIL) {
- pr_err("Failed writing data\n");
- return -EIO;
- }
-
- /* now check the FIFO status */
- if ((reg = readl(host_data->base + SDIFSTA)) & SDIFSTA_FIFOFAIL) {
- pr_err("Data loss due to FIFO underrun when writing %08X\n",
- reg);
- return -EIO;
- }
-
- /* we only want to write full words */
- cnt = 16 - (((readl(host_data->base + SDIFSTA) & SDIFSTA_COUNTMASK) + 3) >> 2);
-
- /* fill the FIFO if it has free entries */
- while (cnt--) {
- writel(*p, host_data->base + SDIDATA);
- p++;
- if (data_size >= 4)
- data_size -= 4;
- else {
- data_size = 0;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/**
- * Handle MCI commands with or without data
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
-*/
-static int s3c_mci_adtc(struct s3c_mci_host *host_data, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- int rc;
-
- rc = s3c_prepare_engine(host_data);
- if (rc != 0)
- return rc;
-
- rc = s3c_prepare_data_transfer(host_data, data);
- if (rc != 0)
- return rc;
-
- if (data->flags & MMC_DATA_READ) {
- s3c_send_command(host_data, cmd, data);
- rc = s3c_mci_read_block(host_data, data);
- if (rc == 0) {
- while (!(readl(host_data->base + SDIDSTA) & SDIDSTA_XFERFINISH))
- ;
- } else
- s3c_terminate_transfer(host_data);
- }
-
- if (data->flags & MMC_DATA_WRITE) {
- rc = s3c_mci_write_block(host_data, cmd, data);
- if (rc == 0) {
- while (!(readl(host_data->base + SDIDSTA) & SDIDSTA_XFERFINISH))
- ;
- } else
- s3c_terminate_transfer(host_data);
- }
- writel(0, host_data->base + SDIDCON);
-
- return rc;
-}
-
-/* ------------------------- MCI API -------------------------------------- */
-
-/**
- * Keep the attached MMC/SD unit in a well know state
- * @param host MCI host
- * @param mci_dev MCI device instance
- * @return 0 on success, negative value else
- */
-static int mci_reset(struct mci_host *host, struct device_d *mci_dev)
-{
- struct s3c_mci_host *host_data = to_s3c_host(host);
-
- return s3c_mci_initialize(host_data, mci_dev);
-}
-
-/**
- * Process one command to the MCI card
- * @param host MCI host
- * @param cmd The command to process
- * @param data The data to handle in the command (can be NULL)
- * @return 0 on success, negative value else
- */
-static int mci_request(struct mci_host *host, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- struct s3c_mci_host *host_data = to_s3c_host(host);
- int rc;
-
- /* enable clock */
- writel(readl(host_data->base + SDICON) | SDICON_CLKEN,
- host_data->base + SDICON);
-
- if ((cmd->resp_type == 0) || (data == NULL))
- rc = s3c_mci_std_cmds(host_data, cmd);
- else
- rc = s3c_mci_adtc(host_data, cmd, data); /* with response and data */
-
- s3c_finish_request(host_data);
-
- /* disable clock */
- writel(readl(host_data->base + SDICON) & ~SDICON_CLKEN,
- host_data->base + SDICON);
- return rc;
-}
-
-/**
- * Setup the bus width and IO speed
- * @param host MCI host
- * @param bus_width New bus width value (1, 4 or 8)
- * @param clock New clock in Hz (can be '0' to disable the clock)
- */
-static void mci_set_ios(struct mci_host *host, struct mci_ios *ios)
-{
- struct s3c_mci_host *host_data = to_s3c_host(host);
- uint32_t reg;
-
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_4:
- host_data->bus_width = 1;
- break;
- case MMC_BUS_WIDTH_1:
- host_data->bus_width = 0;
- break;
- default:
- return;
- }
-
- reg = readl(host_data->base + SDICON);
- if (ios->clock) {
- /* setup the IO clock frequency and enable it */
- host_data->clock = s3c_setup_clock_speed(host_data, ios->clock);
- reg |= SDICON_CLKEN; /* enable the clock */
- } else {
- reg &= ~SDICON_CLKEN; /* disable the clock */
- host_data->clock = 0;
- }
- writel(reg, host_data->base + SDICON);
-
- pr_debug("IO settings: bus width=%d, frequency=%u Hz\n",
- host_data->bus_width, host_data->clock);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void s3c_info(struct device_d *hw_dev)
-{
- struct s3c_mci_host *host = hw_dev->priv;
- struct s3c_mci_platform_data *pd = hw_dev->platform_data;
-
- printf(" Bus data width: %d bit\n", host->bus_width == 1 ? 4 : 1);
- printf(" Bus frequency: %u Hz\n", host->clock);
- printf(" Frequency limits: ");
- if (pd->f_min == 0)
- printf("no lower limit ");
- else
- printf("%u Hz lower limit ", pd->f_min);
- if (pd->f_max == 0)
- printf("- no upper limit");
- else
- printf("- %u Hz upper limit", pd->f_max);
- printf("\n Card detection support: %s\n",
- pd->gpio_detect != 0 ? "yes" : "no");
-}
-
-static int s3c_mci_probe(struct device_d *hw_dev)
-{
- struct resource *iores;
- struct s3c_mci_host *s3c_host;
- struct s3c_mci_platform_data *pd = hw_dev->platform_data;
-
- s3c_host = xzalloc(sizeof(*s3c_host));
- s3c_host->host.send_cmd = mci_request;
- s3c_host->host.set_ios = mci_set_ios;
- s3c_host->host.init = mci_reset;
-
- /* TODO replace by the global func: enable the SDI unit clock */
- writel(readl(S3C_CLOCK_POWER_BASE + 0x0c) | 0x200,
- S3C_CLOCK_POWER_BASE + 0x0c);
-
- if (pd == NULL) {
- pr_err("Missing platform data\n");
- return -EINVAL;
- }
-
- hw_dev->priv = s3c_host;
- iores = dev_request_mem_resource(hw_dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- s3c_host->base = IOMEM(iores->start);
-
- s3c_host->host.hw_dev = hw_dev;
-
- /* feed forward the platform specific values */
- s3c_host->host.voltages = pd->voltages;
- s3c_host->host.host_caps = pd->caps;
- s3c_host->host.f_min = pd->f_min == 0 ? s3c_get_pclk() / 256 : pd->f_min;
- s3c_host->host.f_max = pd->f_max == 0 ? s3c_get_pclk() / 2 : pd->f_max;
-
- if (IS_ENABLED(CONFIG_MCI_INFO))
- hw_dev->info = s3c_info;
-
- /*
- * Start the clock to let the engine and the card finishes its startup
- */
- s3c_host->clock = s3c_setup_clock_speed(s3c_host, pd->f_min);
- writel(SDICON_FIFORESET | SDICON_MMCCLOCK, s3c_host->base + SDICON);
-
- return mci_register(&s3c_host->host);
-}
-
-static struct driver_d s3c_mci_driver = {
- .name = "s3c_mci",
- .probe = s3c_mci_probe,
-};
-device_platform_driver(s3c_mci_driver);
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 8da32d57c2..ba1e07e966 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -9,6 +9,227 @@
#include "sdhci.h"
+#define MAX_TUNING_LOOP 40
+#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+enum sdhci_reset_reason {
+ SDHCI_RESET_FOR_INIT,
+ SDHCI_RESET_FOR_REQUEST_ERROR,
+ SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY,
+ SDHCI_RESET_FOR_TUNING_ABORT,
+ SDHCI_RESET_FOR_CARD_REMOVED,
+ SDHCI_RESET_FOR_CQE_RECOVERY,
+};
+
+static void sdhci_reset_for_reason(struct sdhci *host, enum sdhci_reset_reason reason)
+{
+ if (host->quirks2 & SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER) {
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ return;
+ }
+
+ switch (reason) {
+ case SDHCI_RESET_FOR_INIT:
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ break;
+ case SDHCI_RESET_FOR_REQUEST_ERROR:
+ case SDHCI_RESET_FOR_TUNING_ABORT:
+ case SDHCI_RESET_FOR_CARD_REMOVED:
+ case SDHCI_RESET_FOR_CQE_RECOVERY:
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ break;
+ case SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY:
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ break;
+ }
+}
+
+#define sdhci_reset_for(h, r) sdhci_reset_for_reason((h), SDHCI_RESET_FOR_##r)
+
+static int sdhci_send_command_retry(struct sdhci *host, struct mci_cmd *cmd)
+{
+ int timeout = 10;
+
+ while ((sdhci_read32(host, SDHCI_PRESENT_STATE) & SDHCI_CMD_INHIBIT_CMD)) {
+ if (!timeout--)
+ return -ETIMEDOUT;
+
+ mdelay(1);
+ }
+
+ return host->mci->send_cmd(host->mci, cmd, NULL);
+}
+
+/*
+ * We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI
+ * tuning command does not have a data payload (or rather the hardware does it
+ * automatically) so mmc_send_tuning() will return -EIO. Also the tuning command
+ * interrupt setup is different to other commands and there is no timeout
+ * interrupt so special handling is needed.
+ */
+static int sdhci_send_tuning(struct sdhci *host, u32 opcode)
+{
+ struct mci_cmd cmd = {};
+ int ret;
+
+ cmd.cmdidx = opcode;
+ cmd.resp_type = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ /*
+ * In response to CMD19, the card sends 64 bytes of tuning
+ * block to the Host Controller. So we set the block size
+ * to 64 here.
+ */
+ if (cmd.cmdidx == MMC_SEND_TUNING_BLOCK_HS200 &&
+ host->mci->bus_width == MMC_BUS_WIDTH_8) {
+ sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 128));
+ } else {
+ sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 64));
+ }
+
+ ret = sdhci_send_command_retry(host, &cmd);
+
+ return ret;
+}
+
+static void sdhci_end_tuning(struct sdhci *host)
+{
+ sdhci_write32(host, SDHCI_INT_ENABLE, host->tuning_old_ier);
+ sdhci_write32(host, SDHCI_SIGNAL_ENABLE, host->tuning_old_sig);
+}
+
+static void sdhci_start_tuning(struct sdhci *host)
+{
+ u16 ctrl;
+
+ ctrl = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl);
+
+ mdelay(1);
+
+ host->tuning_old_ier = sdhci_read32(host, SDHCI_INT_ENABLE);
+ host->tuning_old_sig = sdhci_read32(host, SDHCI_SIGNAL_ENABLE);
+
+ sdhci_write32(host, SDHCI_INT_ENABLE, SDHCI_INT_DATA_AVAIL);
+ sdhci_write32(host, SDHCI_SIGNAL_ENABLE, SDHCI_INT_DATA_AVAIL);
+}
+
+static void sdhci_reset_tuning(struct sdhci *host)
+{
+ u16 ctrl;
+
+ ctrl = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl);
+}
+
+static void sdhci_abort_tuning(struct sdhci *host, u32 opcode)
+{
+ sdhci_reset_tuning(host);
+
+ sdhci_reset_for(host, TUNING_ABORT);
+
+ sdhci_end_tuning(host);
+
+ mci_send_abort_tuning(host->mci->mci, opcode);
+}
+
+static int __sdhci_execute_tuning(struct sdhci *host, u32 opcode)
+{
+ int i;
+ int ret;
+
+ /*
+ * Issue opcode repeatedly till Execute Tuning is set to 0 or the number
+ * of loops reaches tuning loop count.
+ * Some controllers are known to always require 40 iterations.
+ */
+ for (i = 0; i < host->tuning_loop_count; i++) {
+ u16 ctrl;
+
+ ret = sdhci_send_tuning(host, opcode);
+ if (ret) {
+ sdhci_abort_tuning(host, opcode);
+ return -ETIMEDOUT;
+ }
+
+ /* Spec does not require a delay between tuning cycles */
+ if (host->tuning_delay > 0)
+ mdelay(host->tuning_delay);
+
+ ctrl = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) {
+ if (ctrl & SDHCI_CTRL_TUNED_CLK) {
+ return 0; /* Success! */
+ }
+ break;
+ }
+
+ }
+
+ dev_dbg(&host->mci->mci->dev, "Tuning timeout, falling back to fixed sampling clock\n");
+ sdhci_reset_tuning(host);
+ return -EAGAIN;
+}
+
+int sdhci_execute_tuning(struct sdhci *sdhci, u32 opcode)
+{
+ struct mci_host *host = sdhci->mci;
+ int err = 0;
+ unsigned int tuning_count = 0;
+
+ if (sdhci->tuning_mode == SDHCI_TUNING_MODE_1)
+ tuning_count = sdhci->tuning_count;
+
+ /*
+ * The Host Controller needs tuning in case of SDR104 and DDR50
+ * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
+ * the Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
+ */
+ switch (host->timing) {
+ /* HS400 tuning is done in HS200 mode */
+ case MMC_TIMING_MMC_HS400:
+ err = -EINVAL;
+ goto out;
+
+ case MMC_TIMING_MMC_HS200:
+ break;
+
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_DDR50:
+ break;
+
+ case MMC_TIMING_UHS_SDR50:
+ fallthrough;
+
+ default:
+ goto out;
+ }
+
+ if (sdhci->platform_execute_tuning) {
+ err = sdhci->platform_execute_tuning(host, opcode);
+ goto out;
+ }
+
+ if (sdhci->tuning_delay < 0)
+ sdhci->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
+
+ sdhci_start_tuning(sdhci);
+
+ sdhci->tuning_err = __sdhci_execute_tuning(sdhci, opcode);
+
+ sdhci_end_tuning(sdhci);
+out:
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
+
void sdhci_read_response(struct sdhci *sdhci, struct mci_cmd *cmd)
{
if (cmd->resp_type & MMC_RSP_136) {
@@ -50,7 +271,12 @@ void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
*command |= SDHCI_CMD_INDEX(cmd->cmdidx);
- if (data) {
+ if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK ||
+ cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200) {
+ *command |= SDHCI_DATA_PRESENT;
+ *xfer = SDHCI_DATA_TO_HOST;
+
+ } else if (data) {
*command |= SDHCI_DATA_PRESENT;
*xfer |= SDHCI_BLOCK_COUNT_EN;
@@ -111,6 +337,60 @@ void sdhci_set_bus_width(struct sdhci *host, int width)
sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl);
}
+static void sdhci_set_uhs_signaling(struct sdhci *host, unsigned timing)
+{
+ u16 ctrl_2;
+
+ ctrl_2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ if ((timing == MMC_TIMING_MMC_HS200) ||
+ (timing == MMC_TIMING_UHS_SDR104))
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (timing == MMC_TIMING_UHS_SDR12)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ else if (timing == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (timing == MMC_TIMING_UHS_SDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ else if ((timing == MMC_TIMING_UHS_DDR50) ||
+ (timing == MMC_TIMING_MMC_DDR52))
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ else if (timing == MMC_TIMING_MMC_HS400)
+ ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl_2);
+}
+EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
+
+static inline bool sdhci_can_64bit_dma(struct sdhci *host)
+{
+ /*
+ * According to SD Host Controller spec v4.10, bit[27] added from
+ * version 4.10 in Capabilities Register is used as 64-bit System
+ * Address support for V4 mode.
+ */
+ if (host->version >= SDHCI_SPEC_410 && host->v4_mode)
+ return host->caps & SDHCI_CAN_64BIT_V4;
+
+ return host->caps & SDHCI_CAN_64BIT;
+}
+
+
+static void sdhci_set_adma_addr(struct sdhci *host, dma_addr_t addr)
+{
+ sdhci_write32(host, SDHCI_ADMA_ADDRESS, lower_32_bits(addr));
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ sdhci_write32(host, SDHCI_ADMA_ADDRESS_HI, upper_32_bits(addr));
+}
+
+static void sdhci_set_sdma_addr(struct sdhci *host, dma_addr_t addr)
+{
+ if (host->v4_mode)
+ sdhci_set_adma_addr(host, addr);
+ else
+ sdhci_write32(host, SDHCI_DMA_ADDRESS, addr);
+}
+
#ifdef __PBL__
/*
* Stubs to make timeout logic below work in PBL
@@ -124,6 +404,33 @@ void sdhci_set_bus_width(struct sdhci *host, int width)
#endif
+int sdhci_wait_for_done(struct sdhci *sdhci, u32 mask)
+{
+ u64 start = get_time_ns();
+ u32 stat;
+
+ do {
+ stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+
+ if (stat & SDHCI_INT_TIMEOUT)
+ return -ETIMEDOUT;
+
+ if (stat & SDHCI_INT_ERROR) {
+ dev_err(sdhci->mci->hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
+ stat);
+ return -EPERM;
+ }
+
+ if (is_timeout(start, 1000 * MSECOND)) {
+ dev_err(sdhci->mci->hw_dev,
+ "SDHCI timeout while waiting for done\n");
+ return -ETIMEDOUT;
+ }
+ } while ((stat & mask) != mask);
+
+ return 0;
+}
+
void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data)
{
if (!data)
@@ -133,10 +440,37 @@ void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data)
SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize) | data->blocks << 16);
}
+static void sdhci_config_dma(struct sdhci *host)
+{
+ u8 ctrl;
+ u16 ctrl2;
+
+ if (host->version < SDHCI_SPEC_200)
+ return;
+
+ ctrl = sdhci_read8(host, SDHCI_HOST_CONTROL);
+ /* Note if DMA Select is zero then SDMA is selected */
+ ctrl &= ~SDHCI_CTRL_DMA_MASK;
+ sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl);
+
+ if (host->flags & SDHCI_USE_64_BIT_DMA) {
+ /*
+ * If v4 mode, all supported DMA can be 64-bit addressing if
+ * controller supports 64-bit system address, otherwise only
+ * ADMA can support 64-bit addressing.
+ */
+ if (host->v4_mode) {
+ ctrl2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ ctrl2 |= SDHCI_CTRL_64BIT_ADDR;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl2);
+ }
+ }
+}
+
void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
dma_addr_t *dma)
{
- struct device_d *dev = sdhci->mci->hw_dev;
+ struct device *dev = sdhci->mci->hw_dev;
int nbytes;
if (!data)
@@ -150,10 +484,10 @@ void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
nbytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_READ)
- *dma = dma_map_single(dev, (void *)data->src, nbytes,
+ *dma = dma_map_single(dev, data->dest, nbytes,
DMA_FROM_DEVICE);
else
- *dma = dma_map_single(dev, data->dest, nbytes,
+ *dma = dma_map_single(dev, (void *)data->src, nbytes,
DMA_TO_DEVICE);
if (dma_mapping_error(dev, *dma)) {
@@ -161,13 +495,15 @@ void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
return;
}
- sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, *dma);
+ sdhci_config_dma(sdhci);
+ sdhci_set_sdma_addr(sdhci, *dma);
}
int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
dma_addr_t dma)
{
- struct device_d *dev = sdhci->mci->hw_dev;
+ struct device *dev = sdhci->mci->hw_dev;
+ u64 start;
int nbytes;
u32 irqstat;
int ret;
@@ -177,6 +513,8 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
nbytes = data->blocks * data->blocksize;
+ start = get_time_ns();
+
do {
irqstat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
@@ -195,19 +533,33 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
goto out;
}
+ /*
+ * We currently don't do anything fancy with DMA
+ * boundaries, but as we can't disable the feature
+ * we need to at least restart the transfer.
+ *
+ * According to the spec sdhci_readl(host, SDHCI_DMA_ADDRESS)
+ * should return a valid address to continue from, but as
+ * some controllers are faulty, don't trust them.
+ */
if (irqstat & SDHCI_INT_DMA) {
- u32 addr = sdhci_read32(sdhci, SDHCI_DMA_ADDRESS);
-
/*
* DMA engine has stopped on buffer boundary. Acknowledge
* the interrupt and kick the DMA engine again.
*/
sdhci_write32(sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA);
- sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, addr);
+ sdhci_set_sdma_addr(sdhci, ALIGN(dma, SDHCI_DEFAULT_BOUNDARY_SIZE));
}
if (irqstat & SDHCI_INT_XFER_COMPLETE)
break;
+
+ if (is_timeout(start, 10 * SECOND)) {
+ dev_alert(dev, "DMA wait timed out. Resetting, but recovery unlikely\n");
+ sdhci_reset(sdhci, SDHCI_RESET_ALL);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
} while (1);
ret = 0;
@@ -217,7 +569,7 @@ out:
else
dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
- return 0;
+ return ret;
}
int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
@@ -263,7 +615,7 @@ int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma)
{
- struct device_d *dev = sdhci->mci->hw_dev;
+ struct device *dev = sdhci->mci->hw_dev;
if (!data)
return 0;
@@ -436,6 +788,53 @@ void sdhci_enable_clk(struct sdhci *host, u16 clk)
sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk);
}
+int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *data)
+{
+ u32 mask;
+ int ret;
+
+ mask = SDHCI_CMD_INHIBIT_CMD;
+
+ if (data || (cmd && (cmd->resp_type & MMC_RSP_BUSY)))
+ mask |= SDHCI_CMD_INHIBIT_DATA;
+
+ if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ mask &= ~SDHCI_CMD_INHIBIT_DATA;
+
+ ret = wait_on_timeout(10 * MSECOND,
+ !(sdhci_read32(host, SDHCI_PRESENT_STATE) & mask));
+
+ if (ret) {
+ dev_err(host->mci->hw_dev,
+ "SDHCI timeout while waiting for idle\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int sdhci_wait_idle_data(struct sdhci *host, struct mci_cmd *cmd)
+{
+ u32 mask;
+ int ret;
+
+ mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
+
+ if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ mask &= ~SDHCI_CMD_INHIBIT_DATA;
+
+ ret = wait_on_timeout(10 * MSECOND,
+ !(sdhci_read32(host, SDHCI_PRESENT_STATE) & mask));
+
+ if (ret) {
+ dev_err(host->mci->hw_dev,
+ "SDHCI timeout while waiting for idle\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock)
{
u16 clk;
@@ -444,6 +843,10 @@ void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_
host->mci->clock = 0;
+ sdhci_set_uhs_signaling(host, host->mci->timing);
+
+ sdhci_wait_idle_data(host, NULL);
+
sdhci_write16(host, SDHCI_CLOCK_CONTROL, 0);
if (clock == 0)
@@ -453,13 +856,31 @@ void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_
sdhci_enable_clk(host, clk);
}
+static void sdhci_do_enable_v4_mode(struct sdhci *host)
+{
+ u16 ctrl2;
+
+ ctrl2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ if (ctrl2 & SDHCI_CTRL_V4_MODE)
+ return;
+
+ ctrl2 |= SDHCI_CTRL_V4_MODE;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl2);
+}
+
+void sdhci_enable_v4_mode(struct sdhci *host)
+{
+ host->v4_mode = true;
+ sdhci_do_enable_v4_mode(host);
+}
+
void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
const u32 *caps, const u32 *caps1)
{
u16 v;
u64 dt_caps_mask = 0;
u64 dt_caps = 0;
- struct device_node *np = host->mci->hw_dev->device_node;
+ struct device_node *np = host->mci->hw_dev->of_node;
BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
@@ -470,6 +891,9 @@ void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
sdhci_reset(host, SDHCI_RESET_ALL);
+ if (host->v4_mode)
+ sdhci_do_enable_v4_mode(host);
+
of_property_read_u64(np, "sdhci-caps-mask", &dt_caps_mask);
of_property_read_u64(np, "sdhci-caps", &dt_caps);
@@ -541,7 +965,33 @@ int sdhci_setup_host(struct sdhci *host)
if (host->caps & SDHCI_CAN_DO_HISPD)
mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
- host->sdma_boundary = SDHCI_DMA_BOUNDARY_512K;
+ if (host->caps & SDHCI_CAN_DO_8BIT)
+ mci->host_caps |= MMC_CAP_8_BIT_DATA;
+
+ host->sdma_boundary = SDHCI_DEFAULT_BOUNDARY_ARG;
+
+ if (sdhci_can_64bit_dma(host))
+ host->flags |= SDHCI_USE_64_BIT_DMA;
+
+ if ((mci->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
+ host->flags |= SDHCI_SIGNALING_180;
+
+ host->tuning_delay = -1;
+ host->tuning_loop_count = MAX_TUNING_LOOP;
+
+ /* Initial value for re-tuning timer count */
+ host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
+ host->caps1);
+
+ /*
+ * In case Re-tuning Timer is not disabled, the actual value of
+ * re-tuning timer will be 2 ^ (n - 1).
+ */
+ if (host->tuning_count)
+ host->tuning_count = 1 << (host->tuning_count - 1);
+
+ /* Re-tuning mode supported by the Host Controller */
+ host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
return 0;
}
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index c538385939..5de85239b1 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -5,6 +5,7 @@
#include <pbl.h>
#include <dma.h>
#include <linux/iopoll.h>
+#include <linux/sizes.h>
#define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_BLOCK_SIZE__BLOCK_COUNT 0x04
@@ -18,6 +19,8 @@
#define SDHCI_DMA_BOUNDARY_8K SDHCI_DMA_BOUNDARY(1)
#define SDHCI_DMA_BOUNDARY_4K SDHCI_DMA_BOUNDARY(0)
#define SDHCI_DMA_BOUNDARY(x) (((x) & 0x7) << 12)
+#define SDHCI_DEFAULT_BOUNDARY_SIZE SZ_512K
+#define SDHCI_DEFAULT_BOUNDARY_ARG SDHCI_DMA_BOUNDARY_512K
#define SDHCI_TRANSFER_BLOCK_SIZE(x) ((x) & 0xfff)
#define SDHCI_BLOCK_COUNT 0x06
#define SDHCI_ARGUMENT 0x08
@@ -25,9 +28,11 @@
#define SDHCI_TRANSFER_MODE 0x0c
#define SDHCI_MULTIPLE_BLOCKS BIT(5)
#define SDHCI_DATA_TO_HOST BIT(4)
+#define SDHCI_TRNS_AUTO_CMD12 BIT(3)
#define SDHCI_BLOCK_COUNT_EN BIT(1)
#define SDHCI_DMA_EN BIT(0)
#define SDHCI_COMMAND 0x0e
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
#define SDHCI_CMD_INDEX(c) (((c) & 0x3f) << 8)
#define SDHCI_COMMAND_CMDTYP_SUSPEND (1 << 6)
#define SDHCI_COMMAND_CMDTYP_RESUME (2 << 6)
@@ -117,6 +122,18 @@
#define SDHCI_INT_ERROR_ENABLE 0x36
#define SDHCI_SIGNAL_ENABLE 0x38
#define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C
+#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL_UHS_MASK GENMASK(3, 0)
+#define SDHCI_CTRL_UHS_SDR12 0x0
+#define SDHCI_CTRL_UHS_SDR25 0x1
+#define SDHCI_CTRL_UHS_SDR50 0x2
+#define SDHCI_CTRL_UHS_SDR104 0x3
+#define SDHCI_CTRL_UHS_DDR50 0x4
+#define SDHCI_CTRL_HS400 0x5 /* Non-standard */
+#define SDHCI_CTRL_EXEC_TUNING BIT(6)
+#define SDHCI_CTRL_TUNED_CLK BIT(7)
+#define SDHCI_CTRL_64BIT_ADDR BIT(13)
+#define SDHCI_CTRL_V4_MODE BIT(12)
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
@@ -159,6 +176,8 @@
#define SDHCI_PRESET_CLKGEN_SEL BIT(10)
#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
+#define SDHCI_P_VENDOR_SPEC_AREA 0xE8
+#define SDHCI_P_VENDOR_SPEC_AREA_MASK GENMASK(11, 0)
#define SDHCI_HOST_VERSION 0xFE
#define SDHCI_VENDOR_VER_MASK 0xFF00
#define SDHCI_VENDOR_VER_SHIFT 8
@@ -173,6 +192,9 @@
#define SDHCI_CLOCK_MUL_SHIFT 16
+#define SDHCI_ADMA_ADDRESS 0x58
+#define SDHCI_ADMA_ADDRESS_HI 0x5c
+
#define SDHCI_MMC_BOOT 0xC4
#define SDHCI_MAX_DIV_SPEC_200 256
@@ -191,21 +213,52 @@ struct sdhci {
int max_clk; /* Max possible freq (Hz) */
int clk_mul; /* Clock Muliplier value */
+ int flags; /* Host attributes */
+#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
+#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
+#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
+#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
+#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
+#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
+#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
+#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
+#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
+#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
+#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
+#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */
+#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */
+
unsigned int version; /* SDHCI spec. version */
enum mci_timing timing;
bool preset_enabled; /* Preset is enabled */
+ bool v4_mode; /* Host Version 4 Enable */
unsigned int quirks;
#define SDHCI_QUIRK_MISSING_CAPS BIT(27)
unsigned int quirks2;
#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN BIT(15)
+#define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER BIT(19)
u32 caps; /* CAPABILITY_0 */
u32 caps1; /* CAPABILITY_1 */
bool read_caps; /* Capability flags have been read */
u32 sdma_boundary;
+ unsigned int tuning_count; /* Timer count for re-tuning */
+ unsigned int tuning_mode; /* Re-tuning mode supported by host */
+ unsigned int tuning_err; /* Error code for re-tuning */
+#define SDHCI_TUNING_MODE_1 0
+#define SDHCI_TUNING_MODE_2 1
+#define SDHCI_TUNING_MODE_3 2
+ /* Delay (ms) between tuning commands */
+ int tuning_delay;
+ int tuning_loop_count;
+ int tuning_old_ier;
+ int tuning_old_sig;
+
struct mci_host *mci;
+
+ int (*platform_execute_tuning)(struct mci_host *host, u32 opcode);
};
static inline u32 sdhci_read32(struct sdhci *host, int reg)
@@ -257,6 +310,10 @@ static inline void sdhci_write8(struct sdhci *host, int reg, u32 val)
}
#define SDHCI_NO_DMA DMA_ERROR_CODE
+int sdhci_execute_tuning(struct sdhci *sdhci, u32 opcode);
+int sdhci_wait_idle_data(struct sdhci *host, struct mci_cmd *cmd);
+int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *data);
+int sdhci_wait_for_done(struct sdhci *host, u32 mask);
void sdhci_read_response(struct sdhci *host, struct mci_cmd *cmd);
void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
struct mci_data *data, bool dma, u32 *command,
@@ -272,6 +329,7 @@ u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
unsigned int *actual_clock, unsigned int input_clock);
void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock);
void sdhci_enable_clk(struct sdhci *host, u16 clk);
+void sdhci_enable_v4_mode(struct sdhci *host);
int sdhci_setup_host(struct sdhci *host);
void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
const u32 *caps, const u32 *caps1);
diff --git a/drivers/mci/stm32_sdmmc2.c b/drivers/mci/stm32_sdmmc2.c
index 40ffc17908..418213a1b3 100644
--- a/drivers/mci/stm32_sdmmc2.c
+++ b/drivers/mci/stm32_sdmmc2.c
@@ -176,7 +176,7 @@
struct stm32_sdmmc2_priv {
void __iomem *base;
struct mci_host mci;
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
struct reset_control *reset_ctl;
u32 clk_reg_msk;
@@ -185,7 +185,7 @@ struct stm32_sdmmc2_priv {
#define to_mci_host(mci) container_of(mci, struct stm32_sdmmc2_priv, mci)
-static int stm32_sdmmc2_reset(struct mci_host *mci, struct device_d *mci_dev)
+static int stm32_sdmmc2_reset(struct mci_host *mci, struct device *mci_dev)
{
struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
@@ -257,11 +257,12 @@ static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
udelay(DIV_ROUND_UP(74 * USEC_PER_SEC, priv->mci.clock));
}
-static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
- struct mci_data *data, u32 data_length)
+static dma_addr_t stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
+ struct mci_data *data, u32 data_length)
{
unsigned int num_bytes = data->blocks * data->blocksize;
- u32 data_ctrl, idmabase0;
+ dma_addr_t idmabase0;
+ u32 data_ctrl;
/* Configure the SDMMC DPSM (Data Path State Machine) */
data_ctrl = (__ilog2_u32(data->blocksize) <<
@@ -270,27 +271,27 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
if (data->flags & MMC_DATA_READ) {
data_ctrl |= SDMMC_DCTRL_DTDIR;
- idmabase0 = (u32)data->dest;
+ idmabase0 = dma_map_single(priv->dev, (void *)data->src, num_bytes,
+ DMA_FROM_DEVICE);
} else {
- idmabase0 = (u32)data->src;
+ idmabase0 = dma_map_single(priv->dev, (void *)data->src, num_bytes,
+ DMA_TO_DEVICE);
}
+ if (dma_mapping_error(priv->dev, idmabase0))
+ return DMA_ERROR_CODE;
+
/* Set the SDMMC DataLength value */
writel(data_length, priv->base + SDMMC_DLEN);
/* Write to SDMMC DCTRL */
writel(data_ctrl, priv->base + SDMMC_DCTRL);
- if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_device((unsigned long)idmabase0,
- num_bytes, DMA_TO_DEVICE);
- else
- dma_sync_single_for_device((unsigned long)idmabase0,
- num_bytes, DMA_FROM_DEVICE);
-
/* Enable internal DMA */
writel(idmabase0, priv->base + SDMMC_IDMABASE0);
writel(SDMMC_IDMACTRL_IDMAEN, priv->base + SDMMC_IDMACTRL);
+
+ return idmabase0;
}
static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv,
@@ -415,7 +416,8 @@ static int stm32_sdmmc2_end_cmd(struct stm32_sdmmc2_priv *priv,
static int stm32_sdmmc2_end_data(struct stm32_sdmmc2_priv *priv,
struct mci_cmd *cmd,
- struct mci_data *data)
+ struct mci_data *data,
+ dma_addr_t dma_addr)
{
u32 mask = SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT |
SDMMC_STA_IDMATE | SDMMC_STA_DATAEND;
@@ -436,12 +438,10 @@ static int stm32_sdmmc2_end_data(struct stm32_sdmmc2_priv *priv,
return ret;
}
- if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
- num_bytes, DMA_TO_DEVICE);
+ if (data->flags & MMC_DATA_READ)
+ dma_unmap_single(priv->dev, dma_addr, num_bytes, DMA_FROM_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
- num_bytes, DMA_FROM_DEVICE);
+ dma_unmap_single(priv->dev, dma_addr, num_bytes, DMA_TO_DEVICE);
if (status & SDMMC_STA_DCRCFAIL) {
dev_err(priv->dev, "error SDMMC_STA_DCRCFAIL (0x%x) for cmd %d\n",
@@ -481,23 +481,26 @@ static int stm32_sdmmc2_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
u32 cmdat = data ? SDMMC_CMD_CMDTRANS : 0;
+ dma_addr_t dma_addr = DMA_ERROR_CODE;
u32 data_length = 0;
int ret;
if (data) {
data_length = data->blocks * data->blocksize;
- stm32_sdmmc2_start_data(priv, data, data_length);
+ dma_addr = stm32_sdmmc2_start_data(priv, data, data_length);
+ if (dma_addr == DMA_ERROR_CODE)
+ return -EFAULT;
}
stm32_sdmmc2_start_cmd(priv, cmd, cmdat, data_length);
- dev_dbg(priv->dev, "%s: send cmd %d data: 0x%x @ 0x%x\n", __func__,
- cmd->cmdidx, data ? data_length : 0, (unsigned int)data);
+ dev_dbg(priv->dev, "%s: send cmd %d data: 0x%x @ %p\n", __func__,
+ cmd->cmdidx, data ? data_length : 0, data);
ret = stm32_sdmmc2_end_cmd(priv, cmd);
if (data && !ret)
- ret = stm32_sdmmc2_end_data(priv, cmd, data);
+ ret = stm32_sdmmc2_end_data(priv, cmd, data, dma_addr);
/* Clear flags */
writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
@@ -538,11 +541,14 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
u32 desired = mci->clock;
u32 sys_clock = clk_get_rate(priv->clk);
- u32 clk = 0;
+ u32 clk = 0, ddr = 0;
dev_dbg(priv->dev, "%s: bus_width = %d, clock = %d\n", __func__,
mci->bus_width, mci->clock);
+ if (mci_timing_is_ddr(ios->timing))
+ ddr = SDMMC_CLKCR_DDR;
+
if (mci->clock)
stm32_sdmmc2_pwron(priv);
else
@@ -555,13 +561,15 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
* clk_div > 0 and NEGEDGE = 1 => command and data generated on
* SDMMCCLK falling edge
*/
- if (desired && (sys_clock > desired ||
+ if (desired && (sys_clock > desired || ddr ||
IS_RISING_EDGE(priv->clk_reg_msk))) {
clk = DIV_ROUND_UP(sys_clock, 2 * desired);
if (clk > SDMMC_CLKCR_CLKDIV_MAX)
clk = SDMMC_CLKCR_CLKDIV_MAX;
}
+ clk |= ddr;
+
if (mci->bus_width == MMC_BUS_WIDTH_4)
clk |= SDMMC_CLKCR_WIDBUS_4;
if (mci->bus_width == MMC_BUS_WIDTH_8)
@@ -574,8 +582,8 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
static int stm32_sdmmc2_probe(struct amba_device *adev,
const struct amba_id *id)
{
- struct device_d *dev = &adev->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = &adev->dev;
+ struct device_node *np = dev->of_node;
struct stm32_sdmmc2_priv *priv;
struct mci_host *mci;
int ret;
@@ -624,6 +632,11 @@ static int stm32_sdmmc2_probe(struct amba_device *adev,
if (mci->f_max >= 52000000)
mci->host_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
+ if (of_property_read_bool(np, "mmc-ddr-3_3v"))
+ mci->host_caps |= MMC_CAP_MMC_3_3V_DDR;
+ if (of_property_read_bool(np, "mmc-ddr-1_8v"))
+ mci->host_caps |= MMC_CAP_MMC_1_8V_DDR;
+
return mci_register(&priv->mci);
priv_free:
diff --git a/drivers/mci/tegra-sdmmc.c b/drivers/mci/tegra-sdmmc.c
index 756fb94c52..e940edf322 100644
--- a/drivers/mci/tegra-sdmmc.c
+++ b/drivers/mci/tegra-sdmmc.c
@@ -113,13 +113,15 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_WRITE) {
- dma_sync_single_for_device((unsigned long)data->src,
+ dma_sync_single_for_device(mci->hw_dev, (unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->src)));
} else {
- dma_sync_single_for_device((unsigned long)data->dest,
+ dma_sync_single_for_device(mci->hw_dev, (unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->dest)));
}
sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT,
@@ -220,10 +222,10 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
+ dma_sync_single_for_cpu(mci->hw_dev, (unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
+ dma_sync_single_for_cpu(mci->hw_dev, (unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
}
@@ -282,7 +284,7 @@ static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
}
-static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
+static int tegra_sdmmc_init(struct mci_host *mci, struct device *dev)
{
struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
void __iomem *regs = host->regs;
@@ -308,9 +310,8 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
/* sdmmc1 and sdmmc3 on T30 need a bit of padctrl init */
- if (of_device_is_compatible(mci->hw_dev->device_node,
- "nvidia,tegra30-sdhci") &&
- ((u32)regs == 0x78000000 || (u32)regs == 78000400)) {
+ if (of_device_is_compatible(mci->hw_dev->of_node, "nvidia,tegra30-sdhci") &&
+ (regs == IOMEM(0x78000000) || regs == IOMEM(0x78000400))) {
val = readl(regs + TEGRA_SDMMC_SDMEMCOMPPADCTRL);
val &= 0xfffffff0;
val |= 0x7 << TEGRA_SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_SHIFT;
@@ -366,14 +367,14 @@ static int tegra_sdmmc_card_present(struct mci_host *mci)
static void tegra_sdmmc_parse_dt(struct tegra_sdmmc_host *host)
{
- struct device_node *np = host->mci.hw_dev->device_node;
+ struct device_node *np = host->mci.hw_dev->of_node;
host->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
host->gpio_pwr = of_get_named_gpio(np, "power-gpios", 0);
mci_of_parse(&host->mci);
}
-static int tegra_sdmmc_probe(struct device_d *dev)
+static int tegra_sdmmc_probe(struct device *dev)
{
struct resource *iores;
struct tegra_sdmmc_host *host;
@@ -451,8 +452,9 @@ static __maybe_unused struct of_device_id tegra_sdmmc_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra_sdmmc_compatible);
-static struct driver_d tegra_sdmmc_driver = {
+static struct driver tegra_sdmmc_driver = {
.name = "tegra-sdmmc",
.probe = tegra_sdmmc_probe,
.of_compatible = DRV_OF_COMPAT(tegra_sdmmc_compatible),
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index e27695e981..70c5846e69 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -1,6 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "Memory controller drivers"
+config ATMEL_EBI
+ bool "Atmel EBI driver"
+ default y if ARCH_AT91
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on OFDEVICE
+ select MFD_SYSCON
+ select MFD_ATMEL_SMC
+ help
+ Driver for Atmel EBI controller.
+ Used to configure the EBI (external bus interface) when the device-
+ tree is used. This bus supports NANDs, external ethernet controller,
+ SRAMs, ATA devices, etc.
+
+
config MC_TEGRA124
bool "Support for Tegra124 memory controller"
depends on ARCH_TEGRA || COMPILE_TEST
@@ -9,4 +23,15 @@ config MC_TEGRA124
the Tegra124 SoC. This driver performs the necessary initialization
to provide a function GPU when the OS is running.
+config STM32_FMC2_EBI
+ bool "Support for FMC2 External Bus Interface on STM32MP SoCs"
+ depends on ARCH_STM32MP || COMPILE_TEST
+ select RESET_CONTROLLER if ARCH_STM32MP
+ select RESET_SIMPLE if ARCH_STM32MP
+ help
+ Select this option to enable the STM32 FMC2 External Bus Interface
+ controller. This driver configures the transactions with external
+ devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
+ SOCs containing the FMC2 External Bus Interface.
+
endmenu
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 39d1ba08ea..67d3c47621 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
obj-$(CONFIG_MC_TEGRA124) += mc-tegra124.o
+obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c
new file mode 100644
index 0000000000..293c3d7f9d
--- /dev/null
+++ b/drivers/memory/atmel-ebi.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * EBI driver for Atmel chips
+ * inspired by the fsl weim bus driver
+ *
+ * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ */
+
+#include <linux/clk.h>
+#include <io.h>
+#include <mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <init.h>
+#include <driver.h>
+#include <of_device.h>
+#include <linux/regmap.h>
+#include <soc/at91/atmel-sfr.h>
+#include <linux/time.h>
+#include <linux/printk.h>
+
+#define AT91_EBI_NUM_CS 8
+
+struct atmel_ebi_dev_config {
+ int cs;
+ struct atmel_smc_cs_conf smcconf;
+};
+
+struct atmel_ebi;
+
+struct atmel_ebi_dev {
+ struct list_head node;
+ struct atmel_ebi *ebi;
+ u32 mode;
+ int numcs;
+ struct atmel_ebi_dev_config configs[];
+};
+
+struct atmel_ebi_caps {
+ unsigned int available_cs;
+ unsigned int ebi_csa_offs;
+ const char *regmap_name;
+ void (*get_config)(struct atmel_ebi_dev *ebid,
+ struct atmel_ebi_dev_config *conf);
+ int (*xlate_config)(struct atmel_ebi_dev *ebid,
+ struct device_node *configs_np,
+ struct atmel_ebi_dev_config *conf);
+ void (*apply_config)(struct atmel_ebi_dev *ebid,
+ struct atmel_ebi_dev_config *conf);
+};
+
+struct atmel_ebi {
+ struct clk *clk;
+ struct regmap *regmap;
+ struct {
+ struct regmap *regmap;
+ struct clk *clk;
+ const struct atmel_hsmc_reg_layout *layout;
+ } smc;
+
+ struct device *dev;
+ const struct atmel_ebi_caps *caps;
+ struct list_head devs;
+};
+
+struct atmel_smc_timing_xlate {
+ const char *name;
+ int (*converter)(struct atmel_smc_cs_conf *conf,
+ unsigned int shift, unsigned int nycles);
+ unsigned int shift;
+};
+
+#define ATMEL_SMC_SETUP_XLATE(nm, pos) \
+ { .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos}
+
+#define ATMEL_SMC_PULSE_XLATE(nm, pos) \
+ { .name = nm, .converter = atmel_smc_cs_conf_set_pulse, .shift = pos}
+
+#define ATMEL_SMC_CYCLE_XLATE(nm, pos) \
+ { .name = nm, .converter = atmel_smc_cs_conf_set_cycle, .shift = pos}
+
+static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t flags)
+{
+ return kzalloc(size, flags);
+}
+
+static void at91sam9_ebi_get_config(struct atmel_ebi_dev *ebid,
+ struct atmel_ebi_dev_config *conf)
+{
+ atmel_smc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs,
+ &conf->smcconf);
+}
+
+static void sama5_ebi_get_config(struct atmel_ebi_dev *ebid,
+ struct atmel_ebi_dev_config *conf)
+{
+ atmel_hsmc_cs_conf_get(ebid->ebi->smc.regmap, ebid->ebi->smc.layout,
+ conf->cs, &conf->smcconf);
+}
+
+static const struct atmel_smc_timing_xlate timings_xlate_table[] = {
+ ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-rd-setup-ns",
+ ATMEL_SMC_NCS_RD_SHIFT),
+ ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-wr-setup-ns",
+ ATMEL_SMC_NCS_WR_SHIFT),
+ ATMEL_SMC_SETUP_XLATE("atmel,smc-nrd-setup-ns", ATMEL_SMC_NRD_SHIFT),
+ ATMEL_SMC_SETUP_XLATE("atmel,smc-nwe-setup-ns", ATMEL_SMC_NWE_SHIFT),
+ ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-rd-pulse-ns",
+ ATMEL_SMC_NCS_RD_SHIFT),
+ ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-wr-pulse-ns",
+ ATMEL_SMC_NCS_WR_SHIFT),
+ ATMEL_SMC_PULSE_XLATE("atmel,smc-nrd-pulse-ns", ATMEL_SMC_NRD_SHIFT),
+ ATMEL_SMC_PULSE_XLATE("atmel,smc-nwe-pulse-ns", ATMEL_SMC_NWE_SHIFT),
+ ATMEL_SMC_CYCLE_XLATE("atmel,smc-nrd-cycle-ns", ATMEL_SMC_NRD_SHIFT),
+ ATMEL_SMC_CYCLE_XLATE("atmel,smc-nwe-cycle-ns", ATMEL_SMC_NWE_SHIFT),
+};
+
+static int atmel_ebi_xslate_smc_timings(struct atmel_ebi_dev *ebid,
+ struct device_node *np,
+ struct atmel_smc_cs_conf *smcconf)
+{
+ unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
+ unsigned int clk_period_ns = NSEC_PER_SEC / clk_rate;
+ bool required = false;
+ unsigned int ncycles;
+ int ret, i;
+ u32 val;
+
+ ret = of_property_read_u32(np, "atmel,smc-tdf-ns", &val);
+ if (!ret) {
+ required = true;
+ ncycles = DIV_ROUND_UP(val, clk_period_ns);
+ if (ncycles > ATMEL_SMC_MODE_TDF_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
+ ncycles = ATMEL_SMC_MODE_TDF_MIN;
+
+ smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(timings_xlate_table); i++) {
+ const struct atmel_smc_timing_xlate *xlate;
+
+ xlate = &timings_xlate_table[i];
+
+ ret = of_property_read_u32(np, xlate->name, &val);
+ if (ret) {
+ if (!required)
+ continue;
+ else
+ break;
+ }
+
+ if (!required) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ncycles = DIV_ROUND_UP(val, clk_period_ns);
+ ret = xlate->converter(smcconf, xlate->shift, ncycles);
+ if (ret)
+ goto out;
+ }
+
+out:
+ if (ret) {
+ dev_err(ebid->ebi->dev,
+ "missing or invalid timings definition in %pOF",
+ np);
+ return ret;
+ }
+
+ return required;
+}
+
+static int atmel_ebi_xslate_smc_config(struct atmel_ebi_dev *ebid,
+ struct device_node *np,
+ struct atmel_ebi_dev_config *conf)
+{
+ struct atmel_smc_cs_conf *smcconf = &conf->smcconf;
+ bool required = false;
+ const char *tmp_str;
+ u32 tmp;
+ int ret;
+
+ ret = of_property_read_u32(np, "atmel,smc-bus-width", &tmp);
+ if (!ret) {
+ switch (tmp) {
+ case 8:
+ smcconf->mode |= ATMEL_SMC_MODE_DBW_8;
+ break;
+
+ case 16:
+ smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
+ break;
+
+ case 32:
+ smcconf->mode |= ATMEL_SMC_MODE_DBW_32;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ required = true;
+ }
+
+ if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) {
+ smcconf->mode |= ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str);
+ if (tmp_str && !strcmp(tmp_str, "write")) {
+ smcconf->mode |= ATMEL_SMC_MODE_BAT_WRITE;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-read-mode", &tmp_str);
+ if (tmp_str && !strcmp(tmp_str, "nrd")) {
+ smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-write-mode", &tmp_str);
+ if (tmp_str && !strcmp(tmp_str, "nwe")) {
+ smcconf->mode |= ATMEL_SMC_MODE_WRITEMODE_NWE;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str);
+ if (tmp_str) {
+ if (!strcmp(tmp_str, "frozen"))
+ smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_FROZEN;
+ else if (!strcmp(tmp_str, "ready"))
+ smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_READY;
+ else if (strcmp(tmp_str, "disabled"))
+ return -EINVAL;
+
+ required = true;
+ }
+
+ ret = of_property_read_u32(np, "atmel,smc-page-mode", &tmp);
+ if (!ret) {
+ switch (tmp) {
+ case 4:
+ smcconf->mode |= ATMEL_SMC_MODE_PS_4;
+ break;
+
+ case 8:
+ smcconf->mode |= ATMEL_SMC_MODE_PS_8;
+ break;
+
+ case 16:
+ smcconf->mode |= ATMEL_SMC_MODE_PS_16;
+ break;
+
+ case 32:
+ smcconf->mode |= ATMEL_SMC_MODE_PS_32;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ smcconf->mode |= ATMEL_SMC_MODE_PMEN;
+ required = true;
+ }
+
+ ret = atmel_ebi_xslate_smc_timings(ebid, np, &conf->smcconf);
+ if (ret < 0)
+ return -EINVAL;
+
+ if ((ret > 0 && !required) || (!ret && required)) {
+ dev_err(ebid->ebi->dev, "missing atmel,smc- properties in %pOF",
+ np);
+ return -EINVAL;
+ }
+
+ return required;
+}
+
+static void at91sam9_ebi_apply_config(struct atmel_ebi_dev *ebid,
+ struct atmel_ebi_dev_config *conf)
+{
+ atmel_smc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs,
+ &conf->smcconf);
+}
+
+static void sama5_ebi_apply_config(struct atmel_ebi_dev *ebid,
+ struct atmel_ebi_dev_config *conf)
+{
+ atmel_hsmc_cs_conf_apply(ebid->ebi->smc.regmap, ebid->ebi->smc.layout,
+ conf->cs, &conf->smcconf);
+}
+
+static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
+ int reg_cells)
+{
+ const struct atmel_ebi_caps *caps = ebi->caps;
+ struct atmel_ebi_dev_config conf = { };
+ struct device *dev = ebi->dev;
+ struct atmel_ebi_dev *ebid;
+ unsigned long cslines = 0;
+ int ret, numcs = 0, nentries, i;
+ bool apply = false;
+ u32 cs;
+
+ nentries = of_property_count_elems_of_size(np, "reg",
+ reg_cells * sizeof(u32));
+ for (i = 0; i < nentries; i++) {
+ ret = of_property_read_u32_index(np, "reg", i * reg_cells,
+ &cs);
+ if (ret)
+ return ret;
+
+ if (cs >= AT91_EBI_NUM_CS ||
+ !(ebi->caps->available_cs & BIT(cs))) {
+ dev_err(dev, "invalid reg property in %pOF\n", np);
+ return -EINVAL;
+ }
+
+ if (!test_and_set_bit(cs, &cslines))
+ numcs++;
+ }
+
+ if (!numcs) {
+ dev_err(dev, "invalid reg property in %pOF\n", np);
+ return -EINVAL;
+ }
+
+ ebid = devm_kzalloc(ebi->dev, struct_size(ebid, configs, numcs),
+ GFP_KERNEL);
+ if (!ebid)
+ return -ENOMEM;
+
+ ebid->ebi = ebi;
+ ebid->numcs = numcs;
+
+ ret = caps->xlate_config(ebid, np, &conf);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ apply = true;
+
+ i = 0;
+ for_each_set_bit(cs, &cslines, AT91_EBI_NUM_CS) {
+ ebid->configs[i].cs = cs;
+
+ if (apply) {
+ conf.cs = cs;
+ caps->apply_config(ebid, &conf);
+ }
+
+ caps->get_config(ebid, &ebid->configs[i]);
+
+ /*
+ * Attach the EBI device to the generic SMC logic if at least
+ * one "atmel,smc-" property is present.
+ */
+ if (ebi->caps->ebi_csa_offs && apply)
+ regmap_update_bits(ebi->regmap,
+ ebi->caps->ebi_csa_offs,
+ BIT(cs), 0);
+
+ i++;
+ }
+
+ list_add_tail(&ebid->node, &ebi->devs);
+
+ return 0;
+}
+
+static const struct atmel_ebi_caps at91sam9260_ebi_caps = {
+ .available_cs = 0xff,
+ .ebi_csa_offs = AT91SAM9260_MATRIX_EBICSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps at91sam9261_ebi_caps = {
+ .available_cs = 0xff,
+ .ebi_csa_offs = AT91SAM9261_MATRIX_EBICSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps at91sam9263_ebi0_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa_offs = AT91SAM9263_MATRIX_EBI0CSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps at91sam9263_ebi1_caps = {
+ .available_cs = 0x7,
+ .ebi_csa_offs = AT91SAM9263_MATRIX_EBI1CSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps at91sam9rl_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa_offs = AT91SAM9RL_MATRIX_EBICSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps at91sam9g45_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa_offs = AT91SAM9G45_MATRIX_EBICSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps at91sam9x5_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa_offs = AT91SAM9X5_MATRIX_EBICSA,
+ .regmap_name = "atmel,matrix",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps sama5d3_ebi_caps = {
+ .available_cs = 0xf,
+ .get_config = sama5_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = sama5_ebi_apply_config,
+};
+
+static const struct atmel_ebi_caps sam9x60_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa_offs = AT91_SFR_CCFG_EBICSA,
+ .regmap_name = "microchip,sfr",
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = atmel_ebi_xslate_smc_config,
+ .apply_config = at91sam9_ebi_apply_config,
+};
+
+static const struct of_device_id atmel_ebi_id_table[] = {
+ {
+ .compatible = "atmel,at91sam9260-ebi",
+ .data = &at91sam9260_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9261-ebi",
+ .data = &at91sam9261_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9263-ebi0",
+ .data = &at91sam9263_ebi0_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9263-ebi1",
+ .data = &at91sam9263_ebi1_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9rl-ebi",
+ .data = &at91sam9rl_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9g45-ebi",
+ .data = &at91sam9g45_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9x5-ebi",
+ .data = &at91sam9x5_ebi_caps,
+ },
+ {
+ .compatible = "atmel,sama5d3-ebi",
+ .data = &sama5d3_ebi_caps,
+ },
+ {
+ .compatible = "microchip,sam9x60-ebi",
+ .data = &sam9x60_ebi_caps,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_ebi_id_table);
+
+static int atmel_ebi_probe(struct device *dev)
+{
+ struct device_node *child, *np = dev->of_node, *smc_np;
+ const struct of_device_id *match;
+ struct atmel_ebi *ebi;
+ int ret, reg_cells;
+ struct clk *clk;
+ u32 val;
+
+ match = of_match_device(atmel_ebi_id_table, dev);
+ if (!match || !match->data)
+ return -EINVAL;
+
+ ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL);
+ if (!ebi)
+ return -ENOMEM;
+
+ dev->priv = ebi;
+
+ INIT_LIST_HEAD(&ebi->devs);
+ ebi->caps = match->data;
+ ebi->dev = dev;
+
+ clk = clk_get(dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ebi->clk = clk;
+
+ smc_np = of_parse_phandle(dev->of_node, "atmel,smc", 0);
+
+ ebi->smc.regmap = syscon_node_to_regmap(smc_np);
+ if (IS_ERR(ebi->smc.regmap)) {
+ ret = PTR_ERR(ebi->smc.regmap);
+ goto put_node;
+ }
+
+ ebi->smc.layout = atmel_hsmc_get_reg_layout(smc_np);
+ if (IS_ERR(ebi->smc.layout)) {
+ ret = PTR_ERR(ebi->smc.layout);
+ goto put_node;
+ }
+
+ ebi->smc.clk = of_clk_get(smc_np, 0);
+ if (IS_ERR(ebi->smc.clk)) {
+ if (PTR_ERR(ebi->smc.clk) != -ENOENT) {
+ ret = PTR_ERR(ebi->smc.clk);
+ goto put_node;
+ }
+
+ ebi->smc.clk = NULL;
+ }
+ of_node_put(smc_np);
+ ret = clk_prepare_enable(ebi->smc.clk);
+ if (ret)
+ return ret;
+
+ /*
+ * The sama5d3 does not provide an EBICSA register and thus does need
+ * to access it.
+ */
+ if (ebi->caps->ebi_csa_offs) {
+ ebi->regmap =
+ syscon_regmap_lookup_by_phandle(np,
+ ebi->caps->regmap_name);
+ if (IS_ERR(ebi->regmap))
+ return PTR_ERR(ebi->regmap);
+ }
+
+ ret = of_property_read_u32(np, "#address-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells = val;
+
+ ret = of_property_read_u32(np, "#size-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells += val;
+
+ for_each_available_child_of_node(np, child) {
+ if (!of_find_property(child, "reg", NULL))
+ continue;
+
+ ret = atmel_ebi_dev_setup(ebi, child, reg_cells);
+ if (ret) {
+ dev_err(dev, "failed to configure EBI bus for %pOF, disabling the device",
+ child);
+
+ ret = of_device_disable(child);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+ }
+
+ return of_platform_populate(np, NULL, dev);
+
+put_node:
+ of_node_put(smc_np);
+ return ret;
+}
+
+static struct driver atmel_ebi_driver = {
+ .name = "atmel-ebi",
+ .of_match_table = atmel_ebi_id_table,
+ .probe = atmel_ebi_probe,
+};
+coredevice_platform_driver(atmel_ebi_driver);
diff --git a/drivers/memory/mc-tegra124.c b/drivers/memory/mc-tegra124.c
index 9a06fd03d8..e527ed4f9a 100644
--- a/drivers/memory/mc-tegra124.c
+++ b/drivers/memory/mc-tegra124.c
@@ -23,7 +23,7 @@ static int tegra124_mc_of_fixup(struct device_node *root, void *context)
return 0;
}
-static int tegra124_mc_probe(struct device_d *dev)
+static int tegra124_mc_probe(struct device *dev)
{
struct resource *iores;
void __iomem *base;
@@ -50,8 +50,9 @@ static __maybe_unused struct of_device_id tegra124_mc_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, tegra124_mc_dt_ids);
-static struct driver_d tegra124_mc_driver = {
+static struct driver tegra124_mc_driver = {
.name = "tegra124-mc",
.of_compatible = DRV_OF_COMPAT(tegra124_mc_dt_ids),
.probe = tegra124_mc_probe,
diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c
new file mode 100644
index 0000000000..5fee805261
--- /dev/null
+++ b/drivers/memory/stm32-fmc2-ebi.c
@@ -0,0 +1,1136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2020
+ */
+
+#include <common.h>
+#include <init.h>
+#include <regmap.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <mfd/syscon.h>
+
+/* FMC2 Controller Registers */
+#define FMC2_BCR1 0x0
+#define FMC2_BTR1 0x4
+#define FMC2_BCR(x) ((x) * 0x8 + FMC2_BCR1)
+#define FMC2_BTR(x) ((x) * 0x8 + FMC2_BTR1)
+#define FMC2_PCSCNTR 0x20
+#define FMC2_BWTR1 0x104
+#define FMC2_BWTR(x) ((x) * 0x8 + FMC2_BWTR1)
+
+/* Register: FMC2_BCR1 */
+#define FMC2_BCR1_CCLKEN BIT(20)
+#define FMC2_BCR1_FMC2EN BIT(31)
+
+/* Register: FMC2_BCRx */
+#define FMC2_BCR_MBKEN BIT(0)
+#define FMC2_BCR_MUXEN BIT(1)
+#define FMC2_BCR_MTYP GENMASK(3, 2)
+#define FMC2_BCR_MWID GENMASK(5, 4)
+#define FMC2_BCR_FACCEN BIT(6)
+#define FMC2_BCR_BURSTEN BIT(8)
+#define FMC2_BCR_WAITPOL BIT(9)
+#define FMC2_BCR_WAITCFG BIT(11)
+#define FMC2_BCR_WREN BIT(12)
+#define FMC2_BCR_WAITEN BIT(13)
+#define FMC2_BCR_EXTMOD BIT(14)
+#define FMC2_BCR_ASYNCWAIT BIT(15)
+#define FMC2_BCR_CPSIZE GENMASK(18, 16)
+#define FMC2_BCR_CBURSTRW BIT(19)
+#define FMC2_BCR_NBLSET GENMASK(23, 22)
+
+/* Register: FMC2_BTRx/FMC2_BWTRx */
+#define FMC2_BXTR_ADDSET GENMASK(3, 0)
+#define FMC2_BXTR_ADDHLD GENMASK(7, 4)
+#define FMC2_BXTR_DATAST GENMASK(15, 8)
+#define FMC2_BXTR_BUSTURN GENMASK(19, 16)
+#define FMC2_BTR_CLKDIV GENMASK(23, 20)
+#define FMC2_BTR_DATLAT GENMASK(27, 24)
+#define FMC2_BXTR_ACCMOD GENMASK(29, 28)
+#define FMC2_BXTR_DATAHLD GENMASK(31, 30)
+
+/* Register: FMC2_PCSCNTR */
+#define FMC2_PCSCNTR_CSCOUNT GENMASK(15, 0)
+#define FMC2_PCSCNTR_CNTBEN(x) BIT((x) + 16)
+
+#define FMC2_MAX_EBI_CE 4
+#define FMC2_MAX_BANKS 5
+
+#define FMC2_BCR_CPSIZE_0 0x0
+#define FMC2_BCR_CPSIZE_128 0x1
+#define FMC2_BCR_CPSIZE_256 0x2
+#define FMC2_BCR_CPSIZE_512 0x3
+#define FMC2_BCR_CPSIZE_1024 0x4
+
+#define FMC2_BCR_MWID_8 0x0
+#define FMC2_BCR_MWID_16 0x1
+
+#define FMC2_BCR_MTYP_SRAM 0x0
+#define FMC2_BCR_MTYP_PSRAM 0x1
+#define FMC2_BCR_MTYP_NOR 0x2
+
+#define FMC2_BXTR_EXTMOD_A 0x0
+#define FMC2_BXTR_EXTMOD_B 0x1
+#define FMC2_BXTR_EXTMOD_C 0x2
+#define FMC2_BXTR_EXTMOD_D 0x3
+
+#define FMC2_BCR_NBLSET_MAX 0x3
+#define FMC2_BXTR_ADDSET_MAX 0xf
+#define FMC2_BXTR_ADDHLD_MAX 0xf
+#define FMC2_BXTR_DATAST_MAX 0xff
+#define FMC2_BXTR_BUSTURN_MAX 0xf
+#define FMC2_BXTR_DATAHLD_MAX 0x3
+#define FMC2_BTR_CLKDIV_MAX 0xf
+#define FMC2_BTR_DATLAT_MAX 0xf
+#define FMC2_PCSCNTR_CSCOUNT_MAX 0xff
+
+enum stm32_fmc2_ebi_bank {
+ FMC2_EBI1 = 0,
+ FMC2_EBI2,
+ FMC2_EBI3,
+ FMC2_EBI4,
+ FMC2_NAND
+};
+
+enum stm32_fmc2_ebi_register_type {
+ FMC2_REG_BCR = 1,
+ FMC2_REG_BTR,
+ FMC2_REG_BWTR,
+ FMC2_REG_PCSCNTR
+};
+
+enum stm32_fmc2_ebi_transaction_type {
+ FMC2_ASYNC_MODE_1_SRAM = 0,
+ FMC2_ASYNC_MODE_1_PSRAM,
+ FMC2_ASYNC_MODE_A_SRAM,
+ FMC2_ASYNC_MODE_A_PSRAM,
+ FMC2_ASYNC_MODE_2_NOR,
+ FMC2_ASYNC_MODE_B_NOR,
+ FMC2_ASYNC_MODE_C_NOR,
+ FMC2_ASYNC_MODE_D_NOR,
+ FMC2_SYNC_READ_SYNC_WRITE_PSRAM,
+ FMC2_SYNC_READ_ASYNC_WRITE_PSRAM,
+ FMC2_SYNC_READ_SYNC_WRITE_NOR,
+ FMC2_SYNC_READ_ASYNC_WRITE_NOR
+};
+
+enum stm32_fmc2_ebi_buswidth {
+ FMC2_BUSWIDTH_8 = 8,
+ FMC2_BUSWIDTH_16 = 16
+};
+
+enum stm32_fmc2_ebi_cpsize {
+ FMC2_CPSIZE_0 = 0,
+ FMC2_CPSIZE_128 = 128,
+ FMC2_CPSIZE_256 = 256,
+ FMC2_CPSIZE_512 = 512,
+ FMC2_CPSIZE_1024 = 1024
+};
+
+struct stm32_fmc2_ebi {
+ struct device *dev;
+ struct clk *clk;
+ struct regmap *regmap;
+ u8 bank_assigned;
+
+ u32 bcr[FMC2_MAX_EBI_CE];
+ u32 btr[FMC2_MAX_EBI_CE];
+ u32 bwtr[FMC2_MAX_EBI_CE];
+ u32 pcscntr;
+};
+
+/*
+ * struct stm32_fmc2_prop - STM32 FMC2 EBI property
+ * @name: the device tree binding name of the property
+ * @bprop: indicate that it is a boolean property
+ * @mprop: indicate that it is a mandatory property
+ * @reg_type: the register that have to be modified
+ * @reg_mask: the bit that have to be modified in the selected register
+ * in case of it is a boolean property
+ * @reset_val: the default value that have to be set in case the property
+ * has not been defined in the device tree
+ * @check: this callback ckecks that the property is compliant with the
+ * transaction type selected
+ * @calculate: this callback is called to calculate for exemple a timing
+ * set in nanoseconds in the device tree in clock cycles or in
+ * clock period
+ * @set: this callback applies the values in the registers
+ */
+struct stm32_fmc2_prop {
+ const char *name;
+ bool bprop;
+ bool mprop;
+ int reg_type;
+ u32 reg_mask;
+ u32 reset_val;
+ int (*check)(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop, int cs);
+ u32 (*calculate)(struct stm32_fmc2_ebi *ebi, int cs, u32 setup);
+ int (*set)(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup);
+};
+
+static int stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr;
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+
+ if (bcr & FMC2_BCR_MTYP)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+
+ if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr;
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+
+ if (bcr & FMC2_BCR_BURSTEN)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr;
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+
+ if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+
+ if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr, bxtr, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+ if (prop->reg_type == FMC2_REG_BWTR)
+ regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr);
+ else
+ regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr);
+
+ if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) &&
+ ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 bcr, bcr1;
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+ if (cs)
+ regmap_read(ebi->regmap, FMC2_BCR1, &bcr1);
+ else
+ bcr1 = bcr;
+
+ if (bcr & FMC2_BCR_BURSTEN && (!cs || !(bcr1 & FMC2_BCR1_CCLKEN)))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_cclk(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ if (cs)
+ return -EINVAL;
+
+ return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs);
+}
+
+static u32 stm32_fmc2_ebi_ns_to_clock_cycles(struct stm32_fmc2_ebi *ebi,
+ int cs, u32 setup)
+{
+ unsigned long hclk = clk_get_rate(ebi->clk);
+ unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
+
+ return DIV_ROUND_UP(setup * 1000, hclkp);
+}
+
+static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi,
+ int cs, u32 setup)
+{
+ u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup);
+ u32 bcr, btr, clk_period;
+
+ regmap_read(ebi->regmap, FMC2_BCR1, &bcr);
+ if (bcr & FMC2_BCR1_CCLKEN || !cs)
+ regmap_read(ebi->regmap, FMC2_BTR1, &btr);
+ else
+ regmap_read(ebi->regmap, FMC2_BTR(cs), &btr);
+
+ clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1;
+
+ return DIV_ROUND_UP(nb_clk_cycles, clk_period);
+}
+
+static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg)
+{
+ switch (reg_type) {
+ case FMC2_REG_BCR:
+ *reg = FMC2_BCR(cs);
+ break;
+ case FMC2_REG_BTR:
+ *reg = FMC2_BTR(cs);
+ break;
+ case FMC2_REG_BWTR:
+ *reg = FMC2_BWTR(cs);
+ break;
+ case FMC2_REG_PCSCNTR:
+ *reg = FMC2_PCSCNTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_bit_field(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 reg;
+ int ret;
+
+ ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(ebi->regmap, reg, prop->reg_mask,
+ setup ? prop->reg_mask : 0);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_trans_type(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 bcr_mask, bcr = FMC2_BCR_WREN;
+ u32 btr_mask, btr = 0;
+ u32 bwtr_mask, bwtr = 0;
+
+ bwtr_mask = FMC2_BXTR_ACCMOD;
+ btr_mask = FMC2_BXTR_ACCMOD;
+ bcr_mask = FMC2_BCR_MUXEN | FMC2_BCR_MTYP | FMC2_BCR_FACCEN |
+ FMC2_BCR_WREN | FMC2_BCR_WAITEN | FMC2_BCR_BURSTEN |
+ FMC2_BCR_EXTMOD | FMC2_BCR_CBURSTRW;
+
+ switch (setup) {
+ case FMC2_ASYNC_MODE_1_SRAM:
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
+ /*
+ * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+ */
+ break;
+ case FMC2_ASYNC_MODE_1_PSRAM:
+ /*
+ * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+ break;
+ case FMC2_ASYNC_MODE_A_SRAM:
+ /*
+ * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
+ bcr |= FMC2_BCR_EXTMOD;
+ btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+ bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+ break;
+ case FMC2_ASYNC_MODE_A_PSRAM:
+ /*
+ * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+ bcr |= FMC2_BCR_EXTMOD;
+ btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+ bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+ break;
+ case FMC2_ASYNC_MODE_2_NOR:
+ /*
+ * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+ bcr |= FMC2_BCR_FACCEN;
+ break;
+ case FMC2_ASYNC_MODE_B_NOR:
+ /*
+ * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 1
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+ bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
+ btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
+ bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
+ break;
+ case FMC2_ASYNC_MODE_C_NOR:
+ /*
+ * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 2
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+ bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
+ btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
+ bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
+ break;
+ case FMC2_ASYNC_MODE_D_NOR:
+ /*
+ * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+ * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 3
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+ bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
+ btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+ bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+ break;
+ case FMC2_SYNC_READ_SYNC_WRITE_PSRAM:
+ /*
+ * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+ bcr |= FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
+ break;
+ case FMC2_SYNC_READ_ASYNC_WRITE_PSRAM:
+ /*
+ * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+ bcr |= FMC2_BCR_BURSTEN;
+ break;
+ case FMC2_SYNC_READ_SYNC_WRITE_NOR:
+ /*
+ * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+ bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
+ break;
+ case FMC2_SYNC_READ_ASYNC_WRITE_NOR:
+ /*
+ * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
+ * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+ */
+ bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+ bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN;
+ break;
+ default:
+ /* Type of transaction not supported */
+ return -EINVAL;
+ }
+
+ if (bcr & FMC2_BCR_EXTMOD)
+ regmap_update_bits(ebi->regmap, FMC2_BWTR(cs),
+ bwtr_mask, bwtr);
+ regmap_update_bits(ebi->regmap, FMC2_BTR(cs), btr_mask, btr);
+ regmap_update_bits(ebi->regmap, FMC2_BCR(cs), bcr_mask, bcr);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_buswidth(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val;
+
+ switch (setup) {
+ case FMC2_BUSWIDTH_8:
+ val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_8);
+ break;
+ case FMC2_BUSWIDTH_16:
+ val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_16);
+ break;
+ default:
+ /* Buswidth not supported */
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_MWID, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_cpsize(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val;
+
+ switch (setup) {
+ case FMC2_CPSIZE_0:
+ val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_0);
+ break;
+ case FMC2_CPSIZE_128:
+ val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_128);
+ break;
+ case FMC2_CPSIZE_256:
+ val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_256);
+ break;
+ case FMC2_CPSIZE_512:
+ val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_512);
+ break;
+ case FMC2_CPSIZE_1024:
+ val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_1024);
+ break;
+ default:
+ /* Cpsize not supported */
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_CPSIZE, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_bl_setup(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val;
+
+ val = min_t(u32, setup, FMC2_BCR_NBLSET_MAX);
+ val = FIELD_PREP(FMC2_BCR_NBLSET, val);
+ regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_NBLSET, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 bcr, bxtr, reg;
+ u32 val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+ int ret;
+
+ ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+ if (ret)
+ return ret;
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+ if (prop->reg_type == FMC2_REG_BWTR)
+ regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr);
+ else
+ regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr);
+
+ if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)
+ val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX);
+ else
+ val = min_t(u32, setup, FMC2_BXTR_ADDSET_MAX);
+ val = FIELD_PREP(FMC2_BXTR_ADDSET, val);
+ regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_ADDSET, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_address_hold(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+ if (ret)
+ return ret;
+
+ val = clamp_val(setup, 1, FMC2_BXTR_ADDHLD_MAX);
+ val = FIELD_PREP(FMC2_BXTR_ADDHLD, val);
+ regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_ADDHLD, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_data_setup(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+ if (ret)
+ return ret;
+
+ val = clamp_val(setup, 1, FMC2_BXTR_DATAST_MAX);
+ val = FIELD_PREP(FMC2_BXTR_DATAST, val);
+ regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_DATAST, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_bus_turnaround(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+ if (ret)
+ return ret;
+
+ val = setup ? min_t(u32, setup - 1, FMC2_BXTR_BUSTURN_MAX) : 0;
+ val = FIELD_PREP(FMC2_BXTR_BUSTURN, val);
+ regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_BUSTURN, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_data_hold(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+ if (ret)
+ return ret;
+
+ if (prop->reg_type == FMC2_REG_BWTR)
+ val = setup ? min_t(u32, setup - 1, FMC2_BXTR_DATAHLD_MAX) : 0;
+ else
+ val = min_t(u32, setup, FMC2_BXTR_DATAHLD_MAX);
+ val = FIELD_PREP(FMC2_BXTR_DATAHLD, val);
+ regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_DATAHLD, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val;
+
+ val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1;
+ val = FIELD_PREP(FMC2_BTR_CLKDIV, val);
+ regmap_update_bits(ebi->regmap, FMC2_BTR(cs), FMC2_BTR_CLKDIV, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val;
+
+ val = setup > 1 ? min_t(u32, setup - 2, FMC2_BTR_DATLAT_MAX) : 0;
+ val = FIELD_PREP(FMC2_BTR_DATLAT, val);
+ regmap_update_bits(ebi->regmap, FMC2_BTR(cs), FMC2_BTR_DATLAT, val);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 old_val, new_val, pcscntr;
+
+ if (setup < 1)
+ return 0;
+
+ regmap_read(ebi->regmap, FMC2_PCSCNTR, &pcscntr);
+
+ /* Enable counter for the bank */
+ regmap_update_bits(ebi->regmap, FMC2_PCSCNTR,
+ FMC2_PCSCNTR_CNTBEN(cs),
+ FMC2_PCSCNTR_CNTBEN(cs));
+
+ new_val = min_t(u32, setup - 1, FMC2_PCSCNTR_CSCOUNT_MAX);
+ old_val = FIELD_GET(FMC2_PCSCNTR_CSCOUNT, pcscntr);
+ if (old_val && new_val > old_val)
+ /* Keep current counter value */
+ return 0;
+
+ new_val = FIELD_PREP(FMC2_PCSCNTR_CSCOUNT, new_val);
+ regmap_update_bits(ebi->regmap, FMC2_PCSCNTR,
+ FMC2_PCSCNTR_CSCOUNT, new_val);
+
+ return 0;
+}
+
+static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = {
+ /* st,fmc2-ebi-cs-trans-type must be the first property */
+ {
+ .name = "st,fmc2-ebi-cs-transaction-type",
+ .mprop = true,
+ .set = stm32_fmc2_ebi_set_trans_type,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-cclk-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR1_CCLKEN,
+ .check = stm32_fmc2_ebi_check_cclk,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-mux-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_MUXEN,
+ .check = stm32_fmc2_ebi_check_mux,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-buswidth",
+ .reset_val = FMC2_BUSWIDTH_16,
+ .set = stm32_fmc2_ebi_set_buswidth,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-waitpol-high",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_WAITPOL,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-waitcfg-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_WAITCFG,
+ .check = stm32_fmc2_ebi_check_waitcfg,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-wait-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_WAITEN,
+ .check = stm32_fmc2_ebi_check_sync_trans,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-asyncwait-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_ASYNCWAIT,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-cpsize",
+ .check = stm32_fmc2_ebi_check_cpsize,
+ .set = stm32_fmc2_ebi_set_cpsize,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-byte-lane-setup-ns",
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_bl_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-address-setup-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_ADDSET_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-address-hold-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_ADDHLD_MAX,
+ .check = stm32_fmc2_ebi_check_address_hold,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-data-setup-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_DATAST_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-bus-turnaround-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_bus_turnaround,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-data-hold-ns",
+ .reg_type = FMC2_REG_BTR,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-clk-period-ns",
+ .reset_val = FMC2_BTR_CLKDIV_MAX + 1,
+ .check = stm32_fmc2_ebi_check_clk_period,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_clk_period,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-data-latency-ns",
+ .check = stm32_fmc2_ebi_check_sync_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clk_period,
+ .set = stm32_fmc2_ebi_set_data_latency,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-address-setup-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_ADDSET_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-address-hold-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_ADDHLD_MAX,
+ .check = stm32_fmc2_ebi_check_address_hold,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-data-setup-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_DATAST_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-bus-turnaround-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_bus_turnaround,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-data-hold-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-max-low-pulse-ns",
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_max_low_pulse,
+ },
+};
+
+static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi,
+ struct device_node *dev_node,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ struct device *dev = ebi->dev;
+ u32 setup = 0;
+
+ if (!prop->set) {
+ dev_err(dev, "property %s is not well defined\n", prop->name);
+ return -EINVAL;
+ }
+
+ if (prop->check && prop->check(ebi, prop, cs))
+ /* Skeep this property */
+ return 0;
+
+ if (prop->bprop) {
+ bool bprop;
+
+ bprop = of_property_read_bool(dev_node, prop->name);
+ if (prop->mprop && !bprop) {
+ dev_err(dev, "mandatory property %s not defined in the device tree\n",
+ prop->name);
+ return -EINVAL;
+ }
+
+ if (bprop)
+ setup = 1;
+ } else {
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(dev_node, prop->name, &val);
+ if (prop->mprop && ret) {
+ dev_err(dev, "mandatory property %s not defined in the device tree\n",
+ prop->name);
+ return ret;
+ }
+
+ if (ret)
+ setup = prop->reset_val;
+ else if (prop->calculate)
+ setup = prop->calculate(ebi, cs, val);
+ else
+ setup = val;
+ }
+
+ return prop->set(ebi, prop, cs, setup);
+}
+
+static void stm32_fmc2_ebi_enable_bank(struct stm32_fmc2_ebi *ebi, int cs)
+{
+ regmap_update_bits(ebi->regmap, FMC2_BCR(cs),
+ FMC2_BCR_MBKEN, FMC2_BCR_MBKEN);
+}
+
+static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs)
+{
+ regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_MBKEN, 0);
+}
+
+static void stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi)
+{
+ unsigned int cs;
+
+ for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) {
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &ebi->bcr[cs]);
+ regmap_read(ebi->regmap, FMC2_BTR(cs), &ebi->btr[cs]);
+ regmap_read(ebi->regmap, FMC2_BWTR(cs), &ebi->bwtr[cs]);
+ }
+
+ regmap_read(ebi->regmap, FMC2_PCSCNTR, &ebi->pcscntr);
+}
+
+static void stm32_fmc2_ebi_disable_banks(struct stm32_fmc2_ebi *ebi)
+{
+ unsigned int cs;
+
+ for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) {
+ if (!(ebi->bank_assigned & BIT(cs)))
+ continue;
+
+ stm32_fmc2_ebi_disable_bank(ebi, cs);
+ }
+}
+
+/* NWAIT signal can not be connected to EBI controller and NAND controller */
+static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
+{
+ unsigned int cs;
+ u32 bcr;
+
+ for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) {
+ if (!(ebi->bank_assigned & BIT(cs)))
+ continue;
+
+ regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr);
+ if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) &&
+ ebi->bank_assigned & BIT(FMC2_NAND))
+ return true;
+ }
+
+ return false;
+}
+
+static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi)
+{
+ regmap_update_bits(ebi->regmap, FMC2_BCR1,
+ FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN);
+}
+
+static void stm32_fmc2_ebi_disable(struct stm32_fmc2_ebi *ebi)
+{
+ regmap_update_bits(ebi->regmap, FMC2_BCR1, FMC2_BCR1_FMC2EN, 0);
+}
+
+static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
+ struct device_node *dev_node,
+ u32 cs)
+{
+ unsigned int i;
+ int ret;
+
+ stm32_fmc2_ebi_disable_bank(ebi, cs);
+
+ for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) {
+ const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i];
+
+ ret = stm32_fmc2_ebi_parse_prop(ebi, dev_node, p, cs);
+ if (ret) {
+ dev_err(ebi->dev, "property %s could not be set: %d\n",
+ p->name, ret);
+ return ret;
+ }
+ }
+
+ stm32_fmc2_ebi_enable_bank(ebi, cs);
+
+ return 0;
+}
+
+static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi)
+{
+ struct device *dev = ebi->dev;
+ struct device_node *child;
+ bool child_found = false;
+ u32 bank;
+ int ret;
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ ret = of_property_read_u32(child, "reg", &bank);
+ if (ret) {
+ dev_err(dev, "could not retrieve reg property: %d\n",
+ ret);
+ of_node_put(child);
+ return ret;
+ }
+
+ if (bank >= FMC2_MAX_BANKS) {
+ dev_err(dev, "invalid reg value: %d\n", bank);
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ if (ebi->bank_assigned & BIT(bank)) {
+ dev_err(dev, "bank already assigned: %d\n", bank);
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ if (bank < FMC2_MAX_EBI_CE) {
+ ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
+ if (ret) {
+ dev_err(dev, "setup chip select %d failed: %d\n",
+ bank, ret);
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ ebi->bank_assigned |= BIT(bank);
+ child_found = true;
+ }
+
+ if (!child_found) {
+ dev_warn(dev, "no subnodes found, disable the driver.\n");
+ return -ENODEV;
+ }
+
+ if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) {
+ dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n");
+ return -EINVAL;
+ }
+
+ stm32_fmc2_ebi_enable(ebi);
+
+ return of_platform_populate(dev->of_node, NULL, dev);
+}
+
+static int stm32_fmc2_ebi_probe(struct device *dev)
+{
+ struct stm32_fmc2_ebi *ebi;
+ int ret;
+
+ ebi = kzalloc(sizeof(*ebi), GFP_KERNEL);
+ if (!ebi)
+ return -ENOMEM;
+
+ ebi->dev = dev;
+
+ ebi->regmap = device_node_to_regmap(dev->of_node);
+ if (IS_ERR(ebi->regmap))
+ return PTR_ERR(ebi->regmap);
+
+ ebi->clk = clk_get(dev, NULL);
+ if (IS_ERR(ebi->clk))
+ return PTR_ERR(ebi->clk);
+
+ ret = device_reset_us(dev, 2);
+ if (ret)
+ return -EINVAL;
+
+ ret = clk_prepare_enable(ebi->clk);
+ if (ret)
+ return ret;
+
+ ret = stm32_fmc2_ebi_parse_dt(ebi);
+ if (ret)
+ goto err_release;
+
+ stm32_fmc2_ebi_save_setup(ebi);
+
+ return 0;
+
+err_release:
+ stm32_fmc2_ebi_disable_banks(ebi);
+ stm32_fmc2_ebi_disable(ebi);
+ clk_disable_unprepare(ebi->clk);
+
+ return ret;
+}
+
+static __maybe_unused struct of_device_id stm32_fmc2_ebi_match[] = {
+ { .compatible = "st,stm32mp1-fmc2-ebi", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stm32_fmc2_ebi_match);
+
+static struct driver stm32_fmc2_ebi_driver = {
+ .name = "stm32_fmc2_ebi",
+ .probe = stm32_fmc2_ebi_probe,
+ .of_compatible = DRV_OF_COMPAT(stm32_fmc2_ebi_match),
+};
+coredevice_platform_driver(stm32_fmc2_ebi_driver);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 5fd1a0aaa8..5189364c2c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -64,12 +64,21 @@ config RAVE_SP_CORE
config MFD_STPMIC1
depends on I2C
+ select REGMAP_I2C
bool "STPMIC1 MFD driver"
help
Select this to support communication with the STPMIC1.
+config MFD_PCA9450
+ depends on I2C
+ select REGMAP_I2C
+ bool "PCA9450 MFD driver"
+ help
+ Select this to support communication with the PCA9450 PMIC.
+
config MFD_RN568PMIC
depends on I2C
+ select REGMAP_I2C
bool "Ricoh RN5T568 MFD driver"
help
Select this to support communication with the Ricoh RN5T568 PMIC.
@@ -111,6 +120,7 @@ config MFD_ATMEL_FLEXCOM
config MFD_RK808
tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power Management Chip"
depends on I2C && OFDEVICE
+ select REGMAP_I2C
help
If you say yes here you get support for the RK805, RK808, RK809,
RK817 and RK818 Power Management chips.
@@ -120,9 +130,27 @@ config MFD_RK808
config MFD_AXP20X_I2C
tristate "X-Powers AXP series PMICs with I2C"
depends on I2C && OFDEVICE
+ select REGMAP_I2C
help
If you say Y here you get support for the X-Powers AXP series power
management ICs (PMICs) controlled with I2C.
This driver currently only provide a character device in /dev.
+config MFD_ROHM_BD718XX
+ tristate "ROHM BD71837 Power Management IC"
+ depends on I2C=y
+ depends on OFDEVICE
+ select REGMAP_I2C
+ help
+ Select this option to get support for the ROHM BD71837
+ Power Management ICs. BD71837 is designed to power processors like
+ NXP i.MX8. It contains 8 BUCK outputs and 7 LDOs, voltage monitoring
+ and emergency shut down as well as 32,768KHz clock output.
+
+ This driver currently only provide a character device in /dev.
+
+config MFD_ATMEL_SMC
+ bool
+ select MFD_SYSCON
+
endmenu
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a3cb90e883..00f3eacf3c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -23,3 +23,6 @@ obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o
obj-$(CONFIG_MFD_RK808) += rk808.o
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o axp20x.o
+obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
+obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
+obj-$(CONFIG_MFD_PCA9450) += pca9450.o
diff --git a/drivers/mfd/act8846.c b/drivers/mfd/act8846.c
index 0e2ac8471e..f15310f507 100644
--- a/drivers/mfd/act8846.c
+++ b/drivers/mfd/act8846.c
@@ -110,7 +110,7 @@ static struct cdev_operations act8846_fops = {
.write = act8846_write,
};
-static int act8846_probe(struct device_d *dev)
+static int act8846_probe(struct device *dev)
{
if (act8846_dev)
return -EBUSY;
@@ -127,7 +127,7 @@ static int act8846_probe(struct device_d *dev)
return 0;
}
-static struct driver_d act8846_driver = {
+static struct driver act8846_driver = {
.name = DRIVERNAME,
.probe = act8846_probe,
};
diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index 996d4850ee..58e94c4889 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -20,14 +20,14 @@
#define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) & \
FLEX_MR_OPMODE_MASK)
-static int atmel_flexcom_probe(struct device_d *dev)
+static int atmel_flexcom_probe(struct device *dev)
{
struct resource *res;
struct clk *clk;
u32 opmode;
int err;
- err = of_property_read_u32(dev->device_node,
+ err = of_property_read_u32(dev->of_node,
"atmel,flexcom-mode", &opmode);
if (err)
return err;
@@ -57,15 +57,16 @@ static int atmel_flexcom_probe(struct device_d *dev)
clk_disable(clk);
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
}
static const struct of_device_id atmel_flexcom_of_match[] = {
{ .compatible = "atmel,sama5d2-flexcom" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
-static struct driver_d atmel_flexcom_driver = {
+static struct driver atmel_flexcom_driver = {
.probe = atmel_flexcom_probe,
.name = "atmel_flexcom",
.of_compatible = atmel_flexcom_of_match,
diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
new file mode 100644
index 0000000000..9432aa2c68
--- /dev/null
+++ b/drivers/mfd/atmel-smc.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Atmel SMC (Static Memory Controller) helper functions.
+ *
+ * Copyright (C) 2017 Atmel
+ * Copyright (C) 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ */
+
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <linux/string.h>
+#include <linux/export.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+
+/**
+ * atmel_smc_cs_conf_init - initialize a SMC CS conf
+ * @conf: the SMC CS conf to initialize
+ *
+ * Set all fields to 0 so that one can start defining a new config.
+ */
+void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
+{
+ memset(conf, 0, sizeof(*conf));
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
+
+/**
+ * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
+ * format expected by the SMC engine
+ * @ncycles: number of MCK clk cycles
+ * @msbpos: position of the MSB part of the timing field
+ * @msbwidth: width of the MSB part of the timing field
+ * @msbfactor: factor applied to the MSB
+ * @encodedval: param used to store the encoding result
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
+ * helper which called with different parameter depending on the encoding
+ * scheme.
+ *
+ * If the @ncycles value is too big to be encoded, -ERANGE is returned and
+ * the encodedval is contains the maximum val. Otherwise, 0 is returned.
+ */
+static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
+ unsigned int msbpos,
+ unsigned int msbwidth,
+ unsigned int msbfactor,
+ unsigned int *encodedval)
+{
+ unsigned int lsbmask = GENMASK(msbpos - 1, 0);
+ unsigned int msbmask = GENMASK(msbwidth - 1, 0);
+ unsigned int msb, lsb;
+ int ret = 0;
+
+ msb = ncycles / msbfactor;
+ lsb = ncycles % msbfactor;
+
+ if (lsb > lsbmask) {
+ lsb = 0;
+ msb++;
+ }
+
+ /*
+ * Let's just put the maximum we can if the requested setting does
+ * not fit in the register field.
+ * We still return -ERANGE in case the caller cares.
+ */
+ if (msb > msbmask) {
+ msb = msbmask;
+ lsb = lsbmask;
+ ret = -ERANGE;
+ }
+
+ *encodedval = (msb << msbpos) | lsb;
+
+ return ret;
+}
+
+/**
+ * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
+ * specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the Txx field in the TIMINGS register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
+ * parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Timings Register"), and then stores the result in the
+ * @conf->timings field at @shift position.
+ *
+ * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
+ unsigned int shift, unsigned int ncycles)
+{
+ unsigned int val;
+ int ret;
+
+ if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
+ shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
+ shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
+ shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
+ shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
+ return -EINVAL;
+
+ /*
+ * The formula described in atmel datasheets (section "HSMC Timings
+ * Register"):
+ *
+ * ncycles = (Txx[3] * 64) + Txx[2:0]
+ */
+ ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
+ conf->timings &= ~GENMASK(shift + 3, shift);
+ conf->timings |= val << shift;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
+
+/**
+ * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
+ * specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the xx_SETUP field in the SETUP register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
+ * parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Setup Register"), and then stores the result in the
+ * @conf->setup field at @shift position.
+ *
+ * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
+ unsigned int shift, unsigned int ncycles)
+{
+ unsigned int val;
+ int ret;
+
+ if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
+ shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
+ return -EINVAL;
+
+ /*
+ * The formula described in atmel datasheets (section "SMC Setup
+ * Register"):
+ *
+ * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
+ */
+ ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
+ conf->setup &= ~GENMASK(shift + 7, shift);
+ conf->setup |= val << shift;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
+
+/**
+ * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
+ * specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the xx_PULSE field in the PULSE register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
+ * parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Pulse Register"), and then stores the result in the
+ * @conf->setup field at @shift position.
+ *
+ * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
+ unsigned int shift, unsigned int ncycles)
+{
+ unsigned int val;
+ int ret;
+
+ if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
+ shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
+ return -EINVAL;
+
+ /*
+ * The formula described in atmel datasheets (section "SMC Pulse
+ * Register"):
+ *
+ * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
+ */
+ ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
+ conf->pulse &= ~GENMASK(shift + 7, shift);
+ conf->pulse |= val << shift;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
+
+/**
+ * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
+ * specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the xx_CYCLE field in the CYCLE register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
+ * parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Cycle Register"), and then stores the result in the
+ * @conf->setup field at @shift position.
+ *
+ * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
+ unsigned int shift, unsigned int ncycles)
+{
+ unsigned int val;
+ int ret;
+
+ if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
+ return -EINVAL;
+
+ /*
+ * The formula described in atmel datasheets (section "SMC Cycle
+ * Register"):
+ *
+ * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
+ */
+ ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
+ conf->cycle &= ~GENMASK(shift + 15, shift);
+ conf->cycle |= val << shift;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
+
+/**
+ * atmel_smc_cs_conf_apply - apply an SMC CS conf
+ * @regmap: the SMC regmap
+ * @cs: the CS id
+ * @conf: the SMC CS conf to apply
+ *
+ * Applies an SMC CS configuration.
+ * Only valid on at91sam9/avr32 SoCs.
+ */
+void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
+ const struct atmel_smc_cs_conf *conf)
+{
+ regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
+ regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
+ regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
+ regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
+
+/**
+ * atmel_hsmc_cs_conf_apply - apply an SMC CS conf
+ * @regmap: the HSMC regmap
+ * @cs: the CS id
+ * @layout: the layout of registers
+ * @conf: the SMC CS conf to apply
+ *
+ * Applies an SMC CS configuration.
+ * Only valid on post-sama5 SoCs.
+ */
+void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
+ const struct atmel_hsmc_reg_layout *layout,
+ int cs, const struct atmel_smc_cs_conf *conf)
+{
+ regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
+ regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
+ regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
+ regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
+ regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
+
+/**
+ * atmel_smc_cs_conf_get - retrieve the current SMC CS conf
+ * @regmap: the SMC regmap
+ * @cs: the CS id
+ * @conf: the SMC CS conf object to store the current conf
+ *
+ * Retrieve the SMC CS configuration.
+ * Only valid on at91sam9/avr32 SoCs.
+ */
+void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
+ struct atmel_smc_cs_conf *conf)
+{
+ regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
+ regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
+ regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
+ regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
+
+/**
+ * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
+ * @regmap: the HSMC regmap
+ * @cs: the CS id
+ * @layout: the layout of registers
+ * @conf: the SMC CS conf object to store the current conf
+ *
+ * Retrieve the SMC CS configuration.
+ * Only valid on post-sama5 SoCs.
+ */
+void atmel_hsmc_cs_conf_get(struct regmap *regmap,
+ const struct atmel_hsmc_reg_layout *layout,
+ int cs, struct atmel_smc_cs_conf *conf)
+{
+ regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
+ regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
+ regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
+ regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
+ regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
+
+static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
+ .timing_regs_offset = 0x600,
+};
+
+static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
+ .timing_regs_offset = 0x700,
+};
+
+static const struct of_device_id atmel_smc_ids[] = {
+ { .compatible = "atmel,at91sam9260-smc", .data = NULL },
+ { .compatible = "atmel,sama5d3-smc", .data = &sama5d3_reg_layout },
+ { .compatible = "atmel,sama5d2-smc", .data = &sama5d2_reg_layout },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, atmel_smc_ids);
+
+/**
+ * atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
+ * @np: the HSMC regmap
+ *
+ * Retrieve the layout of HSMC registers.
+ *
+ * Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
+ * in HSMC case, otherwise ERR_PTR(-EINVAL).
+ */
+const struct atmel_hsmc_reg_layout *
+atmel_hsmc_get_reg_layout(struct device_node *np)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(atmel_smc_ids, np);
+
+ return match ? match->data : ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(atmel_hsmc_get_reg_layout);
diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c
index d0f6a0f394..bfd93902b4 100644
--- a/drivers/mfd/axp20x-i2c.c
+++ b/drivers/mfd/axp20x-i2c.c
@@ -19,9 +19,9 @@
#include <i2c/i2c.h>
#include <module.h>
#include <linux/mfd/axp20x.h>
-#include <regmap.h>
+#include <linux/regmap.h>
-static int axp20x_i2c_probe(struct device_d *dev)
+static int axp20x_i2c_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct axp20x_dev *axp20x;
@@ -51,14 +51,16 @@ static const struct of_device_id axp20x_i2c_of_match[] = {
{ .compatible = "x-powers,axp152", .data = (void *)AXP152_ID },
{ .compatible = "x-powers,axp202", .data = (void *)AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *)AXP209_ID },
+ { .compatible = "x-powers,axp313a", .data = (void *)AXP313A_ID },
{ .compatible = "x-powers,axp221", .data = (void *)AXP221_ID },
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
{ .compatible = "x-powers,axp803", .data = (void *)AXP803_ID },
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ },
};
+MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match);
-static struct driver_d axp20x_i2c_driver = {
+static struct driver axp20x_i2c_driver = {
.name = "axp20x-i2c",
.probe = axp20x_i2c_probe,
.of_compatible = DRV_OF_COMPAT(axp20x_i2c_of_match),
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index da1e8ce35a..666b9ea98c 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -23,7 +23,7 @@
#include <module.h>
#include <of.h>
#include <of_device.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <regulator.h>
#define AXP20X_OFF BIT(7)
@@ -38,6 +38,7 @@ static const char * const axp20x_model_names[] = {
"AXP221",
"AXP223",
"AXP288",
+ "AXP313A",
"AXP803",
"AXP806",
"AXP809",
@@ -68,6 +69,12 @@ static const struct regmap_config axp288_regmap_config = {
.max_register = AXP288_FG_TUNE5,
};
+static const struct regmap_config axp313a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AXP313A_POK_CONTROL,
+};
+
static const struct regmap_config axp806_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -141,6 +148,9 @@ static const struct mfd_cell axp152_cells[] = {
{
.name = "axp20x-pek",
},
+ {
+ .name = "axp20x-regulator",
+ },
};
static const struct mfd_cell axp288_cells[] = {
@@ -157,6 +167,13 @@ static const struct mfd_cell axp288_cells[] = {
},
};
+static const struct mfd_cell axp313a_cells[] = {
+ {
+ .name = "axp313a-regulator"
+ },
+};
+
+
static const struct mfd_cell axp803_cells[] = {
{
.name = "axp221-pek",
@@ -231,7 +248,7 @@ static void axp20x_power_off(struct poweroff_handler *handler)
int axp20x_match_device(struct axp20x_dev *axp20x)
{
- struct device_d *dev = axp20x->dev;
+ struct device *dev = axp20x->dev;
const struct of_device_id *of_id;
of_id = of_match_device(dev->driver->of_compatible, dev);
@@ -268,6 +285,11 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
axp20x->regmap_cfg = &axp288_regmap_config;
break;
+ case AXP313A_ID:
+ axp20x->cells = axp313a_cells;
+ axp20x->nr_cells = ARRAY_SIZE(axp313a_cells);
+ axp20x->regmap_cfg = &axp313a_regmap_config;
+ break;
case AXP803_ID:
axp20x->nr_cells = ARRAY_SIZE(axp803_cells);
axp20x->cells = axp803_cells;
@@ -326,9 +348,9 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
* property "x-powers,master-mode" to override the default.
*/
if (axp20x->variant == AXP806_ID) {
- if (of_property_read_bool(axp20x->dev->device_node,
+ if (of_property_read_bool(axp20x->dev->of_node,
"x-powers,master-mode") ||
- of_property_read_bool(axp20x->dev->device_node,
+ of_property_read_bool(axp20x->dev->of_node,
"x-powers,self-working-mode"))
regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE);
@@ -337,6 +359,8 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
}
+ axp20x->dev->priv = axp20x;
+
ret = mfd_add_devices(axp20x->dev, axp20x->cells, axp20x->nr_cells);
if (ret)
return dev_err_probe(axp20x->dev, ret, "failed to add MFD devices\n");
@@ -346,11 +370,9 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
axp20x->poweroff.poweroff = axp20x_power_off;
axp20x->poweroff.priority = 200;
- if (axp20x->variant != AXP288_ID)
+ if (!(axp20x->variant == AXP288_ID) || (axp20x->variant == AXP313A_ID))
poweroff_handler_register(&axp20x->poweroff);
- dev_info(axp20x->dev, "AXP20X driver loaded\n");
-
return 0;
}
EXPORT_SYMBOL(axp20x_device_probe);
diff --git a/drivers/mfd/core.c b/drivers/mfd/core.c
index 495427ae9c..0868bbb905 100644
--- a/drivers/mfd/core.c
+++ b/drivers/mfd/core.c
@@ -3,9 +3,10 @@
#include <linux/mfd/core.h>
#include <driver.h>
-int mfd_add_devices(struct device_d *parent, const struct mfd_cell *cells, int n_devs)
+int mfd_add_devices(struct device *parent, const struct mfd_cell *cells,
+ int n_devs)
{
- struct device_d *dev;
+ struct device *dev;
int ret, i;
for (i = 0; i < n_devs; i++) {
diff --git a/drivers/mfd/da9053.c b/drivers/mfd/da9053.c
index 99827c9689..cbfb62cef9 100644
--- a/drivers/mfd/da9053.c
+++ b/drivers/mfd/da9053.c
@@ -77,7 +77,7 @@
struct da9053_priv {
struct watchdog wd;
struct i2c_client *client;
- struct device_d *dev;
+ struct device *dev;
struct restart_handler restart;
};
@@ -137,7 +137,7 @@ static int da9053_enable_multiwrite(struct da9053_priv *da9053)
static int da9053_set_timeout(struct watchdog *wd, unsigned timeout)
{
struct da9053_priv *da9053 = wd_to_da9053_priv(wd);
- struct device_d *dev = da9053->dev;
+ struct device *dev = da9053->dev;
unsigned scale = 0;
int ret;
u8 val;
@@ -251,7 +251,7 @@ static void __noreturn da9053_force_system_reset(struct restart_handler *rst)
hang();
}
-static int da9053_probe(struct device_d *dev)
+static int da9053_probe(struct device *dev)
{
struct da9053_priv *da9053;
int ret;
@@ -272,7 +272,7 @@ static int da9053_probe(struct device_d *dev)
da9053_detect_reset_source(da9053);
- da9053->restart.priority = of_get_restart_priority(dev->device_node);
+ da9053->restart.of_node = dev->of_node;
da9053->restart.name = "da9063";
da9053->restart.restart = &da9053_force_system_reset;
@@ -288,8 +288,9 @@ static __maybe_unused struct of_device_id da9053_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, da9053_dt_ids);
-static struct driver_d da9053_driver = {
+static struct driver da9053_driver = {
.name = DRIVERNAME,
.probe = da9053_probe,
.of_compatible = DRV_OF_COMPAT(da9053_dt_ids),
diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c
index a4e5226f3c..04bcad8804 100644
--- a/drivers/mfd/da9063.c
+++ b/drivers/mfd/da9063.c
@@ -22,7 +22,7 @@ struct da9063 {
struct i2c_client *client;
/* dummy client for accessing bank #1 */
struct i2c_client *client1;
- struct device_d *dev;
+ struct device *dev;
unsigned int timeout;
uint64_t last_ping;
};
@@ -253,7 +253,7 @@ static int da9063_watchdog_ping(struct da9063 *priv)
static int da9063_watchdog_set_timeout(struct watchdog *wd, unsigned timeout)
{
struct da9063 *priv = container_of(wd, struct da9063, wd);
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
unsigned int scale = 0;
int ret;
@@ -356,7 +356,7 @@ static struct da906x_device_data const da9062_device_data = {
.init = da9062_device_init,
};
-static int da9063_probe(struct device_d *dev)
+static int da9063_probe(struct device *dev)
{
struct da9063 *priv = NULL;
struct da906x_device_data const *dev_data;
@@ -383,7 +383,7 @@ static int da9063_probe(struct device_d *dev)
da9063_detect_reset_source(priv);
- priv->restart.priority = of_get_restart_priority(dev->device_node);
+ priv->restart.of_node = dev->of_node;
priv->restart.name = "da9063";
priv->restart.restart = &da9063_restart;
@@ -399,8 +399,8 @@ static int da9063_probe(struct device_d *dev)
goto on_error;
}
- if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node)
- return of_platform_populate(dev->device_node, NULL, dev);
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node)
+ return of_platform_populate(dev->of_node, NULL, dev);
return 0;
@@ -426,8 +426,9 @@ static struct of_device_id const da906x_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, da906x_dt_ids);
-static struct driver_d da9063_driver = {
+static struct driver da9063_driver = {
.name = "da9063",
.probe = da9063_probe,
.id_table = da9063_id,
diff --git a/drivers/mfd/lp3972.c b/drivers/mfd/lp3972.c
index 934c4fe038..d72f697da5 100644
--- a/drivers/mfd/lp3972.c
+++ b/drivers/mfd/lp3972.c
@@ -63,7 +63,7 @@ static struct cdev_operations lp_fops = {
.read = lp_read,
};
-static int lp_probe(struct device_d *dev)
+static int lp_probe(struct device *dev)
{
if (lp_dev)
return -EBUSY;
@@ -80,7 +80,7 @@ static int lp_probe(struct device_d *dev)
return 0;
}
-static struct driver_d lp_driver = {
+static struct driver lp_driver = {
.name = DRIVERNAME,
.probe = lp_probe,
};
diff --git a/drivers/mfd/mc13xxx.c b/drivers/mfd/mc13xxx.c
index 1851950406..1e06a24b45 100644
--- a/drivers/mfd/mc13xxx.c
+++ b/drivers/mfd/mc13xxx.c
@@ -11,7 +11,7 @@
#include <errno.h>
#include <malloc.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <i2c/i2c.h>
#include <spi/spi.h>
@@ -22,7 +22,7 @@
#define MC13XXX_NUMREGS 0x3f
struct mc13xxx {
- struct device_d *dev;
+ struct device *dev;
struct regmap *map;
union {
struct i2c_client *client;
@@ -300,7 +300,7 @@ static const struct regmap_config mc13xxx_regmap_i2c_config = {
};
#endif
-static int __init mc13xxx_probe(struct device_d *dev)
+static int __init mc13xxx_probe(struct device *dev)
{
struct mc13xxx_devtype *devtype;
int ret, rev;
@@ -353,7 +353,7 @@ static int __init mc13xxx_probe(struct device_d *dev)
if (mc13xxx_init_callback)
mc13xxx_init_callback(mc_dev);
- if (of_property_read_bool(dev->device_node, "fsl,mc13xxx-uses-adc"))
+ if (of_property_read_bool(dev->of_node, "fsl,mc13xxx-uses-adc"))
mc13xxx_adc_probe(dev, mc_dev);
return 0;
@@ -387,8 +387,9 @@ static __maybe_unused struct of_device_id mc13xxx_dt_ids[] = {
{ .compatible = "fsl,mc34708", .data = &mc34708_devtype, },
{ }
};
+MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
-static __maybe_unused struct driver_d mc13xxx_i2c_driver = {
+static __maybe_unused struct driver mc13xxx_i2c_driver = {
.name = "mc13xxx-i2c",
.probe = mc13xxx_probe,
.id_table = mc13xxx_ids,
@@ -399,7 +400,7 @@ static __maybe_unused struct driver_d mc13xxx_i2c_driver = {
coredevice_i2c_driver(mc13xxx_i2c_driver);
#endif
-static __maybe_unused struct driver_d mc13xxx_spi_driver = {
+static __maybe_unused struct driver mc13xxx_spi_driver = {
.name = "mc13xxx-spi",
.probe = mc13xxx_probe,
.id_table = mc13xxx_ids,
diff --git a/drivers/mfd/mc34704.c b/drivers/mfd/mc34704.c
index 732542e34f..7a893ef8b7 100644
--- a/drivers/mfd/mc34704.c
+++ b/drivers/mfd/mc34704.c
@@ -93,7 +93,7 @@ static struct cdev_operations mc34704_fops = {
.write = mc34704_write,
};
-static int mc34704_probe(struct device_d *dev)
+static int mc34704_probe(struct device *dev)
{
if (mc34704_dev)
return -EBUSY;
@@ -114,8 +114,9 @@ static __maybe_unused struct of_device_id mc34704_dt_ids[] = {
{ .compatible = "fsl,mc34704", },
{ }
};
+MODULE_DEVICE_TABLE(of, mc34704_dt_ids);
-static struct driver_d mc34704_driver = {
+static struct driver mc34704_driver = {
.name = DRIVERNAME,
.probe = mc34704_probe,
.of_compatible = DRV_OF_COMPAT(mc34704_dt_ids),
diff --git a/drivers/mfd/mc9sdz60.c b/drivers/mfd/mc9sdz60.c
index f55733dae9..1f8a5611b4 100644
--- a/drivers/mfd/mc9sdz60.c
+++ b/drivers/mfd/mc9sdz60.c
@@ -105,7 +105,7 @@ static struct cdev_operations mc_fops = {
.write = mc_write,
};
-static int mc_probe(struct device_d *dev)
+static int mc_probe(struct device *dev)
{
if (mc_dev)
return -EBUSY;
@@ -122,7 +122,7 @@ static int mc_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mc_driver = {
+static struct driver mc_driver = {
.name = DRIVERNAME,
.probe = mc_probe,
};
diff --git a/drivers/mfd/pca9450.c b/drivers/mfd/pca9450.c
new file mode 100644
index 0000000000..8fa5363f8a
--- /dev/null
+++ b/drivers/mfd/pca9450.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Holger Assmann, Pengutronix
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <i2c/i2c.h>
+#include <init.h>
+#include <mfd/pca9450.h>
+#include <of.h>
+#include <regmap.h>
+#include <reset_source.h>
+
+#define REASON_PMIC_RST 0x10
+#define REASON_SW_RST 0x20
+#define REASON_WDOG 0x40
+#define REASON_PWON 0x80
+
+static const struct regmap_config pca9450_regmap_i2c_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x2E,
+};
+
+static int pca9450_get_reset_source(struct device *dev, struct regmap *map)
+{
+ enum reset_src_type type;
+ int reg;
+ int ret;
+
+ ret = regmap_read(map, PCA9450_PWRON_STAT, &reg);
+ if (ret)
+ return ret;
+
+ switch (reg) {
+ case REASON_PWON:
+ dev_dbg(dev, "Power ON triggered by PMIC_ON_REQ.\n");
+ type = RESET_POR;
+ break;
+ case REASON_WDOG:
+ dev_dbg(dev, "Detected cold reset by WDOGB pin\n");
+ type = RESET_WDG;
+ break;
+ case REASON_SW_RST:
+ dev_dbg(dev, "Detected cold reset by SW_RST\n");
+ type = RESET_RST;
+ break;
+ case REASON_PMIC_RST:
+ dev_dbg(dev, "Detected cold reset by PMIC_RST_B\n");
+ type = RESET_EXT;
+ break;
+ default:
+ dev_warn(dev, "Could not determine reset reason.\n");
+ type = RESET_UKWN;
+ }
+
+ reset_source_set_device(dev, type);
+
+ return 0;
+};
+
+static struct regmap *pca9450_map;
+
+static void (*pca9450_init_callback)(struct regmap *map);
+
+int pca9450_register_init_callback(void(*callback)(struct regmap *map))
+{
+ if (pca9450_init_callback)
+ return -EBUSY;
+
+ pca9450_init_callback = callback;
+
+ if (pca9450_map)
+ pca9450_init_callback(pca9450_map);
+
+ return 0;
+}
+
+static int __init pca9450_probe(struct device *dev)
+{
+ struct regmap *regmap;
+ int reg;
+ int ret;
+
+ regmap = regmap_init_i2c(to_i2c_client(dev), &pca9450_regmap_i2c_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regmap_register_cdev(regmap, NULL);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, PCA9450_REG_DEV_ID, &reg);
+ if (ret) {
+ dev_err(dev, "Unable to read PMIC Chip ID\n");
+ return ret;
+ }
+
+ /* Chip ID defined in bits [7:4] */
+ dev_info(dev, "PMIC Chip ID: 0x%x\n", (reg >> 4));
+
+ if (pca9450_init_callback)
+ pca9450_init_callback(regmap);
+ pca9450_map = regmap;
+
+ pca9450_get_reset_source(dev,regmap);
+
+ return of_platform_populate(dev->of_node, NULL, dev);
+}
+
+static __maybe_unused struct of_device_id pca9450_dt_ids[] = {
+ { .compatible = "nxp,pca9450a" },
+ { .compatible = "nxp,pca9450c" },
+ { .compatible = "nxp,pca9451a" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pca9450_dt_ids);
+
+static struct driver pca9450_i2c_driver = {
+ .name = "pca9450-i2c",
+ .probe = pca9450_probe,
+ .of_compatible = DRV_OF_COMPAT(pca9450_dt_ids),
+};
+
+coredevice_i2c_driver(pca9450_i2c_driver);
diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
index 30d4bde5dc..c7968b75f5 100644
--- a/drivers/mfd/rave-sp.c
+++ b/drivers/mfd/rave-sp.c
@@ -185,7 +185,7 @@ struct rave_sp_variant {
* @part_number_bootloader: Bootloader version
*/
struct rave_sp {
- struct device_d dev;
+ struct device dev;
struct serdev_device *serdev;
struct rave_sp_deframer deframer;
unsigned int ackid;
@@ -310,7 +310,7 @@ int rave_sp_exec(struct rave_sp *sp,
void *__data, size_t data_size,
void *reply_data, size_t reply_data_size)
{
- struct device_d *dev = sp->serdev->dev;
+ struct device *dev = sp->serdev->dev;
struct rave_sp_reply reply = {
.data = reply_data,
.length = reply_data_size,
@@ -366,7 +366,7 @@ static void rave_sp_receive_event(struct rave_sp *sp,
static void rave_sp_receive_reply(struct rave_sp *sp,
const unsigned char *data, size_t length)
{
- struct device_d *dev = sp->serdev->dev;
+ struct device *dev = sp->serdev->dev;
struct rave_sp_reply *reply;
const size_t payload_length = length - 2;
@@ -402,7 +402,7 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
const size_t checksum_length = sp->variant->checksum->length;
const size_t payload_length = length - checksum_length;
const u8 *crc_reported = &data[payload_length];
- struct device_d *dev = sp->serdev->dev;
+ struct device *dev = sp->serdev->dev;
u8 crc_calculated[checksum_length];
print_hex_dump_debug("rave-sp rx: ", DUMP_PREFIX_NONE, 16, 1,
@@ -430,7 +430,7 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
static int rave_sp_receive_buf(struct serdev_device *serdev,
const unsigned char *buf, size_t size)
{
- struct device_d *dev = serdev->dev;
+ struct device *dev = serdev->dev;
struct rave_sp *sp = dev->priv;
struct rave_sp_deframer *deframer = &sp->deframer;
const unsigned char *src = buf;
@@ -603,7 +603,7 @@ static int rave_sp_default_cmd_translate(enum rave_sp_command command)
}
}
-static const char *devm_rave_sp_version(struct device_d *dev,
+static const char *devm_rave_sp_version(struct device *dev,
struct rave_sp_version *version)
{
/*
@@ -666,7 +666,7 @@ static int rave_sp_emulated_get_status(struct rave_sp *sp,
static int rave_sp_get_status(struct rave_sp *sp)
{
- struct device_d *dev = sp->serdev->dev;
+ struct device *dev = sp->serdev->dev;
struct rave_sp_status status;
const char *mode;
int ret;
@@ -731,6 +731,7 @@ static const struct of_device_id __maybe_unused rave_sp_dt_ids[] = {
{ .compatible = "zii,rave-sp-rdu2", .data = &rave_sp_rdu2 },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, rave_sp_dt_ids);
static int rave_sp_req_ip_addr(struct param_d *p, void *context)
{
@@ -766,7 +767,7 @@ static int rave_sp_req_ip_addr(struct param_d *p, void *context)
static int rave_sp_add_params(struct rave_sp *sp)
{
- struct device_d *dev = &sp->dev;
+ struct device *dev = &sp->dev;
struct param_d *p;
int ret;
@@ -788,14 +789,14 @@ static int rave_sp_add_params(struct rave_sp *sp)
return PTR_ERR_OR_ZERO(p);
}
-static int rave_sp_probe(struct device_d *dev)
+static int rave_sp_probe(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev->parent);
struct rave_sp *sp;
u32 baud;
int ret;
- if (of_property_read_u32(dev->device_node, "current-speed", &baud)) {
+ if (of_property_read_u32(dev->of_node, "current-speed", &baud)) {
dev_err(dev,
"'current-speed' is not specified in device node\n");
return -EINVAL;
@@ -850,10 +851,10 @@ static int rave_sp_probe(struct device_d *dev)
return ret;
}
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
}
-static struct driver_d rave_sp_drv = {
+static struct driver rave_sp_drv = {
.name = "rave-sp",
.probe = rave_sp_probe,
.of_compatible = DRV_OF_COMPAT(rave_sp_dt_ids),
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index 1b5b9d3a17..77493a7b5b 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -21,7 +21,7 @@
#include <driver.h>
#include <poweroff.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
struct rk808_reg_data {
int addr;
@@ -224,6 +224,7 @@ static void rk808_poweroff(struct poweroff_handler *handler)
reg = RK808_DEVCTRL_REG,
bit = DEV_OFF_RST;
break;
+ case RK809_ID:
case RK817_ID:
reg = RK817_SYS_CFG(3);
bit = DEV_OFF;
@@ -246,10 +247,10 @@ static void rk808_poweroff(struct poweroff_handler *handler)
hang();
}
-static int rk808_probe(struct device_d *dev)
+static int rk808_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct rk808 *rk808;
const struct rk808_reg_data *pre_init_reg;
const struct mfd_cell *cells;
@@ -380,8 +381,9 @@ static const struct of_device_id rk808_of_match[] = {
{ .compatible = "rockchip,rk818" },
{ },
};
+MODULE_DEVICE_TABLE(of, rk808_of_match);
-static struct driver_d rk808_i2c_driver = {
+static struct driver rk808_i2c_driver = {
.name = "rk808",
.of_compatible = rk808_of_match,
.probe = rk808_probe,
diff --git a/drivers/mfd/rn5t568.c b/drivers/mfd/rn5t568.c
index c1c792cbec..f1e2eeb0c8 100644
--- a/drivers/mfd/rn5t568.c
+++ b/drivers/mfd/rn5t568.c
@@ -13,32 +13,10 @@
#include <i2c/i2c.h>
#include <init.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <reset_source.h>
#include <restart.h>
-
-#define RN5T568_LSIVER 0x00
-#define RN5T568_OTPVER 0x01
-#define RN5T568_PONHIS 0x09
-# define RN5T568_PONHIS_ON_EXTINPON BIT(3)
-# define RN5T568_PONHIS_ON_REPWRPON BIT(1)
-# define RN5T568_PONHIS_ON_PWRONPON BIT(0)
-#define RN5T568_POFFHIS 0x0a
-# define RN5T568_POFFHIS_N_OEPOFF BIT(7)
-# define RN5T568_POFFHIS_DCLIMPOFF BIT(6)
-# define RN5T568_POFFHIS_WDGPOFF BIT(5)
-# define RN5T568_POFFHIS_CPUPOFF BIT(4)
-# define RN5T568_POFFHIS_IODETPOFF BIT(3)
-# define RN5T568_POFFHIS_VINDETPOFF BIT(2)
-# define RN5T568_POFFHIS_TSHUTPOFF BIT(1)
-# define RN5T568_POFFHIS_PWRONPOFF BIT(0)
-#define RN5T568_SLPCNT 0x0e
-# define RN5T568_SLPCNT_SWPPWROFF BIT(0)
-#define RN5T568_REPCNT 0x0f
-# define RN5T568_REPCNT_OFF_RESETO_16MS 0x30
-# define RN5T568_REPCNT_OFF_REPWRTIM_1000MS 0x06
-# define RN5T568_REPCNT_OFF_REPWRON BIT(0)
-#define RN5T568_MAX_REG 0xbc
+#include <mfd/rn5t568.h>
struct rn5t568 {
struct restart_handler restart;
@@ -52,7 +30,8 @@ static void rn5t568_restart(struct restart_handler *rst)
regmap_write(rn5t568->regmap, RN5T568_SLPCNT, RN5T568_SLPCNT_SWPPWROFF);
}
-static int rn5t568_reset_reason_detect(struct device_d *dev, struct regmap *regmap)
+static int rn5t568_reset_reason_detect(struct device *dev,
+ struct regmap *regmap)
{
unsigned int reg;
int ret;
@@ -111,7 +90,7 @@ static const struct regmap_config rn5t568_regmap_config = {
.max_register = RN5T568_MAX_REG,
};
-static int __init rn5t568_i2c_probe(struct device_d *dev)
+static int __init rn5t568_i2c_probe(struct device *dev)
{
struct rn5t568 *pmic_instance;
unsigned char reg[2];
@@ -138,7 +117,7 @@ static int __init rn5t568_i2c_probe(struct device_d *dev)
regmap_write(pmic_instance->regmap, RN5T568_REPCNT, RN5T568_REPCNT_OFF_RESETO_16MS |
RN5T568_REPCNT_OFF_REPWRTIM_1000MS | RN5T568_REPCNT_OFF_REPWRON);
- pmic_instance->restart.priority = of_get_restart_priority(dev->device_node);
+ pmic_instance->restart.of_node = dev->of_node;
pmic_instance->restart.name = "RN5T568";
pmic_instance->restart.restart = &rn5t568_restart;
restart_handler_register(&pmic_instance->restart);
@@ -148,15 +127,16 @@ static int __init rn5t568_i2c_probe(struct device_d *dev)
if (ret)
dev_warn(dev, "Failed to query reset reason\n");
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
}
static __maybe_unused const struct of_device_id rn5t568_of_match[] = {
{ .compatible = "ricoh,rn5t568", .data = NULL, },
{ }
};
+MODULE_DEVICE_TABLE(of, rn5t568_of_match);
-static struct driver_d rn5t568_i2c_driver = {
+static struct driver rn5t568_i2c_driver = {
.name = "rn5t568-i2c",
.probe = rn5t568_i2c_probe,
.of_compatible = DRV_OF_COMPAT(rn5t568_of_match),
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
new file mode 100644
index 0000000000..e317a29ce5
--- /dev/null
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright (C) 2018 ROHM Semiconductors
+//
+// ROHM BD71837MWV and BD71847MWV PMIC driver
+//
+// Datasheet for BD71837MWV available from
+// https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e
+
+#include <common.h>
+#include <i2c/i2c.h>
+#include <mfd/bd71837.h>
+#include <linux/mfd/core.h>
+#include <driver.h>
+#include <poweroff.h>
+#include <of.h>
+#include <linux/regmap.h>
+
+static struct mfd_cell bd71837_mfd_cells[] = {
+ { .name = "gpio-keys", },
+ { .name = "bd71837-clk", },
+ { .name = "bd71837-pmic", },
+};
+
+static struct mfd_cell bd71847_mfd_cells[] = {
+ { .name = "gpio-keys", },
+ { .name = "bd71847-clk", },
+ { .name = "bd71847-pmic", },
+};
+
+static const struct regmap_config bd718xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = BD718XX_MAX_REGISTER - 1,
+};
+
+static int bd718xx_init_press_duration(struct regmap *regmap,
+ struct device *dev)
+{
+ u32 short_press_ms, long_press_ms;
+ u32 short_press_value, long_press_value;
+ int ret;
+
+ ret = of_property_read_u32(dev->of_node, "rohm,short-press-ms",
+ &short_press_ms);
+ if (!ret) {
+ short_press_value = min(15u, (short_press_ms + 250) / 500);
+ ret = regmap_update_bits(regmap, BD718XX_PWRONCONFIG0,
+ BD718XX_PWRBTN_PRESS_DURATION_MASK,
+ short_press_value);
+ if (ret) {
+ dev_err(dev, "Failed to init pwron short press\n");
+ return ret;
+ }
+ }
+
+ ret = of_property_read_u32(dev->of_node, "rohm,long-press-ms",
+ &long_press_ms);
+ if (!ret) {
+ long_press_value = min(15u, (long_press_ms + 500) / 1000);
+ ret = regmap_update_bits(regmap, BD718XX_PWRONCONFIG1,
+ BD718XX_PWRBTN_PRESS_DURATION_MASK,
+ long_press_value);
+ if (ret) {
+ dev_err(dev, "Failed to init pwron long press\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int bd718xx_i2c_probe(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct regmap *regmap;
+ int ret;
+ unsigned int chip_type;
+ struct mfd_cell *mfd;
+ int cells;
+
+ chip_type = (unsigned int)(uintptr_t)device_get_match_data(dev);
+ switch (chip_type) {
+ case ROHM_CHIP_TYPE_BD71837:
+ mfd = bd71837_mfd_cells;
+ cells = ARRAY_SIZE(bd71837_mfd_cells);
+ break;
+ case ROHM_CHIP_TYPE_BD71847:
+ mfd = bd71847_mfd_cells;
+ cells = ARRAY_SIZE(bd71847_mfd_cells);
+ break;
+ default:
+ dev_err(dev, "Unknown device type");
+ return -EINVAL;
+ }
+
+ regmap = regmap_init_i2c(i2c, &bd718xx_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "regmap initialization failed\n");
+ return PTR_ERR(regmap);
+ }
+
+ ret = bd718xx_init_press_duration(regmap, dev);
+ if (ret)
+ return ret;
+
+ ret = mfd_add_devices(dev, mfd, cells);
+ if (ret)
+ dev_err(dev, "Failed to create subdevices\n");
+
+ return regmap_register_cdev(regmap, NULL);
+}
+
+static const struct of_device_id bd718xx_of_match[] = {
+ {
+ .compatible = "rohm,bd71837",
+ .data = (void *)ROHM_CHIP_TYPE_BD71837,
+ },
+ {
+ .compatible = "rohm,bd71847",
+ .data = (void *)ROHM_CHIP_TYPE_BD71847,
+ },
+ {
+ .compatible = "rohm,bd71850",
+ .data = (void *)ROHM_CHIP_TYPE_BD71847,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bd718xx_of_match);
+
+static struct driver bd718xx_i2c_driver = {
+ .name = "rohm-bd718x7",
+ .of_match_table = bd718xx_of_match,
+ .probe = bd718xx_i2c_probe,
+};
+coredevice_i2c_driver(bd718xx_i2c_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD71837/BD71847 Power Management IC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c
index 4814c06a62..3b47800105 100644
--- a/drivers/mfd/stm32-timers.c
+++ b/drivers/mfd/stm32-timers.c
@@ -11,6 +11,7 @@
#include <io.h>
#include <linux/bitfield.h>
#include <linux/mfd/stm32-timers.h>
+#include <linux/regmap.h>
#include <of.h>
#include <linux/reset.h>
@@ -34,7 +35,7 @@ static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
regmap_write(ddata->regmap, TIM_ARR, 0x0);
}
-static int stm32_timers_probe(struct device_d *dev)
+static int stm32_timers_probe(struct device *dev)
{
struct stm32_timers *ddata;
struct resource *res;
@@ -58,15 +59,16 @@ static int stm32_timers_probe(struct device_d *dev)
dev->priv = ddata;
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
}
static const struct of_device_id stm32_timers_of_match[] = {
{ .compatible = "st,stm32-timers", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
-static struct driver_d stm32_timers_driver = {
+static struct driver stm32_timers_driver = {
.name = "stm32-timers",
.probe = stm32_timers_probe,
.of_compatible = stm32_timers_of_match,
diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c
index 37cf6b8b36..08dc48246e 100644
--- a/drivers/mfd/stmpe-i2c.c
+++ b/drivers/mfd/stmpe-i2c.c
@@ -95,17 +95,17 @@ static struct cdev_operations stmpe_fops = {
.write = stmpe_write,
};
-static struct stmpe_platform_data *stmpe_of_probe(struct device_d *dev)
+static struct stmpe_platform_data *stmpe_of_probe(struct device *dev)
{
struct stmpe_platform_data *pdata;
struct device_node *node;
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node)
return NULL;
pdata = xzalloc(sizeof(*pdata));
- for_each_child_of_node(dev->device_node, node) {
+ for_each_child_of_node(dev->of_node, node) {
if (!strcmp(node->name, "stmpe_gpio")) {
pdata->blocks |= STMPE_BLOCK_GPIO;
}
@@ -114,7 +114,7 @@ static struct stmpe_platform_data *stmpe_of_probe(struct device_d *dev)
return pdata;
}
-static int stmpe_probe(struct device_d *dev)
+static int stmpe_probe(struct device *dev)
{
struct stmpe_platform_data *pdata = dev->platform_data;
struct stmpe *stmpe_dev;
@@ -156,7 +156,7 @@ static struct platform_device_id stmpe_i2c_id[] = {
{ }
};
-static struct driver_d stmpe_driver = {
+static struct driver stmpe_driver = {
.name = DRIVERNAME,
.probe = stmpe_probe,
.id_table = stmpe_i2c_id,
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
index 1de5afa2ef..9985673aa6 100644
--- a/drivers/mfd/stpmic1.c
+++ b/drivers/mfd/stpmic1.c
@@ -9,7 +9,7 @@
#include <i2c/i2c.h>
#include <init.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/mfd/stpmic1.h>
static const struct regmap_config stpmic1_regmap_i2c_config = {
@@ -18,7 +18,7 @@ static const struct regmap_config stpmic1_regmap_i2c_config = {
.max_register = 0xB3,
};
-static int __init stpmic1_probe(struct device_d *dev)
+static int __init stpmic1_probe(struct device *dev)
{
struct regmap *regmap;
u32 reg;
@@ -39,15 +39,16 @@ static int __init stpmic1_probe(struct device_d *dev)
}
dev_info(dev, "PMIC Chip Version: 0x%x\n", reg);
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
}
static __maybe_unused struct of_device_id stpmic1_dt_ids[] = {
{ .compatible = "st,stpmic1" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stpmic1_dt_ids);
-static struct driver_d stpmic1_i2c_driver = {
+static struct driver stpmic1_i2c_driver = {
.name = "stpmic1-i2c",
.probe = stpmic1_probe,
.of_compatible = DRV_OF_COMPAT(stpmic1_dt_ids),
diff --git a/drivers/mfd/superio.c b/drivers/mfd/superio.c
index bfb2f15dd2..16d0bba450 100644
--- a/drivers/mfd/superio.c
+++ b/drivers/mfd/superio.c
@@ -7,11 +7,11 @@
#include <common.h>
#include <superio.h>
-#include <regmap.h>
+#include <linux/regmap.h>
-struct device_d *superio_func_add(struct superio_chip *siochip, const char *name)
+struct device *superio_func_add(struct superio_chip *siochip, const char *name)
{
- struct device_d *dev;
+ struct device *dev;
int ret;
dev = device_alloc(name, DEVICE_ID_DYNAMIC);
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index d0f2f0917e..3c2e1241fd 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -16,6 +16,7 @@
#include <of_address.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
@@ -73,8 +74,6 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
syscon_config.val_bits = reg_io_width * 8;
syscon_config.max_register = resource_size(&res) - reg_io_width;
- list_add_tail(&syscon->list, &syscon_list);
-
syscon->regmap = regmap_init_mmio_clk(NULL, NULL, syscon->base,
&syscon_config);
@@ -92,6 +91,8 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
}
}
+ list_add_tail(&syscon->list, &syscon_list);
+
return syscon;
err_map:
@@ -142,21 +143,6 @@ static void __iomem *syscon_node_to_base(struct device_node *np)
return syscon->base;
}
-void __iomem *syscon_base_lookup_by_pdevname(const char *s)
-{
- struct syscon *syscon;
- struct device_d *dev;
-
- for_each_device(dev) {
- if (!strcmp(dev_name(dev), s)) {
- syscon = dev->priv;
- return syscon->base;
- }
- }
-
- return ERR_PTR(-ENODEV);
-}
-
void __iomem *syscon_base_lookup_by_phandle(struct device_node *np,
const char *property)
{
@@ -231,7 +217,7 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
return regmap;
}
-static int syscon_probe(struct device_d *dev)
+static int syscon_probe(struct device *dev)
{
struct syscon *syscon;
struct resource *res;
@@ -257,7 +243,7 @@ static struct platform_device_id syscon_ids[] = {
{ }
};
-static struct driver_d syscon_driver = {
+static struct driver syscon_driver = {
.name = "syscon",
.probe = syscon_probe,
.id_table = syscon_ids,
diff --git a/drivers/mfd/twl4030.c b/drivers/mfd/twl4030.c
index 072a49cb62..7acf9b18bd 100644
--- a/drivers/mfd/twl4030.c
+++ b/drivers/mfd/twl4030.c
@@ -26,7 +26,7 @@ struct twl4030 *twl4030_get(void)
}
EXPORT_SYMBOL(twl4030_get);
-static int twl_probe(struct device_d *dev)
+static int twl_probe(struct device *dev)
{
if (twl_dev)
return -EBUSY;
@@ -43,7 +43,7 @@ static int twl_probe(struct device_d *dev)
return 0;
}
-static struct driver_d twl_driver = {
+static struct driver twl_driver = {
.name = DRIVERNAME,
.probe = twl_probe,
};
diff --git a/drivers/mfd/twl6030.c b/drivers/mfd/twl6030.c
index 1ed0e25c3a..d7a7d9baf8 100644
--- a/drivers/mfd/twl6030.c
+++ b/drivers/mfd/twl6030.c
@@ -23,7 +23,7 @@ struct twl6030 *twl6030_get(void)
}
EXPORT_SYMBOL(twl6030_get);
-static int twl_probe(struct device_d *dev)
+static int twl_probe(struct device *dev)
{
if (twl_dev)
return -EBUSY;
@@ -61,7 +61,7 @@ static int twl_probe(struct device_d *dev)
return 0;
}
-static struct driver_d twl_driver = {
+static struct driver twl_driver = {
.name = DRIVERNAME,
.probe = twl_probe,
};
diff --git a/drivers/misc/jtag.c b/drivers/misc/jtag.c
index 433e22fd2c..e884e58bac 100644
--- a/drivers/misc/jtag.c
+++ b/drivers/misc/jtag.c
@@ -256,7 +256,7 @@ static struct cdev_operations jtag_operations = {
.ioctl = jtag_ioctl,
};
-static void jtag_info(struct device_d *pdev)
+static void jtag_info(struct device *pdev)
{
int dn, ret;
struct jtag_rd_id jid;
@@ -275,7 +275,7 @@ static void jtag_info(struct device_d *pdev)
}
}
-static int jtag_probe(struct device_d *pdev)
+static int jtag_probe(struct device *pdev)
{
int i, ret;
struct jtag_info *info;
@@ -348,7 +348,7 @@ fail_devfs_create:
return ret;
}
-static void jtag_remove(struct device_d *pdev)
+static void jtag_remove(struct device *pdev)
{
struct jtag_info *info = (struct jtag_info *) pdev->priv;
@@ -358,7 +358,7 @@ static void jtag_remove(struct device_d *pdev)
dev_notice(pdev, "Device removed\n");
}
-static struct driver_d jtag_driver = {
+static struct driver jtag_driver = {
.name = JTAG_NAME,
.probe = jtag_probe,
.remove = jtag_remove,
diff --git a/drivers/misc/mem.c b/drivers/misc/mem.c
index 1bf240c0a7..0cf021cf97 100644
--- a/drivers/misc/mem.c
+++ b/drivers/misc/mem.c
@@ -13,7 +13,7 @@ static struct cdev_operations memops = {
.memmap = generic_memmap_rw,
};
-static int mem_probe(struct device_d *dev)
+static int mem_probe(struct device *dev)
{
struct cdev *cdev;
@@ -41,14 +41,14 @@ static int mem_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mem_drv = {
+static struct driver mem_drv = {
.name = "mem",
.probe = mem_probe,
};
static int mem_init(void)
{
- struct device_d *dev;
+ struct device *dev;
struct resource res = {
.start = 0,
.end = ~0,
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index f4a7551fc7..2ebd82436e 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -22,7 +22,7 @@ static struct cdev_operations memops = {
.memmap = generic_memmap_rw,
};
-static int sram_probe(struct device_d *dev)
+static int sram_probe(struct device *dev)
{
struct resource *iores;
struct sram *sram;
@@ -53,8 +53,9 @@ static __maybe_unused struct of_device_id sram_dt_ids[] = {
}, {
},
};
+MODULE_DEVICE_TABLE(of, sram_dt_ids);
-static struct driver_d sram_driver = {
+static struct driver sram_driver = {
.name = "mmio-sram",
.probe = sram_probe,
.of_compatible = sram_dt_ids,
diff --git a/drivers/misc/starfive-pwrseq.c b/drivers/misc/starfive-pwrseq.c
index 8527d12504..794a67c40e 100644
--- a/drivers/misc/starfive-pwrseq.c
+++ b/drivers/misc/starfive-pwrseq.c
@@ -13,7 +13,7 @@ struct starfive_pwrseq {
const char **names;
};
-static int starfive_pwrseq_probe(struct device_d *dev)
+static int starfive_pwrseq_probe(struct device *dev)
{
int ret;
@@ -21,7 +21,7 @@ static int starfive_pwrseq_probe(struct device_d *dev)
if (ret)
return ret;
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
}
static struct of_device_id starfive_pwrseq_dt_ids[] = {
@@ -36,8 +36,9 @@ static struct of_device_id starfive_pwrseq_dt_ids[] = {
{ .compatible = "starfive,spi2ahb" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, starfive_pwrseq_dt_ids);
-static struct driver_d starfive_pwrseq_driver = {
+static struct driver starfive_pwrseq_driver = {
.name = "starfive_pwrseq",
.probe = starfive_pwrseq_probe,
.of_compatible = starfive_pwrseq_dt_ids,
diff --git a/drivers/misc/state.c b/drivers/misc/state.c
index cb0d9cf3ad..05b02aa91d 100644
--- a/drivers/misc/state.c
+++ b/drivers/misc/state.c
@@ -12,9 +12,9 @@
#include <linux/err.h>
-static int state_probe(struct device_d *dev)
+static int state_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct state *state;
bool readonly = false;
int ret;
@@ -44,8 +44,9 @@ static __maybe_unused struct of_device_id state_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, state_ids);
-static struct driver_d state_driver = {
+static struct driver state_driver = {
.name = "state",
.probe = state_probe,
.of_compatible = DRV_OF_COMPAT(state_ids),
diff --git a/drivers/misc/storage-by-uuid.c b/drivers/misc/storage-by-uuid.c
index 12a06076a2..5548f477a4 100644
--- a/drivers/misc/storage-by-uuid.c
+++ b/drivers/misc/storage-by-uuid.c
@@ -4,7 +4,6 @@
#include <io.h>
#include <of.h>
#include <malloc.h>
-#include <partition.h>
#include <envfs.h>
#include <fs.h>
@@ -12,7 +11,7 @@ static LIST_HEAD(sbu_list);
struct sbu {
char *uuid;
- struct device_d *dev;
+ struct device *dev;
struct cdev *rcdev;
struct cdev cdev;
struct list_head list;
@@ -133,7 +132,7 @@ static void storage_by_uuid_add_partitions(struct sbu *sbu, struct cdev *rcdev)
return;
}
- of_parse_partitions(&sbu->cdev, sbu->dev->device_node);
+ of_parse_partitions(&sbu->cdev, sbu->dev->of_node);
}
static void check_exist(struct sbu *sbu)
@@ -141,14 +140,14 @@ static void check_exist(struct sbu *sbu)
struct cdev *cdev;
for_each_cdev(cdev) {
- if (!strcmp(cdev->uuid, sbu->uuid)) {
- dev_dbg(sbu->dev, "Found %s %s\n", cdev->name, cdev->uuid);
+ if (!strcmp(cdev->diskuuid, sbu->uuid)) {
+ dev_dbg(sbu->dev, "Found %s %s\n", cdev->name, cdev->diskuuid);
storage_by_uuid_add_partitions(sbu, cdev);
}
}
}
-static int sbu_detect(struct device_d *dev)
+static int sbu_detect(struct device *dev)
{
struct sbu *sbu = dev->priv;
@@ -159,7 +158,7 @@ static int sbu_detect(struct device_d *dev)
return 0;
}
-static int storage_by_uuid_probe(struct device_d *dev)
+static int storage_by_uuid_probe(struct device *dev)
{
struct sbu *sbu;
int ret;
@@ -167,7 +166,7 @@ static int storage_by_uuid_probe(struct device_d *dev)
sbu = xzalloc(sizeof(*sbu));
- ret = of_property_read_string(dev->device_node, "uuid", &uuid);
+ ret = of_property_read_string(dev->of_node, "uuid", &uuid);
if (ret)
return ret;
@@ -190,8 +189,9 @@ static struct of_device_id storage_by_uuid_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, storage_by_uuid_dt_ids);
-static struct driver_d storage_by_uuid_driver = {
+static struct driver storage_by_uuid_driver = {
.name = "storage-by-uuid",
.probe = storage_by_uuid_probe,
.of_compatible = storage_by_uuid_dt_ids,
diff --git a/drivers/misc/ubootvar.c b/drivers/misc/ubootvar.c
index 723e9e2b54..127fdac779 100644
--- a/drivers/misc/ubootvar.c
+++ b/drivers/misc/ubootvar.c
@@ -11,7 +11,6 @@
#include <io.h>
#include <of.h>
#include <malloc.h>
-#include <partition.h>
#include <envfs.h>
#include <fs.h>
#include <libfile.h>
@@ -36,7 +35,7 @@ struct ubootvar_data {
static int ubootvar_flush(struct cdev *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct ubootvar_data *ubdata = dev->priv;
const char *path = ubdata->path[!ubdata->current];
uint32_t crc = 0xffffffff;
@@ -126,7 +125,7 @@ static ssize_t
ubootvar_read(struct cdev *cdev, void *buf, size_t count, loff_t offset,
unsigned long flags)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct ubootvar_data *ubdata = dev->priv;
WARN_ON(flags & O_RWSIZE_MASK);
@@ -140,7 +139,7 @@ static ssize_t
ubootvar_write(struct cdev *cdev, const void *buf, size_t count,
loff_t offset, unsigned long flags)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct ubootvar_data *ubdata = dev->priv;
WARN_ON(flags & O_RWSIZE_MASK);
@@ -152,7 +151,7 @@ ubootvar_write(struct cdev *cdev, const void *buf, size_t count,
static int ubootvar_memmap(struct cdev *cdev, void **map, int flags)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct ubootvar_data *ubdata = dev->priv;
*map = ubdata->data;
@@ -167,7 +166,7 @@ static struct cdev_operations ubootvar_ops = {
.flush = ubootvar_flush,
};
-static void ubootenv_info(struct device_d *dev)
+static void ubootenv_info(struct device *dev)
{
struct ubootvar_data *ubdata = dev->priv;
@@ -175,7 +174,7 @@ static void ubootenv_info(struct device_d *dev)
ubdata->path[ubdata->current]);
}
-static int ubootenv_probe(struct device_d *dev)
+static int ubootenv_probe(struct device *dev)
{
struct ubootvar_data *ubdata;
unsigned int crc_ok = 0;
@@ -196,11 +195,11 @@ static int ubootenv_probe(struct device_d *dev)
ubdata = xzalloc(sizeof(*ubdata));
- ret = of_find_path(dev->device_node, "device-path-0",
+ ret = of_find_path(dev->of_node, "device-path-0",
&ubdata->path[0],
OF_FIND_PATH_FLAGS_BB);
if (ret)
- ret = of_find_path(dev->device_node, "device-path",
+ ret = of_find_path(dev->of_node, "device-path",
&ubdata->path[0],
OF_FIND_PATH_FLAGS_BB);
@@ -211,7 +210,7 @@ static int ubootenv_probe(struct device_d *dev)
count++;
- if (!of_find_path(dev->device_node, "device-path-1",
+ if (!of_find_path(dev->of_node, "device-path-1",
&ubdata->path[1],
OF_FIND_PATH_FLAGS_BB)) {
count++;
@@ -351,8 +350,9 @@ static struct of_device_id ubootenv_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ubootenv_dt_ids);
-static struct driver_d ubootenv_driver = {
+static struct driver ubootenv_driver = {
.name = "uboot-environment",
.probe = ubootenv_probe,
.of_compatible = ubootenv_dt_ids,
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index dc05793ca1..5a0753edd0 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_NAND) += nand/
+obj-y += nand/
obj-$(CONFIG_DRIVER_CFI) += nor/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 14a4f336c8..ec2c3ff7bb 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -590,7 +590,7 @@ static int mtd_part_compare(struct list_head *a, struct list_head *b)
return 0;
}
-static int mtd_detect(struct device_d *dev)
+static int mtd_detect(struct device *dev)
{
struct mtd_info *mtd = container_of(dev, struct mtd_info, dev);
int bufsize = 512;
@@ -599,7 +599,10 @@ static int mtd_detect(struct device_d *dev)
enum filetype filetype;
int npebs = mtd_div_by_eb(mtd->size, mtd);
- npebs = max(npebs, 64);
+ /* No point in looking for UBI on a partition that's too small */
+ npebs = min(npebs, 64);
+ if (npebs < 5)
+ return 0;
/*
* Do not try to attach an UBI device if this device has partitions
@@ -644,8 +647,8 @@ static int mtd_partition_fixup_generic(struct mtd_info *mtd, struct device_node
np = of_find_node_by_reproducible_name(root, name);
free(name);
if (!np) {
- dev_err(&mtd->dev, "Cannot find nodepath %s, cannot fixup\n",
- mtdnp->full_name);
+ dev_err(&mtd->dev, "Cannot find nodepath %pOF, cannot fixup\n",
+ mtdnp);
return -EINVAL;
}
@@ -688,6 +691,7 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
mtd->dev.id);
INIT_LIST_HEAD(&mtd->partitions);
+ INIT_LIST_HEAD(&mtd->partitions_entry);
mtd->cdev.priv = mtd;
mtd->cdev.dev = &mtd->dev;
@@ -763,8 +767,7 @@ int del_mtd_device(struct mtd_info *mtd)
unregister_device(&mtd->dev);
free(mtd->param_size.value);
free(mtd->cdev.name);
- if (mtd->parent)
- list_del(&mtd->partitions_entry);
+ list_del(&mtd->partitions_entry);
return 0;
}
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 4b243a7fb1..fcf9403b8f 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -325,7 +325,7 @@ static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
ecc[i] = bitrev8(hwecc[i]);
- numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
+ numerrs = bch_decode(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
NULL, ecc, NULL, errorpos);
BUG_ON(numerrs == -EINVAL);
if (numerrs < 0)
@@ -1071,7 +1071,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
}
static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
- struct device_d *dev)
+ struct device *dev)
{
int ret, bbt_nbpages;
u16 chip_id, chip_id_inv;
@@ -1131,7 +1131,7 @@ nomem1:
return ERR_PTR(ret);
}
-static int __init docg3_probe(struct device_d *dev)
+static int __init docg3_probe(struct device *dev)
{
struct resource *iores;
struct mtd_info *mtd;
@@ -1144,8 +1144,8 @@ static int __init docg3_probe(struct device_d *dev)
base = IOMEM(iores->start);
ret = -ENOMEM;
- docg3_bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
- DOC_ECC_BCH_PRIMPOLY);
+ docg3_bch = bch_init(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
+ DOC_ECC_BCH_PRIMPOLY, false);
if (!docg3_bch)
goto nomem2;
@@ -1181,12 +1181,12 @@ notfound:
ret = -ENODEV;
dev_info(dev, "No supported DiskOnChip found\n");
err_probe:
- free_bch(docg3_bch);
+ bch_free(docg3_bch);
nomem2:
return ret;
}
-static struct driver_d g3_driver = {
+static struct driver g3_driver = {
.name = "docg3",
.probe = docg3_probe,
};
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index afdcd7c8be..f917dc4a1e 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -254,7 +254,7 @@
#define DOC_LAYOUT_DPS_KEY_LENGTH 8
struct docg3 {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
unsigned int device_id:4;
unsigned int if_cfg:1;
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 5592f9597e..f4db8c402a 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -15,7 +15,6 @@
#include <driver.h>
#include <of.h>
#include <spi/spi.h>
-#include <spi/flash.h>
#include <xfuncs.h>
#include <malloc.h>
#include <errno.h>
@@ -201,11 +200,10 @@ static const struct platform_device_id m25p_ids[] = {
* matches what the READ command supports, at least until this driver
* understands FAST_READ (for clocks over 25 MHz).
*/
-static int m25p_probe(struct device_d *dev)
+static int m25p_probe(struct device *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
struct spi_mem *spimem = spi->mem;
- struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
struct spi_nor_hwcaps hwcaps = {
@@ -218,8 +216,6 @@ static int m25p_probe(struct device_d *dev)
bool use_large_blocks;
int ret;
- data = dev->platform_data;
-
flash = xzalloc(sizeof *flash);
nor = &flash->spi_nor;
@@ -245,30 +241,21 @@ static int m25p_probe(struct device_d *dev)
dev->priv = (void *)flash;
- if (data && data->name)
- flash->mtd.name = data->name;
-
- if (data && data->type)
- flash_name = data->type;
- else if (data && data->name)
- flash_name = data->name;
- else if (dev->id_entry)
+ if (dev->id_entry)
flash_name = dev->id_entry->name;
else
flash_name = NULL; /* auto-detect */
- use_large_blocks = of_property_read_bool(dev->device_node,
- "use-large-blocks");
+ use_large_blocks = of_property_read_bool(dev->of_node,
+ "use-large-blocks");
ret = spi_nor_scan(nor, flash_name, &hwcaps, use_large_blocks);
if (ret)
return ret;
device_id = DEVICE_ID_SINGLE;
- if (dev->device_node)
- flash_name = of_alias_get(dev->device_node);
- else if (data && data->name)
- flash_name = data->name;
+ if (dev->of_node)
+ flash_name = of_alias_get(dev->of_node);
if (!flash_name) {
device_id = DEVICE_ID_DYNAMIC;
@@ -287,8 +274,9 @@ static __maybe_unused struct of_device_id m25p80_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, m25p80_dt_ids);
-static struct driver_d m25p80_driver = {
+static struct driver m25p80_driver = {
.name = "m25p80",
.probe = m25p_probe,
.of_compatible = DRV_OF_COMPAT(m25p80_dt_ids),
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index c1af1feb36..80d16dec10 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -17,7 +17,6 @@
#include <clock.h>
#include <spi/spi.h>
-#include <spi/flash.h>
#include <linux/math64.h>
#include <linux/mtd/mtd.h>
@@ -599,7 +598,6 @@ add_dataflash_otp(struct spi_device *spi, char *name,
{
struct dataflash *priv;
struct mtd_info *device;
- struct flash_platform_data *pdata = spi->dev.platform_data;
char *otp_tag = "";
int err = 0;
@@ -617,7 +615,7 @@ add_dataflash_otp(struct spi_device *spi, char *name,
name);
device = &priv->mtd;
- device->name = (pdata && pdata->name) ? pdata->name : "dataflash";
+ device->name = "dataflash";
device->size = nr_pages * (uint64_t)pagesize;
device->erasesize = pagesize;
device->writesize = pagesize;
@@ -816,7 +814,7 @@ static struct flash_info * jedec_probe(struct spi_device *spi)
* AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11
* AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11
*/
-static int dataflash_probe(struct device_d *dev)
+static int dataflash_probe(struct device *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
int status;
@@ -897,8 +895,9 @@ static __maybe_unused struct of_device_id dataflash_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
-static struct driver_d dataflash_driver = {
+static struct driver dataflash_driver = {
.name = "mtd_dataflash",
.probe = dataflash_probe,
.of_compatible = DRV_OF_COMPAT(dataflash_dt_ids),
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index abef07d9c0..33c221e3a1 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -36,7 +36,7 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retle
return 0;
}
-static int mtdram_probe(struct device_d *dev)
+static int mtdram_probe(struct device *dev)
{
long type;
struct resource *iores;
@@ -48,8 +48,8 @@ static int mtdram_probe(struct device_d *dev)
mtd = xzalloc(sizeof(struct mtd_info));
device_id = DEVICE_ID_SINGLE;
- if (dev->device_node) {
- const char *alias = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ const char *alias = of_alias_get(dev->of_node);
if (alias)
mtd->name = xstrdup(alias);
}
@@ -106,8 +106,9 @@ static __maybe_unused struct of_device_id mtdram_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mtdram_dt_ids);
-static struct driver_d mtdram_driver = {
+static struct driver mtdram_driver = {
.name = "mtdram",
.probe = mtdram_probe,
.of_compatible = DRV_OF_COMPAT(mtdram_dt_ids),
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 964b00166a..d4f0227384 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -382,7 +382,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
return 0;
}
-static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
@@ -416,7 +416,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
return err;
}
-static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_concat *concat = CONCAT(mtd);
int i, err = 0;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2cd52f3820..4c19718467 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,155 +1,40 @@
# SPDX-License-Identifier: GPL-2.0-only
-menuconfig NAND
- bool "NAND support"
- help
- This enables support for accessing all type of NAND flash
- devices. For further information see
- <http://www.linux-mtd.infradead.org/doc/nand.html>.
-
-if NAND
-
-config MTD_NAND_ECC_SOFT
- bool
- prompt "Support software ecc"
-config MTD_NAND_ECC_SW_BCH
- select BCH
- depends on MTD_NAND_ECC_SOFT
- bool
- prompt "Support software BCH ecc"
-
-config NAND_ECC_HW_SYNDROME
- bool
- prompt "Support syndrome hardware ecc controllers"
-
-config NAND_ALLOW_ERASE_BAD
- bool
- depends on MTD_WRITE
- prompt "Add device parameter to allow erasing bad blocks"
- help
- This adds a 'erasebad' device parameter to nand devices. When set
- to '1' it will be allowed to erase bad blocks. This is a potientially
- dangerous operation, so if unsure say no to this option.
+menu "NAND"
-config NAND_IMX
+config MTD_NAND_CORE
bool
- prompt "i.MX21 to 53 NAND driver aka 'mxc', for NFC"
- depends on ARCH_IMX
- help
- Support for NAND flash on Freescale/NXP i.MX devices. This is for the
- "MXC" series: i.MX21/25/27/31/35/51/53.
- This is not for the "MXS" series i.MX processors (23 & 28), or i.MX6
- and later, which use the GPMI NAND controller from the MXS series.
- See the i.MX 'mxs' driver for those chips.
+source "drivers/mtd/nand/raw/Kconfig"
-config NAND_FSL_IFC
- bool
- prompt "FSL IFC NAND driver"
- depends on ARCH_LAYERSCAPE
- help
- Freescale IFC NAND driver for various chips.
+menu "ECC engine support"
-config NAND_MXS
+config MTD_NAND_ECC
bool
- select STMP_DEVICE
- prompt "i.MX23/28 & 6+ NAND driver aka 'mxs', for GPMI"
- depends on MXS_APBH_DMA
- help
- Support for NAND flash on Freescale/NXP i.MX devices. This is for the
- "MXS" series: i.MX23/28 and all i.MX6 and later SoCs.
-
- This is not for the "MXC" series of i.MX processors in the i.MX21 to
- i.MX53 range. See the i.MX "mxc" driver for those chips.
+ select MTD_NAND_CORE
-config NAND_OMAP_GPMC
- tristate "NAND Flash Support for GPMC based OMAP platforms"
- depends on OMAP_GPMC
- help
- Support for NAND flash using GPMC. GPMC is a common memory
- interface found on Texas Instrument's OMAP platforms
-
-config MTD_NAND_OMAP_ELM
- bool "Support for ELM (Error Location Module) on OMAP platforms"
- depends on NAND_OMAP_GPMC || COMPILE_TEST
- help
- This config enables the ELM hardware engine, which can be used to
- locate and correct errors when using BCH ECC scheme. This offloads
- the cpu from doing ECC error searching and correction. However some
- legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
- so this is optional for them.
-
-config NAND_ORION
- bool
- prompt "Marvell Orion NAND driver"
- depends on (ARM && !CPU_32v4T) && (ARCH_KIRKWOOD || COMPILE_TEST)
+config MTD_NAND_ECC_SW_HAMMING
+ bool "Software Hamming ECC engine"
+ default y if MTD_RAW_NAND
+ select MTD_NAND_ECC
help
- Support for the Orion NAND controller, present in Kirkwood SoCs.
+ This enables support for software Hamming error
+ correction. This correction can correct up to 1 bit error
+ per chunk and detect up to 2 bit errors. While it used to be
+ widely used with old parts, newer NAND chips usually require
+ more strength correction and in this case BCH or RS will be
+ preferred.
-config NAND_MRVL_NFC
- bool
- prompt "Marvell PXA3xx NAND driver"
- depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_PXA3XX || COMPILE_TEST
- help
- Support for the PXA3xx NAND controller, present in Armada 370/XP and
- PXA3xx SoCs.
-
-config NAND_ATMEL
- bool
- prompt "Atmel (AT91SAM9xxx) NAND driver"
- depends on ARCH_AT91
-
-config NAND_ATMEL_PMECC
- bool
- prompt "PMECC support"
- depends on NAND_ATMEL || COMPILE_TEST
- help
- Support for PMECC present on the SoC sam9x5 and sam9n12
-
-config NAND_S3C24XX
- bool
- prompt "Samsung S3C24XX NAND driver"
- depends on ARCH_S3C24xx
- help
- Add support for processor's NAND device controller.
-
-config MTD_NAND_ECC_SW_HAMMING_SMC
- bool "NAND ECC Smart Media byte order"
+config MTD_NAND_ECC_SW_BCH
+ bool "Software BCH ECC engine"
+ select BCH
+ select MTD_NAND_ECC
default n
help
- Software ECC according to the Smart Media Specification.
- The original Linux implementation had byte 0 and 1 swapped.
-
-config MTD_NAND_NOMADIK
- tristate "ST Nomadik 8815 NAND support"
- depends on ARCH_NOMADIK
- help
- Driver for the NAND flash controller on the Nomadik, with ECC.
-
-config MTD_NAND_DENALI
- tristate "Support Denali NAND controller"
- depends on HAS_DMA
- help
- Enable support for the Denali NAND controller. This should be
- combined with either the PCI or platform drivers to provide device
- registration.
-
-config MTD_NAND_DENALI_DT
- tristate "Support Denali NAND controller as a DT device"
- depends on HAVE_CLK && MTD_NAND_DENALI
- help
- Enable the driver for NAND flash on platforms using a Denali NAND
- controller as a DT device.
-
-if MTD_NAND_DENALI
-
-config MTD_NAND_DENALI_TIMING_MODE
- int "Overrides default ONFI timing mode."
- default -1
- range -1 5
- help
- -1 indicates use default timings
-
-endif
+ This enables support for software BCH error correction. Binary BCH
+ codes are more powerful and cpu intensive than traditional Hamming
+ ECC codes. They are used with NAND devices requiring more than 1 bit
+ of error correction.
-endif
+endmenu
+endmenu
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1584e86694..617a9c2638 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,26 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
-# Generic NAND options
-obj-$(CONFIG_NAND) += nand_ecc.o
-obj-$(CONFIG_MTD_NAND_ECC_SW_BCH) += nand_bch.o
-obj-$(CONFIG_NAND) += nand_ids.o
-obj-$(CONFIG_NAND) += nand_base.o nand-bb.o nand_timings.o
-obj-$(CONFIG_NAND) += nand_legacy.o nand_onfi.o nand_amd.o
-obj-$(CONFIG_NAND) += nand_esmt.o nand_hynix.o nand_macronix.o
-obj-$(CONFIG_NAND) += nand_micron.o nand_samsung.o nand_toshiba.o
-obj-$(CONFIG_NAND) += nand_jedec.o core.o bbt.o
-obj-$(CONFIG_NAND) += nand_bbt.o
+obj-$(CONFIG_MTD_NAND_CORE) += core.o bbt.o nand-bb.o
-obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
-obj-$(CONFIG_NAND_IMX) += nand_imx.o
-obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o
-obj-$(CONFIG_MTD_NAND_OMAP_ELM) += omap_elm.o
-obj-$(CONFIG_NAND_ORION) += nand_orion.o
-obj-$(CONFIG_NAND_MRVL_NFC) += nand_mrvl_nfc.o
-obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
-obj-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o
-pbl-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o
-obj-$(CONFIG_NAND_MXS) += nand_mxs.o
-obj-$(CONFIG_MTD_NAND_DENALI) += nand_denali.o
-obj-$(CONFIG_MTD_NAND_DENALI_DT) += nand_denali_dt.o
-obj-$(CONFIG_NAND_FSL_IFC) += nand_fsl_ifc.o
+obj-y += raw/
+
+obj-$(CONFIG_MTD_NAND_ECC) += ecc.o
+obj-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += ecc-sw-hamming.o
+obj-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
index 52036dd857..2d165f9474 100644
--- a/drivers/mtd/nand/core.c
+++ b/drivers/mtd/nand/core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 Free Electrons
*
@@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(nanddev_isreserved);
*
* Return: 0 in case of success, a negative error code otherwise.
*/
-int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
+static int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
{
if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
pr_warn("attempt to erase a bad/reserved block @%llx\n",
@@ -133,7 +133,6 @@ int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
return nand->ops->erase(nand, pos);
}
-EXPORT_SYMBOL_GPL(nanddev_erase);
/**
* nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
@@ -208,6 +207,134 @@ int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)
EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);
/**
+ * nanddev_get_ecc_engine() - Find and get a suitable ECC engine
+ * @nand: NAND device
+ */
+static int nanddev_get_ecc_engine(struct nand_device *nand)
+{
+ int engine_type;
+
+ /* Read the user desires in terms of ECC engine/configuration */
+ of_get_nand_ecc_user_config(nand);
+
+ engine_type = nand->ecc.user_conf.engine_type;
+ if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID)
+ engine_type = nand->ecc.defaults.engine_type;
+
+ switch (engine_type) {
+ case NAND_ECC_ENGINE_TYPE_NONE:
+ return 0;
+ case NAND_ECC_ENGINE_TYPE_SOFT:
+ nand->ecc.engine = nand_ecc_get_sw_engine(nand);
+ break;
+ case NAND_ECC_ENGINE_TYPE_ON_DIE:
+ nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
+ break;
+ case NAND_ECC_ENGINE_TYPE_ON_HOST:
+ nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand);
+ if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ break;
+ default:
+ pr_err("Missing ECC engine type\n");
+ }
+
+ if (!nand->ecc.engine)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine
+ * @nand: NAND device
+ */
+static int nanddev_put_ecc_engine(struct nand_device *nand)
+{
+ switch (nand->ecc.ctx.conf.engine_type) {
+ case NAND_ECC_ENGINE_TYPE_ON_HOST:
+ nand_ecc_put_on_host_hw_engine(nand);
+ break;
+ case NAND_ECC_ENGINE_TYPE_NONE:
+ case NAND_ECC_ENGINE_TYPE_SOFT:
+ case NAND_ECC_ENGINE_TYPE_ON_DIE:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * nanddev_find_ecc_configuration() - Find a suitable ECC configuration
+ * @nand: NAND device
+ */
+static int nanddev_find_ecc_configuration(struct nand_device *nand)
+{
+ int ret;
+
+ if (!nand->ecc.engine)
+ return -ENOTSUPP;
+
+ ret = nand_ecc_init_ctx(nand);
+ if (ret)
+ return ret;
+
+ if (!nand_ecc_is_strong_enough(nand))
+ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+ nand->mtd.name);
+
+ return 0;
+}
+
+/**
+ * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip
+ * @nand: NAND device
+ */
+int nanddev_ecc_engine_init(struct nand_device *nand)
+{
+ int ret;
+
+ /* Look for the ECC engine to use */
+ ret = nanddev_get_ecc_engine(nand);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ pr_err("No ECC engine found\n");
+
+ return ret;
+ }
+
+ /* No ECC engine requested */
+ if (!nand->ecc.engine)
+ return 0;
+
+ /* Configure the engine: balance user input and chip requirements */
+ ret = nanddev_find_ecc_configuration(nand);
+ if (ret) {
+ pr_err("No suitable ECC configuration\n");
+ nanddev_put_ecc_engine(nand);
+
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init);
+
+/**
+ * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations
+ * @nand: NAND device
+ */
+void nanddev_ecc_engine_cleanup(struct nand_device *nand)
+{
+ if (nand->ecc.engine)
+ nand_ecc_cleanup_ctx(nand);
+
+ nanddev_put_ecc_engine(nand);
+}
+EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup);
+
+/**
* nanddev_init() - Initialize a NAND device
* @nand: NAND device
* @ops: NAND device operations
diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c
new file mode 100644
index 0000000000..29cf562aa9
--- /dev/null
+++ b/drivers/mtd/nand/ecc-sw-bch.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This file provides ECC correction for more than 1 bit per block of data,
+ * using binary BCH codes. It relies on the generic BCH library lib/bch.c.
+ *
+ * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/bitops.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand-ecc-sw-bch.h>
+
+/**
+ * nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block
+ * @nand: NAND device
+ * @buf: Input buffer with raw data
+ * @code: Output buffer with ECC
+ */
+int nand_ecc_sw_bch_calculate(struct nand_device *nand,
+ const unsigned char *buf, unsigned char *code)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int i;
+
+ memset(code, 0, engine_conf->code_size);
+ bch_encode(engine_conf->bch, buf, nand->ecc.ctx.conf.step_size, code);
+
+ /* apply mask so that an erased page is a valid codeword */
+ for (i = 0; i < engine_conf->code_size; i++)
+ code[i] ^= engine_conf->eccmask[i];
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_calculate);
+
+/**
+ * nand_ecc_sw_bch_correct - Detect, correct and report bit error(s)
+ * @nand: NAND device
+ * @buf: Raw data read from the chip
+ * @read_ecc: ECC bytes from the chip
+ * @calc_ecc: ECC calculated from the raw data
+ *
+ * Detect and correct bit errors for a data block.
+ */
+int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int step_size = nand->ecc.ctx.conf.step_size;
+ unsigned int *errloc = engine_conf->errloc;
+ int i, count;
+
+ count = bch_decode(engine_conf->bch, NULL, step_size, read_ecc,
+ calc_ecc, NULL, errloc);
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ if (errloc[i] < (step_size * 8))
+ /* The error is in the data area: correct it */
+ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
+
+ /* Otherwise the error is in the ECC area: nothing to do */
+ pr_debug("%s: corrected bitflip %u\n", __func__,
+ errloc[i]);
+ }
+ } else if (count < 0) {
+ pr_err("ECC unrecoverable error\n");
+ count = -EBADMSG;
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_correct);
+
+/**
+ * nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources
+ * @nand: NAND device
+ */
+static void nand_ecc_sw_bch_cleanup(struct nand_device *nand)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+
+ bch_free(engine_conf->bch);
+ kfree(engine_conf->errloc);
+ kfree(engine_conf->eccmask);
+}
+
+/**
+ * nand_ecc_sw_bch_init - Initialize software BCH ECC engine
+ * @nand: NAND device
+ *
+ * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure
+ *
+ * Initialize NAND BCH error correction. @nand.ecc parameters 'step_size' and
+ * 'bytes' are used to compute the following BCH parameters:
+ * m, the Galois field order
+ * t, the error correction capability
+ * 'bytes' should be equal to the number of bytes required to store m * t
+ * bits, where m is such that 2^m - 1 > step_size * 8.
+ *
+ * Example: to configure 4 bit correction per 512 bytes, you should pass
+ * step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8)
+ * bytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits)
+ */
+static int nand_ecc_sw_bch_init(struct nand_device *nand)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int eccsize = nand->ecc.ctx.conf.step_size;
+ unsigned int eccbytes = engine_conf->code_size;
+ unsigned int m, t, i;
+ unsigned char *erased_page;
+ int ret;
+
+ m = fls(1 + (8 * eccsize));
+ t = (eccbytes * 8) / m;
+
+ engine_conf->bch = bch_init(m, t, 0, false);
+ if (!engine_conf->bch)
+ return -EINVAL;
+
+ engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL);
+ engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc),
+ GFP_KERNEL);
+ if (!engine_conf->eccmask || !engine_conf->errloc) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ /* Compute and store the inverted ECC of an erased step */
+ erased_page = kmalloc(eccsize, GFP_KERNEL);
+ if (!erased_page) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ memset(erased_page, 0xff, eccsize);
+ bch_encode(engine_conf->bch, erased_page, eccsize,
+ engine_conf->eccmask);
+ kfree(erased_page);
+
+ for (i = 0; i < eccbytes; i++)
+ engine_conf->eccmask[i] ^= 0xff;
+
+ /* Verify that the number of code bytes has the expected value */
+ if (engine_conf->bch->ecc_bytes != eccbytes) {
+ pr_err("Invalid number of ECC bytes: %u, expected: %u\n",
+ eccbytes, engine_conf->bch->ecc_bytes);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ /* Sanity checks */
+ if (8 * (eccsize + eccbytes) >= (1 << m)) {
+ pr_err("ECC step size is too large (%u)\n", eccsize);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ nand_ecc_sw_bch_cleanup(nand);
+
+ return ret;
+}
+
+int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct nand_ecc_sw_bch_conf *engine_conf;
+ unsigned int code_size = 0, nsteps;
+ int ret;
+
+ /* Only large page NAND chips may use BCH */
+ if (mtd->oobsize < 64) {
+ pr_err("BCH cannot be used with small page NAND chips\n");
+ return -EINVAL;
+ }
+
+ if (!mtd->ooblayout)
+ mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
+
+ conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ conf->algo = NAND_ECC_ALGO_BCH;
+ conf->step_size = nand->ecc.user_conf.step_size;
+ conf->strength = nand->ecc.user_conf.strength;
+
+ /*
+ * Board driver should supply ECC size and ECC strength
+ * values to select how many bits are correctable.
+ * Otherwise, default to 512 bytes for large page devices and 256 for
+ * small page devices.
+ */
+ if (!conf->step_size) {
+ if (mtd->oobsize >= 64)
+ conf->step_size = 512;
+ else
+ conf->step_size = 256;
+
+ conf->strength = 4;
+ }
+
+ nsteps = mtd->writesize / conf->step_size;
+
+ /* Maximize */
+ if (nand->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
+ conf->step_size = 1024;
+ nsteps = mtd->writesize / conf->step_size;
+ /* Reserve 2 bytes for the BBM */
+ code_size = (mtd->oobsize - 2) / nsteps;
+ conf->strength = code_size * 8 / fls(8 * conf->step_size);
+ }
+
+ if (!code_size)
+ code_size = DIV_ROUND_UP(conf->strength *
+ fls(8 * conf->step_size), 8);
+
+ if (!conf->strength)
+ conf->strength = (code_size * 8) / fls(8 * conf->step_size);
+
+ if (!code_size && !conf->strength) {
+ pr_err("Missing ECC parameters\n");
+ return -EINVAL;
+ }
+
+ engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
+ if (!engine_conf)
+ return -ENOMEM;
+
+ ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand);
+ if (ret)
+ goto free_engine_conf;
+
+ engine_conf->code_size = code_size;
+ engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+ engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+ if (!engine_conf->calc_buf || !engine_conf->code_buf) {
+ ret = -ENOMEM;
+ goto free_bufs;
+ }
+
+ nand->ecc.ctx.priv = engine_conf;
+ nand->ecc.ctx.nsteps = nsteps;
+ nand->ecc.ctx.total = nsteps * code_size;
+
+ ret = nand_ecc_sw_bch_init(nand);
+ if (ret)
+ goto free_bufs;
+
+ /* Verify the layout validity */
+ if (mtd_ooblayout_count_eccbytes(mtd) !=
+ nand->ecc.ctx.nsteps * engine_conf->code_size) {
+ pr_err("Invalid ECC layout\n");
+ ret = -EINVAL;
+ goto cleanup_bch_ctx;
+ }
+
+ return 0;
+
+cleanup_bch_ctx:
+ nand_ecc_sw_bch_cleanup(nand);
+free_bufs:
+ nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+free_engine_conf:
+ kfree(engine_conf);
+
+ return ret;
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_init_ctx);
+
+void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+
+ if (engine_conf) {
+ nand_ecc_sw_bch_cleanup(nand);
+ nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+ kfree(engine_conf);
+ }
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup_ctx);
+
+static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = nand->ecc.ctx.nsteps;
+ int total = nand->ecc.ctx.total;
+ u8 *ecccalc = engine_conf->calc_buf;
+ const u8 *data;
+ int i;
+
+ /* Nothing to do for a raw operation */
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ nand_ecc_tweak_req(&engine_conf->req_ctx, req);
+
+ /* No more preparation for page read */
+ if (req->type == NAND_PAGE_READ)
+ return 0;
+
+ /* Preparation for page write: derive the ECC bytes and place them */
+ for (i = 0, data = req->databuf.out;
+ eccsteps;
+ eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
+
+ return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out,
+ 0, total);
+}
+
+static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int total = nand->ecc.ctx.total;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = nand->ecc.ctx.nsteps;
+ u8 *ecccalc = engine_conf->calc_buf;
+ u8 *ecccode = engine_conf->code_buf;
+ unsigned int max_bitflips = 0;
+ u8 *data = req->databuf.in;
+ int i, ret;
+
+ /* Nothing to do for a raw operation */
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ /* No more preparation for page write */
+ if (req->type == NAND_PAGE_WRITE) {
+ nand_ecc_restore_req(&engine_conf->req_ctx, req);
+ return 0;
+ }
+
+ /* Finish a page read: retrieve the (raw) ECC bytes*/
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0,
+ total);
+ if (ret)
+ return ret;
+
+ /* Calculate the ECC bytes */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
+
+ /* Finish a page read: compare and correct */
+ for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in;
+ eccsteps;
+ eccsteps--, i += eccbytes, data += eccsize) {
+ int stat = nand_ecc_sw_bch_correct(nand, data,
+ &ecccode[i],
+ &ecccalc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ nand_ecc_restore_req(&engine_conf->req_ctx, req);
+
+ return max_bitflips;
+}
+
+static struct nand_ecc_engine_ops nand_ecc_sw_bch_engine_ops = {
+ .init_ctx = nand_ecc_sw_bch_init_ctx,
+ .cleanup_ctx = nand_ecc_sw_bch_cleanup_ctx,
+ .prepare_io_req = nand_ecc_sw_bch_prepare_io_req,
+ .finish_io_req = nand_ecc_sw_bch_finish_io_req,
+};
+
+static struct nand_ecc_engine nand_ecc_sw_bch_engine = {
+ .ops = &nand_ecc_sw_bch_engine_ops,
+};
+
+struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void)
+{
+ return &nand_ecc_sw_bch_engine;
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_get_engine);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
+MODULE_DESCRIPTION("NAND software BCH ECC support");
diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c
new file mode 100644
index 0000000000..f773a25a3e
--- /dev/null
+++ b/drivers/mtd/nand/ecc-sw-hamming.c
@@ -0,0 +1,660 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This file contains an ECC algorithm that detects and corrects 1 bit
+ * errors in a 256 byte block of data.
+ *
+ * Copyright © 2008 Koninklijke Philips Electronics NV.
+ * Author: Frans Meulenbroeks
+ *
+ * Completely replaces the previous ECC implementation which was written by:
+ * Steven J. Hill (sjhill@realitydiluted.com)
+ * Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Information on how this algorithm works and how it was developed
+ * can be found in Documentation/driver-api/mtd/nand_ecc.rst
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand-ecc-sw-hamming.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+/*
+ * invparity is a 256 byte table that contains the odd parity
+ * for each byte. So if the number of bits in a byte is even,
+ * the array element is 1, and when the number of bits is odd
+ * the array eleemnt is 0.
+ */
+static const char invparity[256] = {
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
+};
+
+/*
+ * bitsperbyte contains the number of bits per byte
+ * this is only used for testing and repairing parity
+ * (a precalculated value slightly improves performance)
+ */
+static const char bitsperbyte[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
+
+/*
+ * addressbits is a lookup table to filter out the bits from the xor-ed
+ * ECC data that identify the faulty location.
+ * this is only used for repairing parity
+ * see the comments in nand_ecc_sw_hamming_correct for more details
+ */
+static const char addressbits[256] = {
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f
+};
+
+int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size,
+ unsigned char *code, bool sm_order)
+{
+ const u32 *bp = (uint32_t *)buf;
+ const u32 eccsize_mult = (step_size == 256) ? 1 : 2;
+ /* current value in buffer */
+ u32 cur;
+ /* rp0..rp17 are the various accumulated parities (per byte) */
+ u32 rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9, rp10, rp11, rp12,
+ rp13, rp14, rp15, rp16, rp17;
+ /* Cumulative parity for all data */
+ u32 par;
+ /* Cumulative parity at the end of the loop (rp12, rp14, rp16) */
+ u32 tmppar;
+ int i;
+
+ par = 0;
+ rp4 = 0;
+ rp6 = 0;
+ rp8 = 0;
+ rp10 = 0;
+ rp12 = 0;
+ rp14 = 0;
+ rp16 = 0;
+ rp17 = 0;
+
+ /*
+ * The loop is unrolled a number of times;
+ * This avoids if statements to decide on which rp value to update
+ * Also we process the data by longwords.
+ * Note: passing unaligned data might give a performance penalty.
+ * It is assumed that the buffers are aligned.
+ * tmppar is the cumulative sum of this iteration.
+ * needed for calculating rp12, rp14, rp16 and par
+ * also used as a performance improvement for rp6, rp8 and rp10
+ */
+ for (i = 0; i < eccsize_mult << 2; i++) {
+ cur = *bp++;
+ tmppar = cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= tmppar;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp8 ^= tmppar;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp10 ^= tmppar;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp8 ^= cur;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+
+ par ^= tmppar;
+ if ((i & 0x1) == 0)
+ rp12 ^= tmppar;
+ if ((i & 0x2) == 0)
+ rp14 ^= tmppar;
+ if (eccsize_mult == 2 && (i & 0x4) == 0)
+ rp16 ^= tmppar;
+ }
+
+ /*
+ * handle the fact that we use longword operations
+ * we'll bring rp4..rp14..rp16 back to single byte entities by
+ * shifting and xoring first fold the upper and lower 16 bits,
+ * then the upper and lower 8 bits.
+ */
+ rp4 ^= (rp4 >> 16);
+ rp4 ^= (rp4 >> 8);
+ rp4 &= 0xff;
+ rp6 ^= (rp6 >> 16);
+ rp6 ^= (rp6 >> 8);
+ rp6 &= 0xff;
+ rp8 ^= (rp8 >> 16);
+ rp8 ^= (rp8 >> 8);
+ rp8 &= 0xff;
+ rp10 ^= (rp10 >> 16);
+ rp10 ^= (rp10 >> 8);
+ rp10 &= 0xff;
+ rp12 ^= (rp12 >> 16);
+ rp12 ^= (rp12 >> 8);
+ rp12 &= 0xff;
+ rp14 ^= (rp14 >> 16);
+ rp14 ^= (rp14 >> 8);
+ rp14 &= 0xff;
+ if (eccsize_mult == 2) {
+ rp16 ^= (rp16 >> 16);
+ rp16 ^= (rp16 >> 8);
+ rp16 &= 0xff;
+ }
+
+ /*
+ * we also need to calculate the row parity for rp0..rp3
+ * This is present in par, because par is now
+ * rp3 rp3 rp2 rp2 in little endian and
+ * rp2 rp2 rp3 rp3 in big endian
+ * as well as
+ * rp1 rp0 rp1 rp0 in little endian and
+ * rp0 rp1 rp0 rp1 in big endian
+ * First calculate rp2 and rp3
+ */
+#ifdef __BIG_ENDIAN
+ rp2 = (par >> 16);
+ rp2 ^= (rp2 >> 8);
+ rp2 &= 0xff;
+ rp3 = par & 0xffff;
+ rp3 ^= (rp3 >> 8);
+ rp3 &= 0xff;
+#else
+ rp3 = (par >> 16);
+ rp3 ^= (rp3 >> 8);
+ rp3 &= 0xff;
+ rp2 = par & 0xffff;
+ rp2 ^= (rp2 >> 8);
+ rp2 &= 0xff;
+#endif
+
+ /* reduce par to 16 bits then calculate rp1 and rp0 */
+ par ^= (par >> 16);
+#ifdef __BIG_ENDIAN
+ rp0 = (par >> 8) & 0xff;
+ rp1 = (par & 0xff);
+#else
+ rp1 = (par >> 8) & 0xff;
+ rp0 = (par & 0xff);
+#endif
+
+ /* finally reduce par to 8 bits */
+ par ^= (par >> 8);
+ par &= 0xff;
+
+ /*
+ * and calculate rp5..rp15..rp17
+ * note that par = rp4 ^ rp5 and due to the commutative property
+ * of the ^ operator we can say:
+ * rp5 = (par ^ rp4);
+ * The & 0xff seems superfluous, but benchmarking learned that
+ * leaving it out gives slightly worse results. No idea why, probably
+ * it has to do with the way the pipeline in pentium is organized.
+ */
+ rp5 = (par ^ rp4) & 0xff;
+ rp7 = (par ^ rp6) & 0xff;
+ rp9 = (par ^ rp8) & 0xff;
+ rp11 = (par ^ rp10) & 0xff;
+ rp13 = (par ^ rp12) & 0xff;
+ rp15 = (par ^ rp14) & 0xff;
+ if (eccsize_mult == 2)
+ rp17 = (par ^ rp16) & 0xff;
+
+ /*
+ * Finally calculate the ECC bits.
+ * Again here it might seem that there are performance optimisations
+ * possible, but benchmarks showed that on the system this is developed
+ * the code below is the fastest
+ */
+ if (sm_order) {
+ code[0] = (invparity[rp7] << 7) | (invparity[rp6] << 6) |
+ (invparity[rp5] << 5) | (invparity[rp4] << 4) |
+ (invparity[rp3] << 3) | (invparity[rp2] << 2) |
+ (invparity[rp1] << 1) | (invparity[rp0]);
+ code[1] = (invparity[rp15] << 7) | (invparity[rp14] << 6) |
+ (invparity[rp13] << 5) | (invparity[rp12] << 4) |
+ (invparity[rp11] << 3) | (invparity[rp10] << 2) |
+ (invparity[rp9] << 1) | (invparity[rp8]);
+ } else {
+ code[1] = (invparity[rp7] << 7) | (invparity[rp6] << 6) |
+ (invparity[rp5] << 5) | (invparity[rp4] << 4) |
+ (invparity[rp3] << 3) | (invparity[rp2] << 2) |
+ (invparity[rp1] << 1) | (invparity[rp0]);
+ code[0] = (invparity[rp15] << 7) | (invparity[rp14] << 6) |
+ (invparity[rp13] << 5) | (invparity[rp12] << 4) |
+ (invparity[rp11] << 3) | (invparity[rp10] << 2) |
+ (invparity[rp9] << 1) | (invparity[rp8]);
+ }
+
+ if (eccsize_mult == 1)
+ code[2] =
+ (invparity[par & 0xf0] << 7) |
+ (invparity[par & 0x0f] << 6) |
+ (invparity[par & 0xcc] << 5) |
+ (invparity[par & 0x33] << 4) |
+ (invparity[par & 0xaa] << 3) |
+ (invparity[par & 0x55] << 2) |
+ 3;
+ else
+ code[2] =
+ (invparity[par & 0xf0] << 7) |
+ (invparity[par & 0x0f] << 6) |
+ (invparity[par & 0xcc] << 5) |
+ (invparity[par & 0x33] << 4) |
+ (invparity[par & 0xaa] << 3) |
+ (invparity[par & 0x55] << 2) |
+ (invparity[rp17] << 1) |
+ (invparity[rp16] << 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(ecc_sw_hamming_calculate);
+
+/**
+ * nand_ecc_sw_hamming_calculate - Calculate 3-byte ECC for 256/512-byte block
+ * @nand: NAND device
+ * @buf: Input buffer with raw data
+ * @code: Output buffer with ECC
+ */
+int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
+ const unsigned char *buf, unsigned char *code)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int step_size = nand->ecc.ctx.conf.step_size;
+ bool sm_order = engine_conf ? engine_conf->sm_order : false;
+
+ return ecc_sw_hamming_calculate(buf, step_size, code, sm_order);
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate);
+
+int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc,
+ unsigned char *calc_ecc, unsigned int step_size,
+ bool sm_order)
+{
+ const u32 eccsize_mult = step_size >> 8;
+ unsigned char b0, b1, b2, bit_addr;
+ unsigned int byte_addr;
+
+ /*
+ * b0 to b2 indicate which bit is faulty (if any)
+ * we might need the xor result more than once,
+ * so keep them in a local var
+ */
+ if (sm_order) {
+ b0 = read_ecc[0] ^ calc_ecc[0];
+ b1 = read_ecc[1] ^ calc_ecc[1];
+ } else {
+ b0 = read_ecc[1] ^ calc_ecc[1];
+ b1 = read_ecc[0] ^ calc_ecc[0];
+ }
+
+ b2 = read_ecc[2] ^ calc_ecc[2];
+
+ /* check if there are any bitfaults */
+
+ /* repeated if statements are slightly more efficient than switch ... */
+ /* ordered in order of likelihood */
+
+ if ((b0 | b1 | b2) == 0)
+ return 0; /* no error */
+
+ if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) &&
+ (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) &&
+ ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) ||
+ (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) {
+ /* single bit error */
+ /*
+ * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty
+ * byte, cp 5/3/1 indicate the faulty bit.
+ * A lookup table (called addressbits) is used to filter
+ * the bits from the byte they are in.
+ * A marginal optimisation is possible by having three
+ * different lookup tables.
+ * One as we have now (for b0), one for b2
+ * (that would avoid the >> 1), and one for b1 (with all values
+ * << 4). However it was felt that introducing two more tables
+ * hardly justify the gain.
+ *
+ * The b2 shift is there to get rid of the lowest two bits.
+ * We could also do addressbits[b2] >> 1 but for the
+ * performance it does not make any difference
+ */
+ if (eccsize_mult == 1)
+ byte_addr = (addressbits[b1] << 4) + addressbits[b0];
+ else
+ byte_addr = (addressbits[b2 & 0x3] << 8) +
+ (addressbits[b1] << 4) + addressbits[b0];
+ bit_addr = addressbits[b2 >> 2];
+ /* flip the bit */
+ buf[byte_addr] ^= (1 << bit_addr);
+ return 1;
+
+ }
+ /* count nr of bits; use table lookup, faster than calculating it */
+ if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
+ return 1; /* error in ECC data; no action needed */
+
+ pr_err("%s: uncorrectable ECC error\n", __func__);
+ return -EBADMSG;
+}
+EXPORT_SYMBOL(ecc_sw_hamming_correct);
+
+/**
+ * nand_ecc_sw_hamming_correct - Detect and correct bit error(s)
+ * @nand: NAND device
+ * @buf: Raw data read from the chip
+ * @read_ecc: ECC bytes read from the chip
+ * @calc_ecc: ECC calculated from the raw data
+ *
+ * Detect and correct up to 1 bit error per 256/512-byte block.
+ */
+int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int step_size = nand->ecc.ctx.conf.step_size;
+ bool sm_order = engine_conf ? engine_conf->sm_order : false;
+
+ return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, step_size,
+ sm_order);
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_correct);
+
+int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+ struct nand_ecc_sw_hamming_conf *engine_conf;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int ret;
+
+ if (!mtd->ooblayout) {
+ switch (mtd->oobsize) {
+ case 8:
+ case 16:
+ mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout());
+ break;
+ case 64:
+ case 128:
+ mtd_set_ooblayout(mtd,
+ nand_get_large_page_hamming_ooblayout());
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ conf->algo = NAND_ECC_ALGO_HAMMING;
+ conf->step_size = nand->ecc.user_conf.step_size;
+ conf->strength = 1;
+
+ /* Use the strongest configuration by default */
+ if (conf->step_size != 256 && conf->step_size != 512)
+ conf->step_size = 256;
+
+ engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
+ if (!engine_conf)
+ return -ENOMEM;
+
+ ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand);
+ if (ret)
+ goto free_engine_conf;
+
+ engine_conf->code_size = 3;
+ engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+ engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+ if (!engine_conf->calc_buf || !engine_conf->code_buf) {
+ ret = -ENOMEM;
+ goto free_bufs;
+ }
+
+ nand->ecc.ctx.priv = engine_conf;
+ nand->ecc.ctx.nsteps = mtd->writesize / conf->step_size;
+ nand->ecc.ctx.total = nand->ecc.ctx.nsteps * engine_conf->code_size;
+
+ return 0;
+
+free_bufs:
+ nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+free_engine_conf:
+ kfree(engine_conf);
+
+ return ret;
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_init_ctx);
+
+void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+
+ if (engine_conf) {
+ nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+ kfree(engine_conf);
+ }
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_cleanup_ctx);
+
+static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = nand->ecc.ctx.nsteps;
+ int total = nand->ecc.ctx.total;
+ u8 *ecccalc = engine_conf->calc_buf;
+ const u8 *data;
+ int i;
+
+ /* Nothing to do for a raw operation */
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ nand_ecc_tweak_req(&engine_conf->req_ctx, req);
+
+ /* No more preparation for page read */
+ if (req->type == NAND_PAGE_READ)
+ return 0;
+
+ /* Preparation for page write: derive the ECC bytes and place them */
+ for (i = 0, data = req->databuf.out;
+ eccsteps;
+ eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
+
+ return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out,
+ 0, total);
+}
+
+static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int total = nand->ecc.ctx.total;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = nand->ecc.ctx.nsteps;
+ u8 *ecccalc = engine_conf->calc_buf;
+ u8 *ecccode = engine_conf->code_buf;
+ unsigned int max_bitflips = 0;
+ u8 *data = req->databuf.in;
+ int i, ret;
+
+ /* Nothing to do for a raw operation */
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ /* No more preparation for page write */
+ if (req->type == NAND_PAGE_WRITE) {
+ nand_ecc_restore_req(&engine_conf->req_ctx, req);
+ return 0;
+ }
+
+ /* Finish a page read: retrieve the (raw) ECC bytes*/
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0,
+ total);
+ if (ret)
+ return ret;
+
+ /* Calculate the ECC bytes */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
+
+ /* Finish a page read: compare and correct */
+ for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in;
+ eccsteps;
+ eccsteps--, i += eccbytes, data += eccsize) {
+ int stat = nand_ecc_sw_hamming_correct(nand, data,
+ &ecccode[i],
+ &ecccalc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ nand_ecc_restore_req(&engine_conf->req_ctx, req);
+
+ return max_bitflips;
+}
+
+static struct nand_ecc_engine_ops nand_ecc_sw_hamming_engine_ops = {
+ .init_ctx = nand_ecc_sw_hamming_init_ctx,
+ .cleanup_ctx = nand_ecc_sw_hamming_cleanup_ctx,
+ .prepare_io_req = nand_ecc_sw_hamming_prepare_io_req,
+ .finish_io_req = nand_ecc_sw_hamming_finish_io_req,
+};
+
+static struct nand_ecc_engine nand_ecc_sw_hamming_engine = {
+ .ops = &nand_ecc_sw_hamming_engine_ops,
+};
+
+struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void)
+{
+ return &nand_ecc_sw_hamming_engine;
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_get_engine);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
+MODULE_DESCRIPTION("NAND software Hamming ECC support");
diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
new file mode 100644
index 0000000000..60e1fa3cf2
--- /dev/null
+++ b/drivers/mtd/nand/ecc.c
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic Error-Correcting Code (ECC) engine
+ *
+ * Copyright (C) 2019 Macronix
+ * Author:
+ * Miquèl RAYNAL <miquel.raynal@bootlin.com>
+ *
+ *
+ * This file describes the abstraction of any NAND ECC engine. It has been
+ * designed to fit most cases, including parallel NANDs and SPI-NANDs.
+ *
+ * There are three main situations where instantiating this ECC engine makes
+ * sense:
+ * - external: The ECC engine is outside the NAND pipeline, typically this
+ * is a software ECC engine, or an hardware engine that is
+ * outside the NAND controller pipeline.
+ * - pipelined: The ECC engine is inside the NAND pipeline, ie. on the
+ * controller's side. This is the case of most of the raw NAND
+ * controllers. In the pipeline case, the ECC bytes are
+ * generated/data corrected on the fly when a page is
+ * written/read.
+ * - ondie: The ECC engine is inside the NAND pipeline, on the chip's side.
+ * Some NAND chips can correct themselves the data.
+ *
+ * Besides the initial setup and final cleanups, the interfaces are rather
+ * simple:
+ * - prepare: Prepare an I/O request. Enable/disable the ECC engine based on
+ * the I/O request type. In case of software correction or external
+ * engine, this step may involve to derive the ECC bytes and place
+ * them in the OOB area before a write.
+ * - finish: Finish an I/O request. Correct the data in case of a read
+ * request and report the number of corrected bits/uncorrectable
+ * errors. Most likely empty for write operations, unless you have
+ * hardware specific stuff to do, like shutting down the engine to
+ * save power.
+ *
+ * The I/O request should be enclosed in a prepare()/finish() pair of calls
+ * and will behave differently depending on the requested I/O type:
+ * - raw: Correction disabled
+ * - ecc: Correction enabled
+ *
+ * The request direction is impacting the logic as well:
+ * - read: Load data from the NAND chip
+ * - write: Store data in the NAND chip
+ *
+ * Mixing all this combinations together gives the following behavior.
+ * Those are just examples, drivers are free to add custom steps in their
+ * prepare/finish hook.
+ *
+ * [external ECC engine]
+ * - external + prepare + raw + read: do nothing
+ * - external + finish + raw + read: do nothing
+ * - external + prepare + raw + write: do nothing
+ * - external + finish + raw + write: do nothing
+ * - external + prepare + ecc + read: do nothing
+ * - external + finish + ecc + read: calculate expected ECC bytes, extract
+ * ECC bytes from OOB buffer, correct
+ * and report any bitflip/error
+ * - external + prepare + ecc + write: calculate ECC bytes and store them at
+ * the right place in the OOB buffer based
+ * on the OOB layout
+ * - external + finish + ecc + write: do nothing
+ *
+ * [pipelined ECC engine]
+ * - pipelined + prepare + raw + read: disable the controller's ECC engine if
+ * activated
+ * - pipelined + finish + raw + read: do nothing
+ * - pipelined + prepare + raw + write: disable the controller's ECC engine if
+ * activated
+ * - pipelined + finish + raw + write: do nothing
+ * - pipelined + prepare + ecc + read: enable the controller's ECC engine if
+ * deactivated
+ * - pipelined + finish + ecc + read: check the status, report any
+ * error/bitflip
+ * - pipelined + prepare + ecc + write: enable the controller's ECC engine if
+ * deactivated
+ * - pipelined + finish + ecc + write: do nothing
+ *
+ * [ondie ECC engine]
+ * - ondie + prepare + raw + read: send commands to disable the on-chip ECC
+ * engine if activated
+ * - ondie + finish + raw + read: do nothing
+ * - ondie + prepare + raw + write: send commands to disable the on-chip ECC
+ * engine if activated
+ * - ondie + finish + raw + write: do nothing
+ * - ondie + prepare + ecc + read: send commands to enable the on-chip ECC
+ * engine if deactivated
+ * - ondie + finish + ecc + read: send commands to check the status, report
+ * any error/bitflip
+ * - ondie + prepare + ecc + write: send commands to enable the on-chip ECC
+ * engine if deactivated
+ * - ondie + finish + ecc + write: do nothing
+ */
+
+#include <common.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+static LIST_HEAD(on_host_hw_engines);
+static DEFINE_MUTEX(on_host_hw_engines_mutex);
+
+/**
+ * nand_ecc_init_ctx - Init the ECC engine context
+ * @nand: the NAND device
+ *
+ * On success, the caller is responsible of calling @nand_ecc_cleanup_ctx().
+ */
+int nand_ecc_init_ctx(struct nand_device *nand)
+{
+ if (!nand->ecc.engine || !nand->ecc.engine->ops->init_ctx)
+ return 0;
+
+ return nand->ecc.engine->ops->init_ctx(nand);
+}
+EXPORT_SYMBOL(nand_ecc_init_ctx);
+
+/**
+ * nand_ecc_cleanup_ctx - Cleanup the ECC engine context
+ * @nand: the NAND device
+ */
+void nand_ecc_cleanup_ctx(struct nand_device *nand)
+{
+ if (nand->ecc.engine && nand->ecc.engine->ops->cleanup_ctx)
+ nand->ecc.engine->ops->cleanup_ctx(nand);
+}
+EXPORT_SYMBOL(nand_ecc_cleanup_ctx);
+
+/**
+ * nand_ecc_prepare_io_req - Prepare an I/O request
+ * @nand: the NAND device
+ * @req: the I/O request
+ */
+int nand_ecc_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ if (!nand->ecc.engine || !nand->ecc.engine->ops->prepare_io_req)
+ return 0;
+
+ return nand->ecc.engine->ops->prepare_io_req(nand, req);
+}
+EXPORT_SYMBOL(nand_ecc_prepare_io_req);
+
+/**
+ * nand_ecc_finish_io_req - Finish an I/O request
+ * @nand: the NAND device
+ * @req: the I/O request
+ */
+int nand_ecc_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ if (!nand->ecc.engine || !nand->ecc.engine->ops->finish_io_req)
+ return 0;
+
+ return nand->ecc.engine->ops->finish_io_req(nand, req);
+}
+EXPORT_SYMBOL(nand_ecc_finish_io_req);
+
+/* Define default OOB placement schemes for large and small page devices */
+static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+ if (section > 1)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 0;
+ if (mtd->oobsize == 16)
+ oobregion->length = 4;
+ else
+ oobregion->length = 3;
+ } else {
+ if (mtd->oobsize == 8)
+ return -ERANGE;
+
+ oobregion->offset = 6;
+ oobregion->length = total_ecc_bytes - 4;
+ }
+
+ return 0;
+}
+
+static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 1)
+ return -ERANGE;
+
+ if (mtd->oobsize == 16) {
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = 8;
+ oobregion->offset = 8;
+ } else {
+ oobregion->length = 2;
+ if (!section)
+ oobregion->offset = 3;
+ else
+ oobregion->offset = 6;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+ .ecc = nand_ooblayout_ecc_sp,
+ .free = nand_ooblayout_free_sp,
+};
+
+const struct mtd_ooblayout_ops *nand_get_small_page_ooblayout(void)
+{
+ return &nand_ooblayout_sp_ops;
+}
+EXPORT_SYMBOL_GPL(nand_get_small_page_ooblayout);
+
+static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+ if (section || !total_ecc_bytes)
+ return -ERANGE;
+
+ oobregion->length = total_ecc_bytes;
+ oobregion->offset = mtd->oobsize - oobregion->length;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = mtd->oobsize - total_ecc_bytes - 2;
+ oobregion->offset = 2;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+ .ecc = nand_ooblayout_ecc_lp,
+ .free = nand_ooblayout_free_lp,
+};
+
+const struct mtd_ooblayout_ops *nand_get_large_page_ooblayout(void)
+{
+ return &nand_ooblayout_lp_ops;
+}
+EXPORT_SYMBOL_GPL(nand_get_large_page_ooblayout);
+
+/*
+ * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
+ * are placed at a fixed offset.
+ */
+static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+ if (section)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ oobregion->offset = 40;
+ break;
+ case 128:
+ oobregion->offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ oobregion->length = total_ecc_bytes;
+ if (oobregion->offset + oobregion->length > mtd->oobsize)
+ return -ERANGE;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+ int ecc_offset = 0;
+
+ if (section < 0 || section > 1)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ ecc_offset = 40;
+ break;
+ case 128:
+ ecc_offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (section == 0) {
+ oobregion->offset = 2;
+ oobregion->length = ecc_offset - 2;
+ } else {
+ oobregion->offset = ecc_offset + total_ecc_bytes;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
+ .ecc = nand_ooblayout_ecc_lp_hamming,
+ .free = nand_ooblayout_free_lp_hamming,
+};
+
+const struct mtd_ooblayout_ops *nand_get_large_page_hamming_ooblayout(void)
+{
+ return &nand_ooblayout_lp_hamming_ops;
+}
+EXPORT_SYMBOL_GPL(nand_get_large_page_hamming_ooblayout);
+
+static enum nand_ecc_engine_type
+of_get_nand_ecc_engine_type(struct device_node *np)
+{
+ struct device_node *eng_np;
+
+ if (of_property_read_bool(np, "nand-no-ecc-engine"))
+ return NAND_ECC_ENGINE_TYPE_NONE;
+
+ if (of_property_read_bool(np, "nand-use-soft-ecc-engine"))
+ return NAND_ECC_ENGINE_TYPE_SOFT;
+
+ eng_np = of_parse_phandle(np, "nand-ecc-engine", 0);
+ of_node_put(eng_np);
+
+ if (eng_np) {
+ if (eng_np == np)
+ return NAND_ECC_ENGINE_TYPE_ON_DIE;
+ else
+ return NAND_ECC_ENGINE_TYPE_ON_HOST;
+ }
+
+ return NAND_ECC_ENGINE_TYPE_INVALID;
+}
+
+static const char * const nand_ecc_placement[] = {
+ [NAND_ECC_PLACEMENT_OOB] = "oob",
+ [NAND_ECC_PLACEMENT_INTERLEAVED] = "interleaved",
+};
+
+static enum nand_ecc_placement of_get_nand_ecc_placement(struct device_node *np)
+{
+ enum nand_ecc_placement placement;
+ const char *pm;
+ int err;
+
+ err = of_property_read_string(np, "nand-ecc-placement", &pm);
+ if (!err) {
+ for (placement = NAND_ECC_PLACEMENT_OOB;
+ placement < ARRAY_SIZE(nand_ecc_placement); placement++) {
+ if (!strcasecmp(pm, nand_ecc_placement[placement]))
+ return placement;
+ }
+ }
+
+ return NAND_ECC_PLACEMENT_UNKNOWN;
+}
+
+static const char * const nand_ecc_algos[] = {
+ [NAND_ECC_ALGO_HAMMING] = "hamming",
+ [NAND_ECC_ALGO_BCH] = "bch",
+ [NAND_ECC_ALGO_RS] = "rs",
+};
+
+static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
+{
+ enum nand_ecc_algo ecc_algo;
+ const char *pm;
+ int err;
+
+ err = of_property_read_string(np, "nand-ecc-algo", &pm);
+ if (!err) {
+ for (ecc_algo = NAND_ECC_ALGO_HAMMING;
+ ecc_algo < ARRAY_SIZE(nand_ecc_algos);
+ ecc_algo++) {
+ if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
+ return ecc_algo;
+ }
+ }
+
+ return NAND_ECC_ALGO_UNKNOWN;
+}
+
+static int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+ return ret ? ret : val;
+}
+
+static int of_get_nand_ecc_strength(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+ return ret ? ret : val;
+}
+
+void of_get_nand_ecc_user_config(struct nand_device *nand)
+{
+ struct device_node *dn = nanddev_get_of_node(nand);
+ int strength, size;
+
+ nand->ecc.user_conf.engine_type = of_get_nand_ecc_engine_type(dn);
+ nand->ecc.user_conf.algo = of_get_nand_ecc_algo(dn);
+ nand->ecc.user_conf.placement = of_get_nand_ecc_placement(dn);
+
+ strength = of_get_nand_ecc_strength(dn);
+ if (strength >= 0)
+ nand->ecc.user_conf.strength = strength;
+
+ size = of_get_nand_ecc_step_size(dn);
+ if (size >= 0)
+ nand->ecc.user_conf.step_size = size;
+
+ if (of_property_read_bool(dn, "nand-ecc-maximize"))
+ nand->ecc.user_conf.flags |= NAND_ECC_MAXIMIZE_STRENGTH;
+}
+EXPORT_SYMBOL(of_get_nand_ecc_user_config);
+
+/**
+ * nand_ecc_is_strong_enough - Check if the chip configuration meets the
+ * datasheet requirements.
+ *
+ * @nand: Device to check
+ *
+ * If our configuration corrects A bits per B bytes and the minimum
+ * required correction level is X bits per Y bytes, then we must ensure
+ * both of the following are true:
+ *
+ * (1) A / B >= X / Y
+ * (2) A >= X
+ *
+ * Requirement (1) ensures we can correct for the required bitflip density.
+ * Requirement (2) ensures we can correct even when all bitflips are clumped
+ * in the same sector.
+ */
+bool nand_ecc_is_strong_enough(struct nand_device *nand)
+{
+ const struct nand_ecc_props *reqs = nanddev_get_ecc_requirements(nand);
+ const struct nand_ecc_props *conf = nanddev_get_ecc_conf(nand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int corr, ds_corr;
+
+ if (conf->step_size == 0 || reqs->step_size == 0)
+ /* Not enough information */
+ return true;
+
+ /*
+ * We get the number of corrected bits per page to compare
+ * the correction density.
+ */
+ corr = (mtd->writesize * conf->strength) / conf->step_size;
+ ds_corr = (mtd->writesize * reqs->strength) / reqs->step_size;
+
+ return corr >= ds_corr && conf->strength >= reqs->strength;
+}
+EXPORT_SYMBOL(nand_ecc_is_strong_enough);
+
+/* ECC engine driver internal helpers */
+int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx,
+ struct nand_device *nand)
+{
+ unsigned int total_buffer_size;
+
+ ctx->nand = nand;
+
+ /* Let the user decide the exact length of each buffer */
+ if (!ctx->page_buffer_size)
+ ctx->page_buffer_size = nanddev_page_size(nand);
+ if (!ctx->oob_buffer_size)
+ ctx->oob_buffer_size = nanddev_per_page_oobsize(nand);
+
+ total_buffer_size = ctx->page_buffer_size + ctx->oob_buffer_size;
+
+ ctx->spare_databuf = kzalloc(total_buffer_size, GFP_KERNEL);
+ if (!ctx->spare_databuf)
+ return -ENOMEM;
+
+ ctx->spare_oobbuf = ctx->spare_databuf + ctx->page_buffer_size;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_ecc_init_req_tweaking);
+
+void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx)
+{
+ kfree(ctx->spare_databuf);
+}
+EXPORT_SYMBOL_GPL(nand_ecc_cleanup_req_tweaking);
+
+/*
+ * Ensure data and OOB area is fully read/written otherwise the correction might
+ * not work as expected.
+ */
+void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx,
+ struct nand_page_io_req *req)
+{
+ struct nand_device *nand = ctx->nand;
+ struct nand_page_io_req *orig, *tweak;
+
+ /* Save the original request */
+ ctx->orig_req = *req;
+ ctx->bounce_data = false;
+ ctx->bounce_oob = false;
+ orig = &ctx->orig_req;
+ tweak = req;
+
+ /* Ensure the request covers the entire page */
+ if (orig->datalen < nanddev_page_size(nand)) {
+ ctx->bounce_data = true;
+ tweak->dataoffs = 0;
+ tweak->datalen = nanddev_page_size(nand);
+ tweak->databuf.in = ctx->spare_databuf;
+ memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size);
+ }
+
+ if (orig->ooblen < nanddev_per_page_oobsize(nand)) {
+ ctx->bounce_oob = true;
+ tweak->ooboffs = 0;
+ tweak->ooblen = nanddev_per_page_oobsize(nand);
+ tweak->oobbuf.in = ctx->spare_oobbuf;
+ memset(tweak->oobbuf.in, 0xFF, ctx->oob_buffer_size);
+ }
+
+ /* Copy the data that must be writen in the bounce buffers, if needed */
+ if (orig->type == NAND_PAGE_WRITE) {
+ if (ctx->bounce_data)
+ memcpy((void *)tweak->databuf.out + orig->dataoffs,
+ orig->databuf.out, orig->datalen);
+
+ if (ctx->bounce_oob)
+ memcpy((void *)tweak->oobbuf.out + orig->ooboffs,
+ orig->oobbuf.out, orig->ooblen);
+ }
+}
+EXPORT_SYMBOL_GPL(nand_ecc_tweak_req);
+
+void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx,
+ struct nand_page_io_req *req)
+{
+ struct nand_page_io_req *orig, *tweak;
+
+ orig = &ctx->orig_req;
+ tweak = req;
+
+ /* Restore the data read from the bounce buffers, if needed */
+ if (orig->type == NAND_PAGE_READ) {
+ if (ctx->bounce_data)
+ memcpy(orig->databuf.in,
+ tweak->databuf.in + orig->dataoffs,
+ orig->datalen);
+
+ if (ctx->bounce_oob)
+ memcpy(orig->oobbuf.in,
+ tweak->oobbuf.in + orig->ooboffs,
+ orig->ooblen);
+ }
+
+ /* Ensure the original request is restored */
+ *req = *orig;
+}
+EXPORT_SYMBOL_GPL(nand_ecc_restore_req);
+
+struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand)
+{
+ unsigned int algo = nand->ecc.user_conf.algo;
+
+ if (algo == NAND_ECC_ALGO_UNKNOWN)
+ algo = nand->ecc.defaults.algo;
+
+ switch (algo) {
+ case NAND_ECC_ALGO_HAMMING:
+ return nand_ecc_sw_hamming_get_engine();
+ case NAND_ECC_ALGO_BCH:
+ return nand_ecc_sw_bch_get_engine();
+ default:
+ break;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(nand_ecc_get_sw_engine);
+
+struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand)
+{
+ return nand->ecc.ondie_engine;
+}
+EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
+
+int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine)
+{
+ struct nand_ecc_engine *item;
+
+ if (!engine)
+ return -EINVAL;
+
+ /* Prevent multiple registrations of one engine */
+ list_for_each_entry(item, &on_host_hw_engines, node)
+ if (item == engine)
+ return 0;
+
+ mutex_lock(&on_host_hw_engines_mutex);
+ list_add_tail(&engine->node, &on_host_hw_engines);
+ mutex_unlock(&on_host_hw_engines_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine);
+
+int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine)
+{
+ if (!engine)
+ return -EINVAL;
+
+ mutex_lock(&on_host_hw_engines_mutex);
+ list_del(&engine->node);
+ mutex_unlock(&on_host_hw_engines_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine);
+
+static struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev)
+{
+ struct nand_ecc_engine *item;
+
+ list_for_each_entry(item, &on_host_hw_engines, node)
+ if (item->dev == dev)
+ return item;
+
+ return NULL;
+}
+
+struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand)
+{
+ struct nand_ecc_engine *engine = NULL;
+ struct device *dev = &nand->mtd.dev;
+ struct device *pdev;
+ struct device_node *np;
+
+ if (list_empty(&on_host_hw_engines))
+ return NULL;
+
+ /* Check for an explicit nand-ecc-engine property */
+ np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0);
+ if (np) {
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ engine = nand_ecc_match_on_host_hw_engine(pdev);
+
+ if (!engine)
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return engine;
+}
+EXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine);
+
+void nand_ecc_put_on_host_hw_engine(struct nand_device *nand)
+{
+ put_device(nand->ecc.engine->dev);
+}
+EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("Generic ECC engine");
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
deleted file mode 100644
index 0d636d9608..0000000000
--- a/drivers/mtd/nand/nand_bch.c
+++ /dev/null
@@ -1,219 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * This file provides ECC correction for more than 1 bit per block of data,
- * using binary BCH codes. It relies on the generic BCH library lib/bch.c.
- *
- * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
- */
-
-#include <common.h>
-#include <malloc.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/nand_bch.h>
-#include <linux/bch.h>
-
-/**
- * struct nand_bch_control - private NAND BCH control structure
- * @bch: BCH control structure
- * @errloc: error location array
- * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
- */
-struct nand_bch_control {
- struct bch_control *bch;
- unsigned int *errloc;
- unsigned char *eccmask;
-};
-
-/**
- * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
- * @chip: NAND chip object
- * @buf: input buffer with raw data
- * @code: output buffer with ECC
- */
-int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
- unsigned char *code)
-{
- struct nand_bch_control *nbc = chip->ecc.priv;
- unsigned int i;
-
- memset(code, 0, chip->ecc.bytes);
- encode_bch(nbc->bch, buf, chip->ecc.size, code);
-
- /* apply mask so that an erased page is a valid codeword */
- for (i = 0; i < chip->ecc.bytes; i++)
- code[i] ^= nbc->eccmask[i];
-
- return 0;
-}
-EXPORT_SYMBOL(nand_bch_calculate_ecc);
-
-/**
- * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @chip: NAND chip object
- * @buf: raw data read from the chip
- * @read_ecc: ECC from the chip
- * @calc_ecc: the ECC calculated from raw data
- *
- * Detect and correct bit errors for a data byte block
- */
-int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
- unsigned char *read_ecc, unsigned char *calc_ecc)
-{
- struct nand_bch_control *nbc = chip->ecc.priv;
- unsigned int *errloc = nbc->errloc;
- int i, count;
-
- count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
- NULL, errloc);
- if (count > 0) {
- for (i = 0; i < count; i++) {
- if (errloc[i] < (chip->ecc.size*8))
- /* error is located in data, correct it */
- buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
- /* else error in ecc, no action needed */
-
- pr_debug("%s: corrected bitflip %u\n", __func__,
- errloc[i]);
- }
- } else if (count < 0) {
- pr_err("ecc unrecoverable error\n");
- count = -EBADMSG;
- }
- return count;
-}
-EXPORT_SYMBOL(nand_bch_correct_data);
-
-/**
- * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
- * @mtd: MTD block structure
- *
- * Returns:
- * a pointer to a new NAND BCH control structure, or NULL upon failure
- *
- * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
- * are used to compute BCH parameters m (Galois field order) and t (error
- * correction capability). @eccbytes should be equal to the number of bytes
- * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
- *
- * Example: to configure 4 bit correction per 512 bytes, you should pass
- * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
- * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
- */
-struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- unsigned int m, t, eccsteps, i;
- struct nand_bch_control *nbc = NULL;
- unsigned char *erased_page;
- unsigned int eccsize = nand->ecc.size;
- unsigned int eccbytes = nand->ecc.bytes;
- unsigned int eccstrength = nand->ecc.strength;
-
- if (!eccbytes && eccstrength) {
- eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
- nand->ecc.bytes = eccbytes;
- }
-
- if (!eccsize || !eccbytes) {
- pr_warn("ecc parameters not supplied\n");
- goto fail;
- }
-
- m = fls(1+8*eccsize);
- t = (eccbytes*8)/m;
-
- nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
- if (!nbc)
- goto fail;
-
- nbc->bch = init_bch(m, t, 0);
- if (!nbc->bch)
- goto fail;
-
- /* verify that eccbytes has the expected value */
- if (nbc->bch->ecc_bytes != eccbytes) {
- pr_warn("invalid eccbytes %u, should be %u\n",
- eccbytes, nbc->bch->ecc_bytes);
- goto fail;
- }
-
- eccsteps = mtd->writesize/eccsize;
-
- /* Check that we have an oob layout description. */
- if (!mtd->ooblayout) {
- pr_warn("missing oob scheme");
- goto fail;
- }
-
- /* sanity checks */
- if (8*(eccsize+eccbytes) >= (1 << m)) {
- pr_warn("eccsize %u is too large\n", eccsize);
- goto fail;
- }
-
- /*
- * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
- * which is called by mtd_ooblayout_count_eccbytes().
- * Make sure they are properly initialized before calling
- * mtd_ooblayout_count_eccbytes().
- * FIXME: we should probably rework the sequencing in nand_scan_tail()
- * to avoid setting those fields twice.
- */
- nand->ecc.steps = eccsteps;
- nand->ecc.total = eccsteps * eccbytes;
- if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
- pr_warn("invalid ecc layout\n");
- goto fail;
- }
-
- nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL);
- nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL);
- if (!nbc->eccmask || !nbc->errloc)
- goto fail;
- /*
- * compute and store the inverted ecc of an erased ecc block
- */
- erased_page = kmalloc(eccsize, GFP_KERNEL);
- if (!erased_page)
- goto fail;
-
- memset(erased_page, 0xff, eccsize);
- encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
- kfree(erased_page);
-
- for (i = 0; i < eccbytes; i++)
- nbc->eccmask[i] ^= 0xff;
-
- if (!eccstrength)
- nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
-
- return nbc;
-fail:
- nand_bch_free(nbc);
- return NULL;
-}
-EXPORT_SYMBOL(nand_bch_init);
-
-/**
- * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
- * @nbc: NAND BCH control structure
- */
-void nand_bch_free(struct nand_bch_control *nbc)
-{
- if (nbc) {
- free_bch(nbc->bch);
- kfree(nbc->errloc);
- kfree(nbc->eccmask);
- kfree(nbc);
- }
-}
-EXPORT_SYMBOL(nand_bch_free);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
-MODULE_DESCRIPTION("NAND software BCH ECC support");
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
deleted file mode 100644
index 66c53670d3..0000000000
--- a/drivers/mtd/nand/nand_imx.c
+++ /dev/null
@@ -1,1493 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- */
-
-/*
- * MX21 Hardware contains a bug which causes HW ECC to fail for two
- * consecutive read pages containing 1bit Errors (See MX21 Chip Erata,
- * Erratum 16). Use software ECC for this chip.
- */
-
-#include <common.h>
-#include <driver.h>
-#include <malloc.h>
-#include <init.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/clk.h>
-#include <mach/generic.h>
-#include <mach/imx-nand.h>
-#include <io.h>
-#include <of_mtd.h>
-#include <errno.h>
-
-struct imx_nand_host {
- struct nand_chip nand;
- struct mtd_partition *parts;
- struct device_d *dev;
-
- void *spare0;
- void *main_area0;
-
- void __iomem *base;
- void __iomem *regs;
- void __iomem *regs_axi;
- void __iomem *regs_ip;
- int status_request;
- struct clk *clk;
-
- int pagesize_2k;
- uint8_t *data_buf;
- unsigned int buf_start;
- int spare_len;
- int eccsize;
- int eccstatus_v1;
-
- int hw_ecc;
- int data_width;
- int flash_bbt;
-
- void (*preset)(struct nand_chip *);
- void (*send_cmd)(struct imx_nand_host *, uint16_t);
- void (*send_addr)(struct imx_nand_host *, uint16_t);
- void (*send_page)(struct imx_nand_host *, unsigned int);
- void (*send_read_id)(struct imx_nand_host *);
- void (*send_read_param)(struct imx_nand_host *);
- uint16_t (*get_dev_status)(struct imx_nand_host *);
- int (*check_int)(struct imx_nand_host *);
- int (*correct)(struct nand_chip *);
- void (*enable_hwecc)(struct nand_chip *, bool enable);
-};
-
-/*
- * OOB placement block for use with hardware ecc generation
- */
-static struct nand_ecclayout nandv1_hw_eccoob_smallpage = {
- .eccbytes = 5,
- .eccpos = {6, 7, 8, 9, 10},
- .oobfree = {{0, 5}, {12, 4}}
-};
-
-static struct nand_ecclayout nandv1_hw_eccoob_largepage = {
- .eccbytes = 20,
- .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
- 38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
- .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
-};
-
-/* OOB description for 512 byte pages with 16 byte OOB */
-static struct nand_ecclayout nandv2_hw_eccoob_smallpage = {
- .eccbytes = 1 * 9,
- .eccpos = {
- 7, 8, 9, 10, 11, 12, 13, 14, 15
- },
- .oobfree = {
- {.offset = 0, .length = 5}
- }
-};
-
-/* OOB description for 2048 byte pages with 64 byte OOB */
-static struct nand_ecclayout nandv2_hw_eccoob_largepage = {
- .eccbytes = 4 * 9,
- .eccpos = {
- 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 39, 40, 41, 42, 43, 44, 45, 46, 47,
- 55, 56, 57, 58, 59, 60, 61, 62, 63
- },
- .oobfree = {
- {.offset = 2, .length = 4},
- {.offset = 16, .length = 7},
- {.offset = 32, .length = 7},
- {.offset = 48, .length = 7}
- }
-};
-
-/* OOB description for 4096 byte pages with 128 byte OOB */
-static struct nand_ecclayout nandv2_hw_eccoob_4k = {
- .eccbytes = 8 * 9,
- .eccpos = {
- 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 39, 40, 41, 42, 43, 44, 45, 46, 47,
- 55, 56, 57, 58, 59, 60, 61, 62, 63,
- 71, 72, 73, 74, 75, 76, 77, 78, 79,
- 87, 88, 89, 90, 91, 92, 93, 94, 95,
- 103, 104, 105, 106, 107, 108, 109, 110, 111,
- 119, 120, 121, 122, 123, 124, 125, 126, 127,
- },
- .oobfree = {
- {.offset = 2, .length = 4},
- {.offset = 16, .length = 7},
- {.offset = 32, .length = 7},
- {.offset = 48, .length = 7},
- {.offset = 64, .length = 7},
- {.offset = 80, .length = 7},
- {.offset = 96, .length = 7},
- {.offset = 112, .length = 7},
- }
-};
-
-static void memcpy32(void *trg, const void *src, int size)
-{
- int i;
- unsigned int *t = trg;
- unsigned const int *s = src;
-
-#ifdef CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS
- if (!((unsigned long)trg & 0x3) && !((unsigned long)src & 0x3))
- memcpy(trg, src, size);
- else
-#endif
- for (i = 0; i < (size >> 2); i++)
- *t++ = *s++;
-}
-
-static int check_int_v3(struct imx_nand_host *host)
-{
- uint32_t tmp;
-
- tmp = readl(NFC_V3_IPC);
- if (!(tmp & NFC_V3_IPC_INT))
- return 0;
-
- tmp &= ~NFC_V3_IPC_INT;
- writel(tmp, NFC_V3_IPC);
-
- return 1;
-}
-
-static int check_int_v1_v2(struct imx_nand_host *host)
-{
- uint32_t tmp;
-
- tmp = readw(host->regs + NFC_V1_V2_CONFIG2);
- if (!(tmp & NFC_V1_V2_CONFIG2_INT))
- return 0;
-
- writew(tmp & ~NFC_V1_V2_CONFIG2_INT, host->regs + NFC_V1_V2_CONFIG2);
-
- return 1;
-}
-
-static void wait_op_done(struct imx_nand_host *host)
-{
- int i;
-
- /* This is a timeout of roughly 15ms on my system. We
- * need about 2us, but be generous. Don't use udelay
- * here as we might be here from nand booting.
- */
- for (i = 0; i < 100000; i++) {
- if (host->check_int(host))
- return;
- }
-}
-
-/*
- * This function issues the specified command to the NAND device and
- * waits for completion.
- *
- * @param cmd command for NAND Flash
- */
-static void send_cmd_v3(struct imx_nand_host *host, uint16_t cmd)
-{
- /* fill command */
- writel(cmd, NFC_V3_FLASH_CMD);
-
- /* send out command */
- writel(NFC_CMD, NFC_V3_LAUNCH);
-
- /* Wait for operation to complete */
- wait_op_done(host);
-}
-
-static void send_cmd_v1_v2(struct imx_nand_host *host, u16 cmd)
-{
- writew(cmd, host->regs + NFC_V1_V2_FLASH_CMD);
- writew(NFC_CMD, host->regs + NFC_V1_V2_CONFIG2);
-
- if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) {
- /* Reset completion is indicated by NFC_CONFIG2 */
- /* being set to 0 */
- int i;
- for (i = 0; i < 100000; i++) {
- if (readw(host->regs + NFC_V1_V2_CONFIG2) == 0) {
- break;
- }
- }
- } else
- /* Wait for operation to complete */
- wait_op_done(host);
-}
-
-/*
- * This function sends an address (or partial address) to the
- * NAND device. The address is used to select the source/destination for
- * a NAND command.
- *
- * @param addr address to be written to NFC.
- * @param islast True if this is the last address cycle for command
- */
-static void send_addr_v3(struct imx_nand_host *host, uint16_t addr)
-{
- /* fill address */
- writel(addr, NFC_V3_FLASH_ADDR0);
-
- /* send out address */
- writel(NFC_ADDR, NFC_V3_LAUNCH);
-
- wait_op_done(host);
-}
-
-static void send_addr_v1_v2(struct imx_nand_host *host, u16 addr)
-{
- writew(addr, host->regs + NFC_V1_V2_FLASH_ADDR);
- writew(NFC_ADDR, host->regs + NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host);
-}
-
-/*
- * This function requests the NANDFC to initate the transfer
- * of data currently in the NANDFC RAM buffer to the NAND device.
- *
- * @param buf_id Specify Internal RAM Buffer number (0-3)
- * @param spare_only set true if only the spare area is transferred
- */
-static void send_page_v3(struct imx_nand_host *host, unsigned int ops)
-{
- uint32_t tmp;
-
- tmp = readl(NFC_V3_CONFIG1);
- tmp &= ~(7 << 4);
- writel(tmp, NFC_V3_CONFIG1);
-
- /* transfer data from NFC ram to nand */
- writel(ops, NFC_V3_LAUNCH);
-
- wait_op_done(host);
-}
-
-static void send_page_v1_v2(struct imx_nand_host *host,
- unsigned int ops)
-{
- int bufs, i;
-
- host->eccstatus_v1 = 0;
-
- if (nfc_is_v1() && host->pagesize_2k)
- bufs = 4;
- else
- bufs = 1;
-
- for (i = 0; i < bufs; i++) {
- u16 status;
- int errors;
-
- /* NANDFC buffer 0 is used for page read/write */
- writew(i, host->regs + NFC_V1_V2_BUF_ADDR);
-
- writew(ops, host->regs + NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host);
-
- status = readw(host->regs + NFC_V1_ECC_STATUS_RESULT);
- errors = max(status & 0x3, status >> 2);
-
- if (errors == 1 && host->eccstatus_v1 >= 0)
- host->eccstatus_v1++;
- if (errors == 2)
- host->eccstatus_v1 = -EBADMSG;
- }
-}
-
-/*
- * This function requests the NANDFC to perform a read of the
- * NAND device ID.
- */
-static void send_read_id_v3(struct imx_nand_host *host)
-{
- /* Read ID into main buffer */
- writel(NFC_ID, NFC_V3_LAUNCH);
-
- wait_op_done(host);
-
- /*
- * NFC_ID results in reading 6 bytes or words (depending on data width),
- * so copying 3 32-bit values is just fine.
- */
- memcpy(host->data_buf, host->main_area0, 12);
-}
-
-static void send_read_param_v3(struct imx_nand_host *host)
-{
- /* Read ID into main buffer */
- writel(NFC_OUTPUT, NFC_V3_LAUNCH);
-
- wait_op_done(host);
-
- memcpy(host->data_buf, host->main_area0, 1024);
-}
-
-static void send_read_id_v1_v2(struct imx_nand_host *host)
-{
- /* NANDFC buffer 0 is used for device ID output */
- writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR);
-
- writew(NFC_ID, host->regs + NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host);
-
- /*
- * NFC_ID results in reading 6 bytes or words (depending on data width),
- * so copying 3 32-bit values is just fine.
- */
- memcpy32(host->data_buf, host->main_area0, 12);
-}
-
-static void send_read_param_v1_v2(struct imx_nand_host *host)
-{
- u32 backup = readw(host->regs + NFC_V1_V2_CONFIG1);
-
- /* Temporary disable ECC to be able to read param page */
- writew(backup & ~NFC_V1_V2_CONFIG1_ECC_EN, host->regs + NFC_V1_V2_CONFIG1);
-
- /* NANDFC buffer 0 is used for param output */
- writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR);
-
- writew(NFC_OUTPUT, host->regs + NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host);
-
- memcpy32(host->data_buf, host->main_area0, 1024);
-
- /* Restore original CONFIG1 value */
- writew(backup, host->regs + NFC_V1_V2_CONFIG1);
-}
-/*
- * This function requests the NANDFC to perform a read of the
- * NAND device status and returns the current status.
- *
- * @return device status
- */
-static uint16_t get_dev_status_v3(struct imx_nand_host *host)
-{
- writew(NFC_STATUS, NFC_V3_LAUNCH);
- wait_op_done(host);
-
- return readl(NFC_V3_CONFIG1) >> 16;
-}
-
-static u16 get_dev_status_v1_v2(struct imx_nand_host *host)
-{
- void *main_buf = host->main_area0;
- u32 store;
- u16 ret;
-
- writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR);
-
- /*
- * The device status is stored in main_area0. To
- * prevent corruption of the buffer save the value
- * and restore it afterwards.
- */
- store = readl(main_buf);
-
- writew(NFC_STATUS, host->regs + NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host);
-
- /* Status is placed in first word of main buffer */
- /* get status, then recovery area 1 data */
- ret = readw(main_buf);
-
- writel(store, main_buf);
-
- return ret;
-}
-
-/*
- * This function is used by upper layer to checks if device is ready
- *
- * @param mtd MTD structure for the NAND Flash
- *
- * @return 0 if device is busy else 1
- */
-static int imx_nand_dev_ready(struct nand_chip *chip)
-{
- /*
- * NFC handles R/B internally.Therefore,this function
- * always returns status as ready.
- */
- return 1;
-}
-
-static void imx_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable)
-{
- struct imx_nand_host *host = chip->priv;
- uint16_t config1;
-
- if (chip->ecc.mode != NAND_ECC_HW)
- return;
-
- config1 = readw(host->regs + NFC_V1_V2_CONFIG1);
-
- if (enable)
- config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
- else
- config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN;
-
- writew(config1, host->regs + NFC_V1_V2_CONFIG1);
-
-}
-
-static void imx_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
-{
- struct imx_nand_host *host = chip->priv;
- uint32_t config2;
-
- if (chip->ecc.mode != NAND_ECC_HW)
- return;
-
- config2 = readl(NFC_V3_CONFIG2);
-
- if (enable)
- config2 |= NFC_V3_CONFIG2_ECC_EN;
- else
- config2 &= ~NFC_V3_CONFIG2_ECC_EN;
-
- writel(config2, NFC_V3_CONFIG2);
-}
-
-static int imx_nand_correct_data_v1(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
-
- if (host->eccstatus_v1 < 0)
- return host->eccstatus_v1;
-
- mtd->ecc_stats.corrected += host->eccstatus_v1;
-
- if (host->eccstatus_v1 > 0)
- return 1;
- else
- return 0;
-}
-
-static int imx_nand_correct_data_v2_v3(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- u32 ecc_stat, err;
- int no_subpages;
- u8 ecc_bit_mask, err_limit, max_bitflips = 0;
-
- ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
- err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
-
- no_subpages = mtd->writesize >> 9;
-
- if (nfc_is_v21())
- ecc_stat = readl(host->regs + NFC_V2_ECC_STATUS_RESULT1);
- else
- ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT);
-
- do {
- err = ecc_stat & ecc_bit_mask;
- if (err > err_limit)
- return -EBADMSG;
- ecc_stat >>= 4;
- max_bitflips = max_t(unsigned int, max_bitflips, err);
- mtd->ecc_stats.corrected += err;
- } while (--no_subpages);
-
- return max_bitflips;
-}
-
-static int imx_nand_calculate_ecc(struct nand_chip *chip, const u_char * dat,
- u_char * ecc_code)
-{
- return 0;
-}
-
-/*
- * This function reads byte from the NAND Flash
- *
- * @param mtd MTD structure for the NAND Flash
- *
- * @return data read from the NAND Flash
- */
-static u_char imx_nand_read_byte(struct nand_chip *chip)
-{
- struct imx_nand_host *host = chip->priv;
- u_char ret;
-
- /* Check for status request */
- if (host->status_request)
- return host->get_dev_status(host) & 0xFF;
-
- if (chip->options & NAND_BUSWIDTH_16) {
- /* only take the lower byte of each word */
- BUG_ON(host->buf_start & 1);
- ret = *(uint16_t *)(host->data_buf + host->buf_start);
-
- host->buf_start += 2;
- } else {
- ret = *(uint8_t *)(host->data_buf + host->buf_start);
- host->buf_start++;
- }
-
- return ret;
-}
-
-/*
- * This function reads word from the NAND Flash
- *
- * @param mtd MTD structure for the NAND Flash
- *
- * @return data read from the NAND Flash
- */
-static u16 imx_nand_read_word(struct nand_chip *chip)
-{
- struct imx_nand_host *host = chip->priv;
- uint16_t ret;
-
- ret = *(uint16_t *)(host->data_buf + host->buf_start);
- host->buf_start += 2;
-
- return ret;
-}
-
-/*
- * This function writes data of length \b len to buffer \b buf. The data to be
- * written on NAND Flash is first copied to RAMbuffer. After the Data Input
- * Operation by the NFC, the data is written to NAND Flash
- *
- * @param mtd MTD structure for the NAND Flash
- * @param buf data to be written to NAND Flash
- * @param len number of bytes to be written
- */
-static void imx_nand_write_buf(struct nand_chip *chip,
- const u_char *buf, int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- u16 col = host->buf_start;
- int n = mtd->oobsize + mtd->writesize - col;
-
- n = min(n, len);
- memcpy(host->data_buf + col, buf, n);
-
- host->buf_start += n;
-}
-
-/*
- * This function is used to read the data buffer from the NAND Flash. To
- * read the data from NAND Flash first the data output cycle is initiated by
- * the NFC, which copies the data to RAMbuffer. This data of length \b len is
- * then copied to buffer \b buf.
- *
- * @param mtd MTD structure for the NAND Flash
- * @param buf data to be read from NAND Flash
- * @param len number of bytes to be read
- */
-static void imx_nand_read_buf(struct nand_chip *chip, u_char * buf, int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- u16 col = host->buf_start;
- int n = mtd->oobsize + mtd->writesize - col;
-
- n = min(n, len);
-
- /* handle the read param special case */
- if ((mtd->writesize == 0) && (len != 0))
- n = len;
-
- memcpy(buf, host->data_buf + col, n);
-
- host->buf_start += n;
-}
-
-/*
- * Function to transfer data to/from spare area.
- */
-static void copy_spare(struct nand_chip *chip, int bfrom, void *buf)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- u16 i, j;
- u16 n = mtd->writesize >> 9;
- u8 *d = buf;
- u8 *s = host->spare0;
- u16 t = host->spare_len;
-
- j = (mtd->oobsize / n >> 1) << 1;
-
- if (bfrom) {
- for (i = 0; i < n - 1; i++)
- memcpy32(d + i * j, s + i * t, j);
-
- /* the last section */
- memcpy32(d + i * j, s + i * t, mtd->oobsize - i * j);
- } else {
- for (i = 0; i < n - 1; i++)
- memcpy32(&s[i * t], &d[i * j], j);
-
- /* the last section */
- memcpy32(&s[i * t], &d[i * j], mtd->oobsize - i * j);
- }
-}
-
-/*
- * This function is used by upper layer for select and deselect of the NAND
- * chip
- *
- * @param mtd MTD structure for the NAND Flash
- * @param chip val indicating select or deselect
- */
-static void imx_nand_select_chip(struct nand_chip *_chip, int chip)
-{
-}
-
-static void mxc_do_addr_cycle(struct nand_chip *chip, int column, int page_addr)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
-
- /*
- * Write out column address, if necessary
- */
- if (column != -1) {
- /*
- * MXC NANDFC can only perform full page+spare or
- * spare-only read/write. When the upper layers
- * layers perform a read/write buf operation,
- * we will used the saved column adress to index into
- * the full page.
- *
- * The colum address must be sent to the flash in
- * order to get the ONFI header (0x20)
- */
- host->send_addr(host, column);
- if (host->pagesize_2k)
- /* another col addr cycle for 2k page */
- host->send_addr(host, 0);
- }
-
- /*
- * Write out page address, if necessary
- */
- if (page_addr != -1) {
- host->send_addr(host, (page_addr & 0xff)); /* paddr_0 - p_addr_7 */
-
- if (host->pagesize_2k) {
- host->send_addr(host, (page_addr >> 8) & 0xFF);
- if (mtd->size >= 0x10000000) {
- host->send_addr(host, (page_addr >> 16) & 0xff);
- }
- } else {
- /* One more address cycle for higher density devices */
- if (mtd->size >= 0x4000000) {
- /* paddr_8 - paddr_15 */
- host->send_addr(host, (page_addr >> 8) & 0xff);
- host->send_addr(host, (page_addr >> 16) & 0xff);
- } else
- /* paddr_8 - paddr_15 */
- host->send_addr(host, (page_addr >> 8) & 0xff);
- }
- }
-}
-
-/*
- * v2 and v3 type controllers can do 4bit or 8bit ecc depending
- * on how much oob the nand chip has. For 8bit ecc we need at least
- * 26 bytes of oob data per 512 byte block.
- */
-static int get_eccsize(struct mtd_info *mtd)
-{
- int oobbytes_per_512 = 0;
-
- oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize;
-
- if (oobbytes_per_512 < 26)
- return 4;
- else
- return 8;
-}
-
-static void preset_v1(struct nand_chip *chip)
-{
- struct imx_nand_host *host = chip->priv;
- uint16_t config1 = 0;
-
- host->eccsize = 1;
-
- writew(config1, host->regs + NFC_V1_V2_CONFIG1);
- /* preset operation */
-
- /* Unlock the internal RAM Buffer */
- writew(0x2, host->regs + NFC_V1_V2_CONFIG);
-
- /* Blocks to be unlocked */
- writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR);
- writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR);
-
- /* Unlock Block Command for given address range */
- writew(0x4, host->regs + NFC_V1_V2_WRPROT);
-}
-
-static void preset_v2(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- uint16_t config1 = 0;
- int mode;
-
- mode = onfi_get_async_timing_mode(chip);
- if (mode != ONFI_TIMING_MODE_UNKNOWN && !IS_ERR(host->clk)) {
- const struct nand_sdr_timings *timings;
-
- mode = fls(mode) - 1;
- if (mode < 0)
- mode = 0;
-
- timings = onfi_async_timing_mode_to_sdr_timings(mode);
- if (!IS_ERR(timings)) {
- unsigned long rate;
- int tRC_min_ns = timings->tRC_min / 1000;
-
- rate = 1000000000 / tRC_min_ns;
- if (tRC_min_ns < 30)
- /* If tRC is smaller than 30ns we have to use EDO timing */
- config1 |= NFC_V1_V2_CONFIG1_ONE_CYCLE;
- else
- /* Otherwise we have two clock cycles per access */
- rate *= 2;
-
- clk_set_rate(host->clk, rate);
- }
- }
-
- config1 |= NFC_V2_CONFIG1_FP_INT;
-
- if (mtd->writesize) {
- uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
-
- host->eccsize = get_eccsize(mtd);
- if (host->eccsize == 4)
- config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
-
- config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6);
- } else {
- host->eccsize = 1;
- }
-
- writew(config1, host->regs + NFC_V1_V2_CONFIG1);
- /* preset operation */
-
- /* Unlock the internal RAM Buffer */
- writew(0x2, host->regs + NFC_V1_V2_CONFIG);
-
- /* Blocks to be unlocked */
- writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR);
- writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR);
-
- /* Unlock Block Command for given address range */
- writew(0x4, host->regs + NFC_V1_V2_WRPROT);
-}
-
-static void preset_v3(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- uint32_t config2, config3;
- int i, addr_phases;
-
- writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
- writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);
-
- /* Unlock the internal RAM Buffer */
- writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
- NFC_V3_WRPROT);
-
- /* Blocks to be unlocked */
- for (i = 0; i < NAND_MAX_CHIPS; i++)
- writel(0x0 | (0xffff << 16),
- NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
-
- writel(0, NFC_V3_IPC);
-
- /* if the flash has a 224 oob, the NFC must be configured to 218 */
- config2 = NFC_V3_CONFIG2_ONE_CYCLE |
- NFC_V3_CONFIG2_2CMD_PHASES |
- NFC_V3_CONFIG2_SPAS(((mtd->oobsize > 218) ?
- 218 : mtd->oobsize) >> 1) |
- NFC_V3_CONFIG2_ST_CMD(0x70) |
- NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
-
- addr_phases = fls(chip->pagemask) >> 3;
-
- if (mtd->writesize == 2048) {
- config2 |= NFC_V3_CONFIG2_PS_2048;
- config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
- } else if (mtd->writesize == 4096) {
- config2 |= NFC_V3_CONFIG2_PS_4096;
- config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
- } else {
- config2 |= NFC_V3_CONFIG2_PS_512;
- config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
- }
-
- if (mtd->writesize) {
- if (cpu_is_mx51())
- config2 |= NFC_V3_MX51_CONFIG2_PPB(
- ffs(mtd->erasesize / mtd->writesize) - 6);
- else
- config2 |= NFC_V3_MX53_CONFIG2_PPB(
- ffs(mtd->erasesize / mtd->writesize) - 6);
- host->eccsize = get_eccsize(mtd);
- if (host->eccsize == 8)
- config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
- }
-
- writel(config2, NFC_V3_CONFIG2);
-
- config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
- NFC_V3_CONFIG3_NO_SDMA |
- NFC_V3_CONFIG3_RBB_MODE |
- NFC_V3_CONFIG3_SBB(6) | /* Reset default */
- NFC_V3_CONFIG3_ADD_OP(0);
-
- if (!(chip->options & NAND_BUSWIDTH_16))
- config3 |= NFC_V3_CONFIG3_FW8;
-
- writel(config3, NFC_V3_CONFIG3);
-
- writel(0, NFC_V3_DELAY_LINE);
-}
-
-static int imx_nand_write_page(struct nand_chip *chip,
- const uint8_t *buf, bool ecc, int page)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
- int status;
-
- host->enable_hwecc(chip, ecc);
-
- chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
-
- memcpy32(host->main_area0, buf, mtd->writesize);
- copy_spare(chip, 0, chip->oob_poi);
-
- host->send_page(host, NFC_INPUT);
- chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return 0;
-}
-
-static int imx_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf,
- int oob_required, int page)
-{
- return imx_nand_write_page(chip, buf, true, page);
-}
-
-static int imx_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
- int oob_required, int page)
-{
- return imx_nand_write_page(chip, buf, false, page);
-}
-
-static void imx_nand_do_read_page(struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- host->send_page(host, NFC_OUTPUT);
-
- memcpy32(buf, host->main_area0, mtd->writesize);
-
- if (oob_required)
- copy_spare(chip, 1, chip->oob_poi);
-}
-
-static int imx_nand_read_page(struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- struct imx_nand_host *host = chip->priv;
-
- host->enable_hwecc(chip, true);
-
- imx_nand_do_read_page(chip, buf, oob_required, page);
-
- return host->correct(chip);
-}
-
-static int imx_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- struct imx_nand_host *host = chip->priv;
-
- host->enable_hwecc(chip, false);
-
- imx_nand_do_read_page(chip, buf, oob_required, page);
-
- return 0;
-}
-
-/*
- * This function is used by the upper layer to write command to NAND Flash for
- * different operations to be carried out on NAND Flash
- *
- * @param mtd MTD structure for the NAND Flash
- * @param command command for NAND Flash
- * @param column column offset for the page read
- * @param page_addr page to be read from NAND Flash
- */
-static void imx_nand_command(struct nand_chip *chip, unsigned command,
- int column, int page_addr)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct imx_nand_host *host = chip->priv;
-
- dev_dbg(host->dev,
- "imx_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
- command, column, page_addr);
-
- /*
- * Reset command state information
- */
- host->status_request = 0;
-
- /*
- * Command pre-processing step
- */
- switch (command) {
- case NAND_CMD_RESET:
- host->preset(chip);
- host->send_cmd(host, command);
- break;
-
- case NAND_CMD_STATUS:
- host->buf_start = 0;
- host->status_request = 1;
- host->send_cmd(host, command);
- mxc_do_addr_cycle(chip, column, page_addr);
- break;
-
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- if (command == NAND_CMD_READ0)
- host->buf_start = column;
- else
- host->buf_start = column + mtd->writesize;
-
- host->send_cmd(host, NAND_CMD_READ0);
- mxc_do_addr_cycle(chip, column, page_addr);
-
- if (host->pagesize_2k)
- /* send read confirm command */
- host->send_cmd(host, NAND_CMD_READSTART);
-
- /*
- * After the core issued READOOB the result is read using
- * .read_buf, so we have to make sure the data is actually
- * there.
- */
- if (command == NAND_CMD_READOOB) {
- host->send_page(host, NFC_OUTPUT);
- copy_spare(chip, 1, host->data_buf + mtd->writesize);
- }
-
- break;
-
- case NAND_CMD_SEQIN:
- if (column >= mtd->writesize) {
- if (host->pagesize_2k) {
- /**
- * FIXME: before send SEQIN command for write
- * OOB, we must read one page out. For K9F1GXX
- * has no READ1 command to set current HW
- * pointer to spare area, we must write the
- * whole page including OOB together.
- */
- /* call ourself to read a page */
- imx_nand_command(chip, NAND_CMD_READ0, 0,
- page_addr);
- }
- host->buf_start = column;
-
- /* Set program pointer to spare region */
- if (!host->pagesize_2k)
- host->send_cmd(host, NAND_CMD_READOOB);
- } else {
- host->buf_start = column;
-
- /* Set program pointer to page start */
- if (!host->pagesize_2k)
- host->send_cmd(host, NAND_CMD_READ0);
- }
- host->send_cmd(host, command);
- mxc_do_addr_cycle(chip, column, page_addr);
-
- break;
-
- case NAND_CMD_PAGEPROG:
- host->send_cmd(host, command);
- mxc_do_addr_cycle(chip, column, page_addr);
- break;
-
- case NAND_CMD_READID:
- host->send_cmd(host, command);
- mxc_do_addr_cycle(chip, column, page_addr);
- host->send_read_id(host);
- host->buf_start = 0;
- break;
-
- case NAND_CMD_PARAM:
- host->send_cmd(host, command);
- mxc_do_addr_cycle(chip, column, page_addr);
- host->send_read_param(host);
- host->buf_start = 0;
- break;
-
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- host->send_cmd(host, command);
- mxc_do_addr_cycle(chip, column, page_addr);
- break;
- }
-}
-
-/*
- * The generic flash bbt decriptors overlap with our ecc
- * hardware, so define some i.MX specific ones.
- */
-static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 0,
- .len = 4,
- .veroffs = 4,
- .maxblocks = 4,
- .pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 0,
- .len = 4,
- .veroffs = 4,
- .maxblocks = 4,
- .pattern = mirror_pattern,
-};
-
-static int __init mxcnd_probe_dt(struct imx_nand_host *host)
-{
- struct device_node *np = host->dev->device_node;
- int buswidth;
-
- if (!IS_ENABLED(CONFIG_OFDEVICE))
- return 1;
-
- if (!np)
- return 1;
-
- if (of_get_nand_ecc_mode(np) == NAND_ECC_HW)
- host->hw_ecc = 1;
-
- host->flash_bbt = of_get_nand_on_flash_bbt(np);
-
- buswidth = of_get_nand_bus_width(np);
- if (buswidth < 0)
- return buswidth;
-
- host->data_width = buswidth / 8;
-
- return 0;
-}
-
-/*
- * The i.MX NAND controller has the problem that it handles the
- * data in chunks of 512 bytes. It doesn't treat 2k NAND chips as
- * 2048 byte data + 64 OOB, but instead:
- *
- * 512b data + 16b OOB +
- * 512b data + 16b OOB +
- * 512b data + 16b OOB +
- * 512b data + 16b OOB
- *
- * This means that the factory provided bad block marker ends up
- * in the page data at offset 2000 instead of in the OOB data.
- *
- * To preserve the factory bad block information we take the following
- * strategy:
- *
- * - If the NAND driver detects that no flash BBT is present on 2k NAND
- * chips it will not create one because it would do so based on the wrong
- * BBM position
- * - This command is used to create a flash BBT then.
- *
- * From this point on we can forget about the BBMs and rely completely
- * on the flash BBT.
- *
- */
-static int checkbad(struct nand_chip *chip, loff_t ofs)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
- uint8_t buf[mtd->writesize + mtd->oobsize];
- struct mtd_oob_ops ops;
-
- ops.mode = MTD_OPS_RAW;
- ops.ooboffs = 0;
- ops.datbuf = buf;
- ops.len = mtd->writesize;
- ops.oobbuf = buf + mtd->writesize;
- ops.ooblen = mtd->oobsize;
-
- ret = mtd_read_oob(mtd, ofs, &ops);
- if (ret < 0)
- return ret;
-
- if (buf[2000] != 0xff)
- return 1;
-
- return 0;
-}
-
-static int imxnd_create_bbt(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int len, i, numblocks, ret;
- loff_t from = 0;
- uint8_t *bbt;
-
- len = mtd->size >> (chip->bbt_erase_shift + 2);
-
- /* Allocate memory (2bit per block) and clear the memory bad block table */
- bbt = kzalloc(len, GFP_KERNEL);
- if (!bbt)
- return -ENOMEM;
-
- numblocks = mtd->size >> (chip->bbt_erase_shift - 1);
-
- for (i = 0; i < numblocks;) {
- ret = checkbad(chip, from);
- if (ret < 0)
- goto out;
-
- if (ret) {
- bbt[i >> 3] |= 0x03 << (i & 0x6);
- dev_info(mtd->dev.parent, "Bad eraseblock %d at 0x%08x\n",
- i >> 1, (unsigned int)from);
- }
-
- i += 2;
- from += (1 << chip->bbt_erase_shift);
- }
-
- chip->bbt_td->options |= NAND_BBT_CREATE;
- chip->bbt_md->options |= NAND_BBT_CREATE;
-
- free(chip->bbt);
- chip->bbt = bbt;
-
- ret = nand_update_bbt(chip, 0);
- if (ret)
- return ret;
-
- ret = nand_create_bbt(chip);
- if (ret)
- return ret;
-
- ret = 0;
-out:
- free(bbt);
-
- return ret;
-}
-
-/*
- * This function is called during the driver binding process.
- *
- * @param pdev the device structure used to store device specific
- * information that is used by the suspend, resume and
- * remove functions
- *
- * @return The function always returns 0.
- */
-
-static int __init imxnd_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct nand_chip *this;
- struct mtd_info *mtd;
- struct imx_nand_host *host;
- struct nand_ecclayout *oob_smallpage, *oob_largepage, *oob_4kpage;
- int err = 0;
-
- /* Allocate memory for MTD device structure and private data */
- host = kzalloc(sizeof(struct imx_nand_host) + NAND_MAX_PAGESIZE +
- NAND_MAX_OOBSIZE, GFP_KERNEL);
- if (!host)
- return -ENOMEM;
-
- host->dev = dev;
-
- err = mxcnd_probe_dt(host);
- if (err < 0)
- goto escan;
-
- if (err > 0) {
- struct imx_nand_platform_data *pdata;
-
- pdata = dev->platform_data;
- host->flash_bbt = pdata->flash_bbt;
- host->data_width = pdata->width;
- host->hw_ecc = pdata->hw_ecc;
- }
-
- host->data_buf = (uint8_t *)(host + 1);
-
- /* No error check, not all SoCs provide a clk yet */
- host->clk = clk_get(dev, NULL);
-
- if (nfc_is_v1() || nfc_is_v21()) {
- host->send_cmd = send_cmd_v1_v2;
- host->send_addr = send_addr_v1_v2;
- host->send_page = send_page_v1_v2;
- host->send_read_id = send_read_id_v1_v2;
- host->send_read_param = send_read_param_v1_v2;
- host->get_dev_status = get_dev_status_v1_v2;
- host->check_int = check_int_v1_v2;
- }
-
- if (nfc_is_v21()) {
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- host->base = IOMEM(iores->start);
- host->main_area0 = host->base;
- host->regs = host->base + 0x1e00;
- host->spare0 = host->base + 0x1000;
- host->spare_len = 64;
- oob_smallpage = &nandv2_hw_eccoob_smallpage;
- oob_largepage = &nandv2_hw_eccoob_largepage;
- oob_4kpage = &nandv2_hw_eccoob_4k; /* FIXME : to check */
- host->preset = preset_v2;
- } else if (nfc_is_v1()) {
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- host->base = IOMEM(iores->start);
- host->main_area0 = host->base;
- host->regs = host->base + 0xe00;
- host->spare0 = host->base + 0x800;
- host->spare_len = 16;
- oob_smallpage = &nandv1_hw_eccoob_smallpage;
- oob_largepage = &nandv1_hw_eccoob_largepage;
- oob_4kpage = &nandv1_hw_eccoob_smallpage; /* FIXME : to check */
- host->preset = preset_v1;
- } else if (nfc_is_v3_2()) {
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- host->regs_ip = IOMEM(iores->start);
-
- iores = dev_request_mem_resource(dev, 1);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- host->base = IOMEM(iores->start);
- host->main_area0 = host->base;
-
- if (IS_ERR(host->regs_ip)) {
- dev_err(dev, "no second mem region\n");
- err = PTR_ERR(host->regs_ip);
- goto escan;
- }
-
- host->regs_axi = host->base + 0x1e00;
- host->spare0 = host->base + 0x1000;
- host->spare_len = 64;
- host->preset = preset_v3;
- host->send_cmd = send_cmd_v3;
- host->send_addr = send_addr_v3;
- host->send_page = send_page_v3;
- host->send_read_id = send_read_id_v3;
- host->send_read_param = send_read_param_v3;
- host->get_dev_status = get_dev_status_v3;
- host->check_int = check_int_v3;
- oob_smallpage = &nandv2_hw_eccoob_smallpage;
- oob_largepage = &nandv2_hw_eccoob_largepage;
- oob_4kpage = &nandv2_hw_eccoob_4k;
- } else {
- err = -EINVAL;
- goto escan;
- }
-
- /* structures must be linked */
- this = &host->nand;
- mtd = nand_to_mtd(this);
- mtd->dev.parent = dev;
- mtd->name = "imx_nand";
-
- /* 50 us command delay time */
- this->legacy.chip_delay = 5;
-
- this->priv = host;
- this->legacy.dev_ready = imx_nand_dev_ready;
- this->legacy.cmdfunc = imx_nand_command;
- this->legacy.select_chip = imx_nand_select_chip;
- this->legacy.read_byte = imx_nand_read_byte;
- this->legacy.read_word = imx_nand_read_word;
- this->legacy.write_buf = imx_nand_write_buf;
- this->legacy.read_buf = imx_nand_read_buf;
- this->ecc.write_page = imx_nand_write_page_ecc;
- this->ecc.write_page_raw = imx_nand_write_page_raw;
-
- if (host->hw_ecc) {
- this->ecc.calculate = imx_nand_calculate_ecc;
- if (nfc_is_v3())
- host->enable_hwecc = imx_nand_enable_hwecc_v3;
- else
- host->enable_hwecc = imx_nand_enable_hwecc_v1_v2;
- if (nfc_is_v1())
- host->correct = imx_nand_correct_data_v1;
- else
- host->correct = imx_nand_correct_data_v2_v3;
- this->ecc.mode = NAND_ECC_HW;
- this->ecc.size = 512;
- this->ecc.read_page_raw = imx_nand_read_page_raw;
- this->ecc.read_page = imx_nand_read_page;
- } else {
- this->ecc.size = 512;
- this->ecc.mode = NAND_ECC_SOFT;
- }
-
- mtd_set_ecclayout(mtd, oob_smallpage);
-
- /* NAND bus width determines access functions used by upper layer */
- if (host->data_width == 2) {
- this->options |= NAND_BUSWIDTH_16;
- mtd_set_ecclayout(mtd, &nandv1_hw_eccoob_smallpage);
- imx_nand_set_layout(0, 16);
- }
-
- if (host->flash_bbt) {
- this->bbt_td = &bbt_main_descr;
- this->bbt_md = &bbt_mirror_descr;
- /* update flash based bbt */
- this->bbt_options |= NAND_BBT_USE_FLASH;
- }
-
- /* first scan to find the device and get the page size */
- if (nand_scan_ident(this, 1, NULL)) {
- err = -ENXIO;
- goto escan;
- }
-
- /* Call preset again, with correct writesize this time */
- host->preset(this);
-
- imx_nand_set_layout(mtd->writesize, host->data_width == 2 ? 16 : 8);
-
- if (mtd->writesize >= 2048) {
- if (!host->flash_bbt)
- dev_warn(dev, "2k or 4k flash detected without flash_bbt. "
- "You will loose factory bad block markers!\n");
-
- if (mtd->writesize == 2048)
- mtd_set_ecclayout(mtd, oob_largepage);
- else
- mtd_set_ecclayout(mtd, oob_4kpage);
- host->pagesize_2k = 1;
- if (nfc_is_v21())
- writew(NFC_V2_SPAS_SPARESIZE(64), host->regs + NFC_V2_SPAS);
- } else {
- bbt_main_descr.options |= NAND_BBT_CREATE;
- bbt_mirror_descr.options |= NAND_BBT_CREATE;
-
- if (nfc_is_v21())
- writew(NFC_V2_SPAS_SPARESIZE(16), host->regs + NFC_V2_SPAS);
- }
-
- if (this->ecc.mode == NAND_ECC_HW)
- this->ecc.strength = host->eccsize;
-
- /* second phase scan */
- if (nand_scan_tail(this)) {
- err = -ENXIO;
- goto escan;
- }
-
- if (host->flash_bbt && this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) {
- dev_info(dev, "no BBT found. creating one\n");
- err = imxnd_create_bbt(this);
- if (err)
- dev_warn(dev, "Failed to create bbt: %s\n",
- strerror(-err));
- err = 0;
- }
-
- add_mtd_nand_device(mtd, "nand");
-
- dev->priv = host;
-
- return 0;
-
-escan:
- kfree(host);
-
- return err;
-
-}
-
-static __maybe_unused struct of_device_id imx_nand_compatible[] = {
- {
- .compatible = "fsl,imx21-nand",
- }, {
- .compatible = "fsl,imx25-nand",
- }, {
- .compatible = "fsl,imx27-nand",
- }, {
- .compatible = "fsl,imx51-nand",
- }, {
- .compatible = "fsl,imx53-nand",
- }, {
- /* sentinel */
- }
-};
-
-static struct driver_d imx_nand_driver = {
- .name = "imx_nand",
- .probe = imxnd_probe,
- .of_compatible = DRV_OF_COMPAT(imx_nand_compatible),
-};
-device_platform_driver(imx_nand_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("MXC NAND MTD driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c
deleted file mode 100644
index b0f16f1d62..0000000000
--- a/drivers/mtd/nand/nand_s3c24xx.c
+++ /dev/null
@@ -1,649 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* linux/drivers/mtd/nand/s3c2410.c
- *
- * Copyright (C) 2009 Juergen Beisert, Pengutronix
- *
- * Copyright © 2004-2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * Samsung S3C2410 NAND driver
- *
- */
-
-#include <config.h>
-#include <common.h>
-#include <driver.h>
-#include <malloc.h>
-#include <init.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand.h>
-#include <mach/s3c-generic.h>
-#include <mach/s3c-iomap.h>
-#include <mach/s3c24xx-nand.h>
-#include <io.h>
-#include <errno.h>
-#include <asm/sections.h>
-
-#ifdef CONFIG_S3C_NAND_BOOT
-# define __nand_boot_init __bare_init
-# ifndef BOARD_DEFAULT_NAND_TIMING
-# define BOARD_DEFAULT_NAND_TIMING 0x0737
-# endif
-#else
-# define __nand_boot_init
-#endif
-
-/**
- * Define this symbol for testing purpose. It will add a command to read an
- * image from the NAND like it the boot strap code will do.
- */
-#define CONFIG_NAND_S3C_BOOT_DEBUG
-
-/* NAND controller's register */
-
-#define NFCONF 0x00
-
-#ifdef CONFIG_CPU_S3C2410
-
-#define NFCMD 0x04
-#define NFADDR 0x08
-#define NFDATA 0x0c
-#define NFSTAT 0x10
-#define NFECC 0x14
-
-/* S3C2410 specific bits */
-#define NFSTAT_BUSY (1)
-#define NFCONF_nFCE (1 << 11)
-#define NFCONF_INITECC (1 << 12)
-#define NFCONF_EN (1 << 15)
-
-#endif /* CONFIG_CPU_S3C2410 */
-
-#ifdef CONFIG_CPU_S3C2440
-
-#define NFCONT 0x04
-#define NFCMD 0x08
-#define NFADDR 0x0C
-#define NFDATA 0x10
-#define NFSTAT 0x20
-#define NFECC 0x2C
-
-/* S3C2440 specific bits */
-#define NFSTAT_BUSY (1)
-#define NFCONT_nFCE (1 << 1)
-#define NFCONT_INITECC (1 << 4)
-#define NFCONT_EN (1)
-
-#endif /* CONFIG_CPU_S3C2440 */
-
-
-struct s3c24x0_nand_host {
- struct nand_chip nand;
- struct mtd_partition *parts;
- struct device_d *dev;
-
- void __iomem *base;
-};
-
-/**
- * oob placement block for use with hardware ecc generation on small page
- */
-static struct nand_ecclayout nand_hw_eccoob = {
- .eccbytes = 3,
- .eccpos = { 0, 1, 2},
- .oobfree = {
- {
- .offset = 8,
- .length = 8
- }
- }
-};
-
-/* - Functions shared between the boot strap code and the regular driver - */
-
-/**
- * Issue the specified command to the NAND device
- * @param[in] host Base address of the NAND controller
- * @param[in] cmd Command for NAND flash
- */
-static void __nand_boot_init send_cmd(void __iomem *host, uint8_t cmd)
-{
- writeb(cmd, host + NFCMD);
-}
-
-/**
- * Issue the specified address to the NAND device
- * @param[in] host Base address of the NAND controller
- * @param[in] addr Address for the NAND flash
- */
-static void __nand_boot_init send_addr(void __iomem *host, uint8_t addr)
-{
- writeb(addr, host + NFADDR);
-}
-
-/**
- * Enable the NAND flash access
- * @param[in] host Base address of the NAND controller
- */
-static void __nand_boot_init enable_cs(void __iomem *host)
-{
-#ifdef CONFIG_CPU_S3C2410
- writew(readw(host + NFCONF) & ~NFCONF_nFCE, host + NFCONF);
-#endif
-#ifdef CONFIG_CPU_S3C2440
- writew(readw(host + NFCONT) & ~NFCONT_nFCE, host + NFCONT);
-#endif
-}
-
-/**
- * Disable the NAND flash access
- * @param[in] host Base address of the NAND controller
- */
-static void __nand_boot_init disable_cs(void __iomem *host)
-{
-#ifdef CONFIG_CPU_S3C2410
- writew(readw(host + NFCONF) | NFCONF_nFCE, host + NFCONF);
-#endif
-#ifdef CONFIG_CPU_S3C2440
- writew(readw(host + NFCONT) | NFCONT_nFCE, host + NFCONT);
-#endif
-}
-
-/**
- * Enable the NAND flash controller
- * @param[in] host Base address of the NAND controller
- * @param[in] timing Timing to access the NAND memory
- */
-static void __nand_boot_init enable_nand_controller(void __iomem *host, uint32_t timing)
-{
-#ifdef CONFIG_CPU_S3C2410
- writew(timing + NFCONF_EN + NFCONF_nFCE, host + NFCONF);
-#endif
-#ifdef CONFIG_CPU_S3C2440
- writew(NFCONT_EN + NFCONT_nFCE, host + NFCONT);
- writew(timing, host + NFCONF);
-#endif
-}
-
-/**
- * Diable the NAND flash controller
- * @param[in] host Base address of the NAND controller
- */
-static void __nand_boot_init disable_nand_controller(void __iomem *host)
-{
-#ifdef CONFIG_CPU_S3C2410
- writew(NFCONF_nFCE, host + NFCONF);
-#endif
-#ifdef CONFIG_CPU_S3C2440
- writew(NFCONT_nFCE, host + NFCONT);
-#endif
-}
-
-/* ----------------------------------------------------------------------- */
-
-#ifdef CONFIG_CPU_S3C2440
-/**
- * Read one block of data from the NAND port
- * @param[in] mtd Instance data
- * @param[out] buf buffer to write data to
- * @param[in] len byte count
- *
- * This is a special block read variant for the S3C2440 CPU.
- */
-static void s3c2440_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
- readsl(host->base + NFDATA, buf, len >> 2);
-
- /* cleanup any fractional read */
- if (len & 3) {
- buf += len & ~3;
-
- for (; len & 3; len--)
- *buf++ = readb(host->base + NFDATA);
- }
-}
-
-/**
- * Write one block of data to the NAND port
- * @param[in] mtd Instance data
- * @param[out] buf buffer to read data from
- * @param[in] len byte count
- *
- * This is a special block write variant for the S3C2440 CPU.
- */
-static void s3c2440_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
- int len)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
- writesl(host->base + NFDATA, buf, len >> 2);
-
- /* cleanup any fractional write */
- if (len & 3) {
- buf += len & ~3;
-
- for (; len & 3; len--, buf++)
- writeb(*buf, host->base + NFDATA);
- }
-}
-#endif
-
-/**
- * Check the ECC and try to repair the data if possible
- * @param[in] mtd_info Not used
- * @param[inout] dat Pointer to the data buffer that might contain a bit error
- * @param[in] read_ecc ECC data from the OOB space
- * @param[in] calc_ecc ECC data calculated from the data
- * @return 0 no error, 1 repaired error, -1 no way...
- *
- * @note: This routine works always on a 24 bit ECC
- */
-static int s3c2410_nand_correct_data(struct nand_chip *chip, uint8_t *dat,
- uint8_t *read_ecc, uint8_t *calc_ecc)
-{
- unsigned int diff0, diff1, diff2;
- unsigned int bit, byte;
-
- diff0 = read_ecc[0] ^ calc_ecc[0];
- diff1 = read_ecc[1] ^ calc_ecc[1];
- diff2 = read_ecc[2] ^ calc_ecc[2];
-
- if (diff0 == 0 && diff1 == 0 && diff2 == 0)
- return 0; /* ECC is ok */
-
- /* sometimes people do not think about using the ECC, so check
- * to see if we have an 0xff,0xff,0xff read ECC and then ignore
- * the error, on the assumption that this is an un-eccd page.
- */
- if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff)
- return 0;
-
- /* Can we correct this ECC (ie, one row and column change).
- * Note, this is similar to the 256 error code on smartmedia */
-
- if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
- ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
- ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
- /* calculate the bit position of the error */
-
- bit = ((diff2 >> 3) & 1) |
- ((diff2 >> 4) & 2) |
- ((diff2 >> 5) & 4);
-
- /* calculate the byte position of the error */
-
- byte = ((diff2 << 7) & 0x100) |
- ((diff1 << 0) & 0x80) |
- ((diff1 << 1) & 0x40) |
- ((diff1 << 2) & 0x20) |
- ((diff1 << 3) & 0x10) |
- ((diff0 >> 4) & 0x08) |
- ((diff0 >> 3) & 0x04) |
- ((diff0 >> 2) & 0x02) |
- ((diff0 >> 1) & 0x01);
-
- dat[byte] ^= (1 << bit);
- return 1;
- }
-
- /* if there is only one bit difference in the ECC, then
- * one of only a row or column parity has changed, which
- * means the error is most probably in the ECC itself */
-
- diff0 |= (diff1 << 8);
- diff0 |= (diff2 << 16);
-
- if ((diff0 & ~(1<<fls(diff0))) == 0)
- return 1;
-
- return -1;
-}
-
-static void s3c2410_nand_enable_hwecc(struct nand_chip *chip, int mode)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
-#ifdef CONFIG_CPU_S3C2410
- writel(readl(host->base + NFCONF) | NFCONF_INITECC , host->base + NFCONF);
-#endif
-#ifdef CONFIG_CPU_S3C2440
- writel(readl(host->base + NFCONT) | NFCONT_INITECC , host->base + NFCONT);
-#endif
-}
-
-static int s3c2410_nand_calculate_ecc(struct nand_chip *chip, const uint8_t *dat, uint8_t *ecc_code)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
-#ifdef CONFIG_CPU_S3C2410
- ecc_code[0] = readb(host->base + NFECC);
- ecc_code[1] = readb(host->base + NFECC + 1);
- ecc_code[2] = readb(host->base + NFECC + 2);
-#endif
-#ifdef CONFIG_CPU_S3C2440
- unsigned long ecc = readl(host->base + NFECC);
-
- ecc_code[0] = ecc;
- ecc_code[1] = ecc >> 8;
- ecc_code[2] = ecc >> 16;
-#endif
- return 0;
-}
-
-static void s3c24x0_nand_select_chip(struct nand_chip *chip, int num)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
- if (num == -1)
- disable_cs(host->base);
- else
- enable_cs(host->base);
-}
-
-static int s3c24x0_nand_devready(struct nand_chip *chip)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
- return readw(host->base + NFSTAT) & NFSTAT_BUSY;
-}
-
-static void s3c24x0_nand_hwcontrol(struct nand_chip *chip, int cmd,
- unsigned int ctrl)
-{
- struct s3c24x0_nand_host *host = chip->priv;
-
- if (cmd == NAND_CMD_NONE)
- return;
- /*
- * If the CLE should be active, this call is a NAND command
- */
- if (ctrl & NAND_CLE)
- send_cmd(host->base, cmd);
- /*
- * If the ALE should be active, this call is a NAND address
- */
- if (ctrl & NAND_ALE)
- send_addr(host->base, cmd);
-}
-
-static int s3c24x0_nand_inithw(struct s3c24x0_nand_host *host)
-{
- struct s3c24x0_nand_platform_data *pdata = host->dev->platform_data;
- uint32_t tmp;
-
- /* reset the NAND controller */
- disable_nand_controller(host->base);
-
- if (pdata != NULL)
- tmp = pdata->nand_timing;
- else
- /* else slowest possible timing */
- tmp = CALC_NFCONF_TIMING(4, 8, 8);
-
- /* reenable the NAND controller */
- enable_nand_controller(host->base, tmp);
-
- return 0;
-}
-
-static int s3c24x0_nand_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct nand_chip *chip;
- struct s3c24x0_nand_platform_data *pdata = dev->platform_data;
- struct mtd_info *mtd;
- struct s3c24x0_nand_host *host;
- int ret;
-
- /* Allocate memory for MTD device structure and private data */
- host = kzalloc(sizeof(struct s3c24x0_nand_host), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
-
- host->dev = dev;
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- host->base = IOMEM(iores->start);
-
- /* structures must be linked */
- chip = &host->nand;
- mtd = nand_to_mtd(chip);
- mtd->dev.parent = dev;
-
- /* init the default settings */
-
- /* 50 us command delay time */
- chip->legacy.chip_delay = 50;
- chip->priv = host;
-
- chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W = host->base + NFDATA;
-
-#ifdef CONFIG_CPU_S3C2440
- chip->legacy.read_buf = s3c2440_nand_read_buf;
- chip->legacy.write_buf = s3c2440_nand_write_buf;
-#endif
- chip->legacy.cmd_ctrl = s3c24x0_nand_hwcontrol;
- chip->legacy.dev_ready = s3c24x0_nand_devready;
- chip->legacy.select_chip = s3c24x0_nand_select_chip;
-
- /* we are using the hardware ECC feature of this device */
- chip->ecc.calculate = s3c2410_nand_calculate_ecc;
- chip->ecc.correct = s3c2410_nand_correct_data;
- chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
-
- /*
- * Setup ECC handling in accordance to the kernel
- * - 1 times 512 bytes with 24 bit ECC for small page
- * - 8 times 256 bytes with 24 bit ECC each for large page
- */
- chip->ecc.mode = NAND_ECC_HW;
- chip->ecc.bytes = 3; /* always 24 bit ECC per turn */
- chip->ecc.strength = 1;
-
-#ifdef CONFIG_CPU_S3C2440
- if (readl(host->base) & 0x8) {
- /* large page (2048 bytes per page) */
- chip->ecc.size = 256;
- } else
-#endif
- {
- /* small page (512 bytes per page) */
- chip->ecc.size = 512;
- mtd_set_ecclayout(mtd, &nand_hw_eccoob);
- }
-
- if (pdata->flash_bbt) {
- /* use a flash based bbt */
- chip->bbt_options |= NAND_BBT_USE_FLASH;
- }
-
- ret = s3c24x0_nand_inithw(host);
- if (ret != 0)
- goto on_error;
-
- /* Scan to find existence of the device */
- ret = nand_scan(chip, 1);
- if (ret != 0) {
- ret = -ENXIO;
- goto on_error;
- }
-
- return add_mtd_nand_device(mtd, "nand");
-
-on_error:
- free(host);
- return ret;
-}
-
-static struct driver_d s3c24x0_nand_driver = {
- .name = "s3c24x0_nand",
- .probe = s3c24x0_nand_probe,
-};
-device_platform_driver(s3c24x0_nand_driver);
-
-#ifdef CONFIG_S3C_NAND_BOOT
-
-static void __nand_boot_init wait_for_completion(void __iomem *host)
-{
- while (!(readw(host + NFSTAT) & NFSTAT_BUSY))
- ;
-}
-
-/**
- * Convert a page offset into a page address for the NAND
- * @param host Where to write the address to
- * @param offs Page's offset in the NAND
- * @param ps Page size (512 or 2048)
- * @param c Address cycle count (3, 4 or 5)
- *
- * Uses the offset of the page to generate an page address into the NAND. This
- * differs when using a 512 byte or 2048 bytes per page NAND.
- * The column part of the page address to be generated is always forced to '0'.
- */
-static void __nand_boot_init nfc_addr(void __iomem *host, uint32_t offs,
- int ps, int c)
-{
- send_addr(host, 0); /* column part 1 */
-
- if (ps == 512) {
- send_addr(host, offs >> 9);
- send_addr(host, offs >> 17);
- if (c > 3)
- send_addr(host, offs >> 25);
- } else {
- send_addr(host, 0); /* column part 2 */
- send_addr(host, offs >> 11);
- send_addr(host, offs >> 19);
- if (c > 4)
- send_addr(host, offs >> 27);
- send_cmd(host, NAND_CMD_READSTART);
- }
-}
-
-/**
- * Load a sequential count of pages from the NAND into memory
- * @param[out] dest Pointer to target area (in SDRAM)
- * @param[in] size Bytes to read from NAND device
- * @param[in] page Start page to read from
- *
- * This function must be located in the first 4kiB of the barebox image
- * (guess why).
- */
-void __nand_boot_init s3c24x0_nand_load_image(void *dest, int size, int page)
-{
- void __iomem *host = (void __iomem *)S3C24X0_NAND_BASE;
- unsigned pagesize;
- int i, cycle;
-
- /*
- * Reenable the NFC and use the default (but slow) access
- * timing or the board specific setting if provided.
- */
- enable_nand_controller(host, BOARD_DEFAULT_NAND_TIMING);
-
- /* use the current NAND hardware configuration */
- switch (readl(S3C24X0_NAND_BASE) & 0xf) {
- case 0x6: /* 8 bit, 4 addr cycles, 512 bpp, normal NAND */
- pagesize = 512;
- cycle = 4;
- break;
- case 0xc: /* 8 bit, 4 addr cycles, 2048 bpp, advanced NAND */
- pagesize = 2048;
- cycle = 4;
- break;
- case 0xe: /* 8 bit, 5 addr cycles, 2048 bpp, advanced NAND */
- pagesize = 2048;
- cycle = 5;
- break;
- default:
- /* we cannot output an error message here :-( */
- disable_nand_controller(host);
- return;
- }
-
- enable_cs(host);
-
- /* Reset the NAND device */
- send_cmd(host, NAND_CMD_RESET);
- wait_for_completion(host);
- disable_cs(host);
-
- do {
- enable_cs(host);
- send_cmd(host, NAND_CMD_READ0);
- nfc_addr(host, page * pagesize, pagesize, cycle);
- wait_for_completion(host);
- /* copy one page (do *not* use readsb() here!)*/
- for (i = 0; i < pagesize; i++)
- writeb(readb(host + NFDATA), (void __iomem *)(dest + i));
- disable_cs(host);
-
- page++;
- dest += pagesize;
- size -= pagesize;
- } while (size >= 0);
-
- /* disable the controller again */
- disable_nand_controller(host);
-}
-
-void __nand_boot_init nand_boot(void)
-{
- void *dest = _text;
- int size = barebox_image_size;
- int page = 0;
-
- s3c24x0_nand_load_image(dest, size, page);
-}
-#ifdef CONFIG_NAND_S3C_BOOT_DEBUG
-#include <command.h>
-
-static int do_nand_boot_test(int argc, char *argv[])
-{
- void *dest;
- int size;
-
- if (argc < 3)
- return COMMAND_ERROR_USAGE;
-
- dest = (void *)strtoul_suffix(argv[1], NULL, 0);
- size = strtoul_suffix(argv[2], NULL, 0);
-
- s3c24x0_nand_load_image(dest, size, 0);
-
- /* re-enable the controller again, as this was a test only */
- enable_nand_controller((void *)S3C24X0_NAND_BASE,
- BOARD_DEFAULT_NAND_TIMING);
-
- return 0;
-}
-
-BAREBOX_CMD_START(nand_boot_test)
- .cmd = do_nand_boot_test,
- BAREBOX_CMD_DESC("load an image from NAND")
- BAREBOX_CMD_OPTS("DEST SIZE")
- BAREBOX_CMD_GROUP(CMD_GRP_BOOT)
-BAREBOX_CMD_END
-#endif
-
-#endif /* CONFIG_S3C_NAND_BOOT */
-
-/**
- * @file
- * @brief Support for various kinds of NAND devices
- *
- * ECC handling in this driver (in accordance to the current 2.6.38 kernel):
- * - for small page NANDs it generates 3 ECC bytes out of 512 data bytes
- * - for large page NANDs it generates 24 ECC bytes out of 2048 data bytes
- *
- * As small page NANDs are using 48 bits ECC per default, this driver uses a
- * local OOB layout description, to shrink it down to 24 bits. This is a bad
- * idea, but we cannot change it here, as the kernel is using this layout.
- *
- * For large page NANDs this driver uses the default layout, as the kernel does.
- */
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
new file mode 100644
index 0000000000..cf65a90db1
--- /dev/null
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -0,0 +1,175 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MTD_RAW_NAND
+ bool
+
+menuconfig NAND
+ bool "Raw/Parallel NAND Device Support"
+ select MTD_NAND_CORE
+ select MTD_NAND_ECC
+ select MTD_RAW_NAND
+ help
+ This enables support for accessing all type of raw/parallel
+ NAND flash devices. For further information see
+ <http://www.linux-mtd.infradead.org/doc/nand.html>.
+
+if MTD_RAW_NAND
+
+config MTD_NAND_ECC_SOFT
+ bool
+ prompt "Support software ecc"
+
+config NAND_ECC_HW_SYNDROME
+ bool
+ prompt "Support syndrome hardware ecc controllers"
+
+config NAND_ALLOW_ERASE_BAD
+ bool
+ depends on MTD_WRITE
+ prompt "Add device parameter to allow erasing bad blocks"
+ help
+ This adds a 'erasebad' device parameter to nand devices. When set
+ to '1' it will be allowed to erase bad blocks. This is a potientially
+ dangerous operation, so if unsure say no to this option.
+
+comment "Raw/parallel NAND flash controllers"
+
+config NAND_IMX
+ bool
+ prompt "i.MX21 to 53 NAND driver aka 'mxc', for NFC"
+ depends on ARCH_IMX
+ help
+ Support for NAND flash on Freescale/NXP i.MX devices. This is for the
+ "MXC" series: i.MX21/25/27/31/35/51/53.
+
+ This is not for the "MXS" series i.MX processors (23 & 28), or i.MX6
+ and later, which use the GPMI NAND controller from the MXS series.
+ See the i.MX 'mxs' driver for those chips.
+
+config NAND_FSL_IFC
+ bool
+ prompt "FSL IFC NAND driver"
+ depends on ARCH_LAYERSCAPE
+ help
+ Freescale IFC NAND driver for various chips.
+
+config NAND_MXS
+ bool
+ select STMP_DEVICE
+ prompt "i.MX23/28 & 6+ NAND driver aka 'mxs', for GPMI"
+ depends on MXS_APBH_DMA
+ help
+ Support for NAND flash on Freescale/NXP i.MX devices. This is for the
+ "MXS" series: i.MX23/28 and all i.MX6 and later SoCs.
+
+ This is not for the "MXC" series of i.MX processors in the i.MX21 to
+ i.MX53 range. See the i.MX "mxc" driver for those chips.
+
+config NAND_OMAP_GPMC
+ tristate "NAND Flash Support for GPMC based OMAP platforms"
+ depends on OMAP_GPMC
+ depends on BUS_OMAP_GPMC
+ help
+ Support for NAND flash using GPMC. GPMC is a common memory
+ interface found on Texas Instrument's OMAP platforms
+
+config MTD_NAND_OMAP_ELM
+ bool "Support for ELM (Error Location Module) on OMAP platforms"
+ depends on NAND_OMAP_GPMC || COMPILE_TEST
+ help
+ This config enables the ELM hardware engine, which can be used to
+ locate and correct errors when using BCH ECC scheme. This offloads
+ the cpu from doing ECC error searching and correction. However some
+ legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
+ so this is optional for them.
+
+config NAND_ORION
+ bool
+ prompt "Marvell Orion NAND driver"
+ depends on (ARM && !CPU_32v4T) && (ARCH_KIRKWOOD || COMPILE_TEST)
+ help
+ Support for the Orion NAND controller, present in Kirkwood SoCs.
+
+config NAND_MRVL_NFC
+ bool
+ prompt "Marvell PXA3xx NAND driver"
+ depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_PXA3XX || COMPILE_TEST
+ help
+ Support for the PXA3xx NAND controller, present in Armada 370/XP and
+ PXA3xx SoCs.
+
+config NAND_STM32
+ bool "Support for NAND controller on STM32MP SoCs"
+ depends on ARCH_STM32MP || COMPILE_TEST
+ select STM32_FMC2_EBI if ARCH_STM32MP
+ select RESET_CONTROLLER if ARCH_STM32MP
+ select RESET_SIMPLE if ARCH_STM32MP
+ help
+ Enables support for NAND Flash chips on SoCs containing the FMC2
+ NAND controller. This controller is found on STM32MP SoCs.
+ The controller supports a maximum 8k page size and supports
+ a maximum 8-bit correction error per sector of 512 bytes.
+
+config NAND_ATMEL
+ bool
+ prompt "Atmel (AT91SAM9xxx) NAND driver"
+ select GENERIC_ALLOCATOR if OFDEVICE
+ depends on ARCH_AT91 || (OFDEVICE && COMPILE_TEST)
+
+config NAND_ATMEL_LEGACY
+ def_bool !AT91_MULTI_BOARDS || SOC_AT91SAM9
+ depends on NAND_ATMEL
+ help
+ Select legacy driver for non-DT-enabled platforms
+ and for the deprecated non-EBI binding.
+
+ The deprecated binding is currently the only one
+ support for AT91SAM9.
+
+config NAND_ATMEL_PMECC
+ bool
+ prompt "PMECC support"
+ depends on NAND_ATMEL_LEGACY
+ help
+ Support for PMECC present on the SoC sam9x5 and sam9n12
+
+config MTD_NAND_ECC_SW_HAMMING_SMC
+ bool "NAND ECC Smart Media byte order"
+ default n
+ help
+ Software ECC according to the Smart Media Specification.
+ The original Linux implementation had byte 0 and 1 swapped.
+
+config MTD_NAND_NOMADIK
+ tristate "ST Nomadik 8815 NAND support"
+ depends on ARCH_NOMADIK
+ help
+ Driver for the NAND flash controller on the Nomadik, with ECC.
+
+config MTD_NAND_DENALI
+ tristate "Support Denali NAND controller"
+ depends on HAS_DMA
+ help
+ Enable support for the Denali NAND controller. This should be
+ combined with either the PCI or platform drivers to provide device
+ registration.
+
+config MTD_NAND_DENALI_DT
+ tristate "Support Denali NAND controller as a DT device"
+ depends on HAVE_CLK && MTD_NAND_DENALI
+ help
+ Enable the driver for NAND flash on platforms using a Denali NAND
+ controller as a DT device.
+
+if MTD_NAND_DENALI
+
+config MTD_NAND_DENALI_TIMING_MODE
+ int "Overrides default ONFI timing mode."
+ default -1
+ range -1 5
+ help
+ -1 indicates use default timings
+
+endif
+
+endif
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
new file mode 100644
index 0000000000..38c7cc809d
--- /dev/null
+++ b/drivers/mtd/nand/raw/Makefile
@@ -0,0 +1,23 @@
+# Generic NAND options
+
+obj-$(CONFIG_MTD_RAW_NAND) += nand_ecc.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_ids.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_base.o nand_timings.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_legacy.o nand_onfi.o nand_amd.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_esmt.o nand_hynix.o nand_macronix.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_micron.o nand_samsung.o nand_toshiba.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_jedec.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand_bbt.o
+
+obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
+obj-$(CONFIG_NAND_IMX) += mxc_nand.o
+obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o
+obj-$(CONFIG_MTD_NAND_OMAP_ELM) += omap_elm.o
+obj-$(CONFIG_NAND_ORION) += nand_orion.o
+obj-$(CONFIG_NAND_STM32) += stm32_fmc2_nand.o
+obj-$(CONFIG_NAND_MRVL_NFC) += nand_mrvl_nfc.o
+obj-$(CONFIG_NAND_ATMEL) += atmel/
+obj-$(CONFIG_NAND_MXS) += nand_mxs.o
+obj-$(CONFIG_MTD_NAND_DENALI) += nand_denali.o
+obj-$(CONFIG_MTD_NAND_DENALI_DT) += nand_denali_dt.o
+obj-$(CONFIG_NAND_FSL_IFC) += nand_fsl_ifc.o
diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile
new file mode 100644
index 0000000000..0f739c3f31
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_OFDEVICE) += nand-controller.o pmecc.o
+obj-$(CONFIG_NAND_ATMEL_LEGACY) += legacy.o
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/raw/atmel/atmel_nand_ecc.h
index c7864d96dd..c7864d96dd 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/raw/atmel/atmel_nand_ecc.h
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/raw/atmel/legacy.c
index 6a54add93b..5e2fd540ea 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/raw/atmel/legacy.c
@@ -33,7 +33,7 @@
#include <linux/err.h>
#include <io.h>
-#include <mach/board.h>
+#include <mach/at91/board.h>
#include <errno.h>
@@ -75,7 +75,7 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
void __iomem *io_base;
struct atmel_nand_data *board;
- struct device_d *dev;
+ struct device *dev;
void __iomem *ecc;
int pmecc_bytes_per_sector;
@@ -835,8 +835,8 @@ static int pmecc_build_galois_table(unsigned int mm, int16_t *index_of,
return 0;
}
-static int __init atmel_pmecc_nand_init_params(struct device_d *dev,
- struct atmel_nand_host *host)
+static int __init atmel_pmecc_nand_init_params(struct device *dev,
+ struct atmel_nand_host *host)
{
struct resource *iores;
struct nand_chip *nand_chip = &host->nand_chip;
@@ -909,7 +909,7 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev,
dev_err(host->dev, "No room for ECC bytes\n");
return -EINVAL;
}
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
break;
case 512:
case 1024:
@@ -919,7 +919,8 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev,
default:
/* page size not handled by HW ECC */
/* switching back to soft ECC */
- nand_chip->ecc.mode = NAND_ECC_SOFT;
+ nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ nand_chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
return 0;
}
@@ -1195,7 +1196,7 @@ static int atmel_nand_of_init(struct atmel_nand_host *host, struct device_node *
return 0;
}
-static int atmel_hw_nand_init_params(struct device_d *dev,
+static int atmel_hw_nand_init_params(struct device *dev,
struct atmel_nand_host *host)
{
struct resource *iores;
@@ -1235,7 +1236,7 @@ static int atmel_hw_nand_init_params(struct device_d *dev,
default:
/* page size not handled by HW ECC */
/* switching back to soft ECC */
- nand_chip->ecc.mode = NAND_ECC_SOFT;
+ nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
return 0;
}
@@ -1253,7 +1254,7 @@ static int atmel_hw_nand_init_params(struct device_d *dev,
/*
* Probe for the NAND device.
*/
-static int __init atmel_nand_probe(struct device_d *dev)
+static int __init atmel_nand_probe(struct device *dev)
{
struct resource *iores;
struct atmel_nand_data *pdata = NULL;
@@ -1281,8 +1282,8 @@ static int __init atmel_nand_probe(struct device_d *dev)
host->board = pdata;
host->dev = dev;
- if (dev->device_node) {
- res = atmel_nand_of_init(host, dev->device_node);
+ if (dev->of_node) {
+ res = atmel_nand_of_init(host, dev->of_node);
if (res)
goto err_no_card;
} else {
@@ -1334,21 +1335,18 @@ static int __init atmel_nand_probe(struct device_d *dev)
}
}
- nand_chip->ecc.mode = pdata->ecc_mode;
nand_chip->ecc.strength = pdata->ecc_strength ? : 1;
- nand_chip->ecc.size = 1 << pdata->ecc_size_shift ? : 512;
+ nand_chip->ecc.size = 1 << (pdata->ecc_size_shift ? : 9);
- if (pdata->ecc_mode == NAND_ECC_HW) {
- nand_chip->ecc.mode = NAND_ECC_HW;
+ if (pdata->ecc_mode == NAND_ECC_SOFT) {
+ nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ nand_chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
+ } else {
+ nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
}
nand_chip->legacy.chip_delay = 40; /* 40us command delay time */
- if (IS_ENABLED(CONFIG_NAND_ECC_BCH) &&
- pdata->ecc_mode == NAND_ECC_SOFT_BCH) {
- nand_chip->ecc.mode = NAND_ECC_SOFT_BCH;
- }
-
if (host->board->bus_width_16) { /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->legacy.read_buf = atmel_read_buf16;
@@ -1397,7 +1395,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
host->ecc_code = xmalloc(mtd->oobsize);
- if (nand_chip->ecc.mode == NAND_ECC_HW) {
+ if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
if (IS_ENABLED(CONFIG_NAND_ATMEL_PMECC) && pdata->has_pmecc)
res = atmel_pmecc_nand_init_params(dev, host);
else
@@ -1433,7 +1431,7 @@ static struct of_device_id atmel_nand_dt_ids[] = {
{ /* sentinel */ }
};
-static struct driver_d atmel_nand_driver = {
+static struct driver atmel_nand_driver = {
.name = "atmel_nand",
.probe = atmel_nand_probe,
.of_compatible = DRV_OF_COMPAT(atmel_nand_dt_ids),
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
new file mode 100644
index 0000000000..5188a11cbe
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -0,0 +1,2049 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 ATMEL
+ * Copyright 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ * Copyright 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
+ * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
+ * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * Copyright 2012 ATMEL, Hong Xu
+ *
+ * Add Nand Flash Controller support for SAMA5 SoC
+ * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
+ *
+ * A few words about the naming convention in this file. This convention
+ * applies to structure and function names.
+ *
+ * Prefixes:
+ *
+ * - atmel_nand_: all generic structures/functions
+ * - atmel_smc_nand_: all structures/functions specific to the SMC interface
+ * (at91sam9 and avr32 SoCs)
+ * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface
+ * (sama5 SoCs and later)
+ * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block
+ * that is available in the HSMC block
+ * - <soc>_nand_: all SoC specific structures/functions
+ */
+
+#include <linux/clk.h>
+#include <linux/genalloc.h>
+#include <linux/gpio/consumer.h>
+#include <mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <module.h>
+#include <linux/mtd/rawnand.h>
+#include <of_address.h>
+#include <of.h>
+#include <of_device.h>
+#include <linux/iopoll.h>
+#include <linux/regmap.h>
+#include <soc/at91/atmel-sfr.h>
+
+#include "pmecc.h"
+
+#define ATMEL_HSMC_NFC_CFG 0x0
+#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24)
+#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24)
+#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20))
+#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16)
+#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13)
+#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12)
+#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9)
+#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8)
+#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0)
+#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1)
+
+#define ATMEL_HSMC_NFC_CTRL 0x4
+#define ATMEL_HSMC_NFC_CTRL_EN BIT(0)
+#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1)
+
+#define ATMEL_HSMC_NFC_SR 0x8
+#define ATMEL_HSMC_NFC_IER 0xc
+#define ATMEL_HSMC_NFC_IDR 0x10
+#define ATMEL_HSMC_NFC_IMR 0x14
+#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1)
+#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4)
+#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5)
+#define ATMEL_HSMC_NFC_SR_BUSY BIT(8)
+#define ATMEL_HSMC_NFC_SR_WR BIT(11)
+#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12)
+#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16)
+#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17)
+#define ATMEL_HSMC_NFC_SR_DTOE BIT(20)
+#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21)
+#define ATMEL_HSMC_NFC_SR_AWB BIT(22)
+#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23)
+#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \
+ ATMEL_HSMC_NFC_SR_UNDEF | \
+ ATMEL_HSMC_NFC_SR_AWB | \
+ ATMEL_HSMC_NFC_SR_NFCASE)
+#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24)
+
+#define ATMEL_HSMC_NFC_ADDR 0x18
+#define ATMEL_HSMC_NFC_BANK 0x1c
+
+#define ATMEL_NFC_MAX_RB_ID 7
+
+#define ATMEL_NFC_SRAM_SIZE 0x2400
+
+#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2))
+#define ATMEL_NFC_VCMD2 BIT(18)
+#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19)
+#define ATMEL_NFC_CSID(cs) ((cs) << 22)
+#define ATMEL_NFC_DATAEN BIT(25)
+#define ATMEL_NFC_NFCWR BIT(26)
+
+#define ATMEL_NFC_MAX_ADDR_CYCLES 5
+
+#define ATMEL_NAND_ALE_OFFSET BIT(21)
+#define ATMEL_NAND_CLE_OFFSET BIT(22)
+
+#define DEFAULT_TIMEOUT_MS 1000
+
+enum atmel_nand_rb_type {
+ ATMEL_NAND_NO_RB,
+ ATMEL_NAND_NATIVE_RB,
+ ATMEL_NAND_GPIO_RB,
+};
+
+struct atmel_nand_rb {
+ enum atmel_nand_rb_type type;
+ union {
+ struct gpio_desc *gpio;
+ int id;
+ };
+};
+
+struct atmel_nand_cs {
+ int id;
+ struct atmel_nand_rb rb;
+ struct gpio_desc *csgpio;
+ struct {
+ void __iomem *virt;
+ } io;
+
+ struct atmel_smc_cs_conf smcconf;
+};
+
+struct atmel_nand {
+ struct list_head node;
+ struct device *dev;
+ struct nand_chip base;
+ struct atmel_nand_cs *activecs;
+ struct atmel_pmecc_user *pmecc;
+ struct gpio_desc *cdgpio;
+ int numcs;
+ struct atmel_nand_cs cs[];
+};
+
+static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip)
+{
+ return container_of(chip, struct atmel_nand, base);
+}
+
+enum atmel_nfc_data_xfer {
+ ATMEL_NFC_NO_DATA,
+ ATMEL_NFC_READ_DATA,
+ ATMEL_NFC_WRITE_DATA,
+};
+
+struct atmel_nfc_op {
+ u8 cs;
+ u8 ncmds;
+ u8 cmds[2];
+ u8 naddrs;
+ u8 addrs[5];
+ enum atmel_nfc_data_xfer data;
+ u32 wait;
+ u32 errors;
+};
+
+struct atmel_nand_controller;
+struct atmel_nand_controller_caps;
+
+struct atmel_nand_controller_ops {
+ int (*probe)(struct device *dev,
+ const struct atmel_nand_controller_caps *caps);
+ void (*nand_init)(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand);
+ int (*ecc_init)(struct nand_chip *chip);
+ int (*setup_interface)(struct atmel_nand *nand, int csline,
+ const struct nand_interface_config *conf);
+ int (*exec_op)(struct atmel_nand *nand,
+ const struct nand_operation *op, bool check_only);
+};
+
+struct atmel_nand_controller_caps {
+ u32 ale_offs;
+ u32 cle_offs;
+ const char *ebi_csa_regmap_name;
+ const struct atmel_nand_controller_ops *ops;
+};
+
+struct atmel_nand_controller {
+ struct nand_controller base;
+ const struct atmel_nand_controller_caps *caps;
+ struct device *dev;
+ struct regmap *smc;
+ struct atmel_pmecc *pmecc;
+ struct list_head chips;
+ struct clk *mck;
+};
+
+static inline struct atmel_nand_controller *
+to_nand_controller(struct nand_controller *ctl)
+{
+ return container_of(ctl, struct atmel_nand_controller, base);
+}
+
+struct atmel_smc_nand_ebi_csa_cfg {
+ u32 offs;
+ u32 nfd0_on_d16;
+};
+
+struct atmel_smc_nand_controller {
+ struct atmel_nand_controller base;
+ struct regmap *ebi_csa_regmap;
+ struct atmel_smc_nand_ebi_csa_cfg *ebi_csa;
+};
+
+static inline struct atmel_smc_nand_controller *
+to_smc_nand_controller(struct nand_controller *ctl)
+{
+ return container_of(to_nand_controller(ctl),
+ struct atmel_smc_nand_controller, base);
+}
+
+struct atmel_hsmc_nand_controller {
+ struct atmel_nand_controller base;
+ struct {
+ struct gen_pool *pool;
+ void __iomem *virt;
+ } sram;
+ const struct atmel_hsmc_reg_layout *hsmc_layout;
+ struct regmap *io;
+ struct atmel_nfc_op op;
+ u32 cfg;
+};
+
+static inline struct atmel_hsmc_nand_controller *
+to_hsmc_nand_controller(struct nand_controller *ctl)
+{
+ return container_of(to_nand_controller(ctl),
+ struct atmel_hsmc_nand_controller, base);
+}
+
+static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status)
+{
+ op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS;
+ op->wait ^= status & op->wait;
+
+ return !op->wait || op->errors;
+}
+
+static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc,
+ unsigned int timeout_ms)
+{
+ u32 status;
+ int ret;
+
+ if (!timeout_ms)
+ timeout_ms = DEFAULT_TIMEOUT_MS;
+
+
+ ret = regmap_read_poll_timeout(nc->base.smc,
+ ATMEL_HSMC_NFC_SR, status,
+ atmel_nfc_op_done(&nc->op,
+ status),
+ timeout_ms * 1000);
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) {
+ dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n");
+ ret = -ETIMEDOUT;
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) {
+ dev_err(nc->base.dev, "Access to an undefined area\n");
+ ret = -EIO;
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) {
+ dev_err(nc->base.dev, "Access while busy\n");
+ ret = -EIO;
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) {
+ dev_err(nc->base.dev, "Wrong access size\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc)
+{
+ u8 *addrs = nc->op.addrs;
+ unsigned int op = 0;
+ u32 addr, val;
+ int i, ret;
+
+ nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE;
+
+ for (i = 0; i < nc->op.ncmds; i++)
+ op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]);
+
+ if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++);
+
+ op |= ATMEL_NFC_CSID(nc->op.cs) |
+ ATMEL_NFC_ACYCLE(nc->op.naddrs);
+
+ if (nc->op.ncmds > 1)
+ op |= ATMEL_NFC_VCMD2;
+
+ addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) |
+ (addrs[3] << 24);
+
+ if (nc->op.data != ATMEL_NFC_NO_DATA) {
+ op |= ATMEL_NFC_DATAEN;
+ nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE;
+
+ if (nc->op.data == ATMEL_NFC_WRITE_DATA)
+ op |= ATMEL_NFC_NFCWR;
+ }
+
+ /* Clear all flags. */
+ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val);
+
+ /* Send the command. */
+ regmap_write(nc->io, op, addr);
+
+ ret = atmel_nfc_wait(nc, 0);
+ if (ret)
+ dev_err(nc->base.dev,
+ "Failed to send NAND command (err = %d)!",
+ ret);
+
+ /* Reset the op state. */
+ memset(&nc->op, 0, sizeof(nc->op));
+
+ return ret;
+}
+
+static void atmel_nand_data_in(struct atmel_nand *nand, void *buf,
+ unsigned int len, bool force_8bit)
+{
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit)
+ ioread16_rep(nand->activecs->io.virt, buf, len / 2);
+ else
+ ioread8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static void atmel_nand_data_out(struct atmel_nand *nand, const void *buf,
+ unsigned int len, bool force_8bit)
+{
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit)
+ iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
+ else
+ iowrite8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static int atmel_nand_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms)
+{
+ if (nand->activecs->rb.type == ATMEL_NAND_NO_RB)
+ return nand_soft_waitrdy(&nand->base, timeout_ms);
+
+ return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio,
+ timeout_ms);
+}
+
+static int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand,
+ unsigned int timeout_ms)
+{
+ struct atmel_hsmc_nand_controller *nc;
+ u32 status, mask;
+
+ if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB)
+ return atmel_nand_waitrdy(nand, timeout_ms);
+
+ nc = to_hsmc_nand_controller(nand->base.controller);
+ mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
+ return regmap_read_poll_timeout(nc->base.smc, ATMEL_HSMC_NFC_SR,
+ status, status & mask,
+ timeout_ms * 1000);
+}
+
+static void atmel_nand_select_target(struct atmel_nand *nand,
+ unsigned int cs)
+{
+ nand->activecs = &nand->cs[cs];
+}
+
+static void atmel_hsmc_nand_select_target(struct atmel_nand *nand,
+ unsigned int cs)
+{
+ struct mtd_info *mtd = nand_to_mtd(&nand->base);
+ struct atmel_hsmc_nand_controller *nc;
+ u32 cfg = ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
+ ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
+ ATMEL_HSMC_NFC_CFG_RSPARE;
+
+ nand->activecs = &nand->cs[cs];
+ nc = to_hsmc_nand_controller(nand->base.controller);
+ if (nc->cfg == cfg)
+ return;
+
+ regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+ ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
+ ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
+ ATMEL_HSMC_NFC_CFG_RSPARE |
+ ATMEL_HSMC_NFC_CFG_WSPARE,
+ cfg);
+ nc->cfg = cfg;
+}
+
+static int atmel_smc_nand_exec_instr(struct atmel_nand *nand,
+ const struct nand_op_instr *instr)
+{
+ struct atmel_nand_controller *nc;
+ unsigned int i;
+
+ nc = to_nand_controller(nand->base.controller);
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ writeb(instr->ctx.cmd.opcode,
+ nand->activecs->io.virt + nc->caps->cle_offs);
+ return 0;
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++)
+ writeb(instr->ctx.addr.addrs[i],
+ nand->activecs->io.virt + nc->caps->ale_offs);
+ return 0;
+ case NAND_OP_DATA_IN_INSTR:
+ atmel_nand_data_in(nand, instr->ctx.data.buf.in,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ return 0;
+ case NAND_OP_DATA_OUT_INSTR:
+ atmel_nand_data_out(nand, instr->ctx.data.buf.out,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ return 0;
+ case NAND_OP_WAITRDY_INSTR:
+ return atmel_nand_waitrdy(nand,
+ instr->ctx.waitrdy.timeout_ms);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int atmel_smc_nand_exec_op(struct atmel_nand *nand,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ unsigned int i;
+ int ret = 0;
+
+ if (check_only)
+ return 0;
+
+ atmel_nand_select_target(nand, op->cs);
+ gpiod_set_value(nand->activecs->csgpio, 0);
+ for (i = 0; i < op->ninstrs; i++) {
+ ret = atmel_smc_nand_exec_instr(nand, &op->instrs[i]);
+ if (ret)
+ break;
+ }
+ gpiod_set_value(nand->activecs->csgpio, 1);
+
+ return ret;
+}
+
+static int atmel_hsmc_exec_cmd_addr(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ unsigned int i, j;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ nc->op.cs = nand->activecs->id;
+ for (i = 0; i < subop->ninstrs; i++) {
+ const struct nand_op_instr *instr = &subop->instrs[i];
+
+ if (instr->type == NAND_OP_CMD_INSTR) {
+ nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode;
+ continue;
+ }
+
+ for (j = nand_subop_get_addr_start_off(subop, i);
+ j < nand_subop_get_num_addr_cyc(subop, i); j++) {
+ nc->op.addrs[nc->op.naddrs] = instr->ctx.addr.addrs[j];
+ nc->op.naddrs++;
+ }
+ }
+
+ return atmel_nfc_exec_op(nc);
+}
+
+static int atmel_hsmc_exec_rw(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ const struct nand_op_instr *instr = subop->instrs;
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ if (instr->type == NAND_OP_DATA_IN_INSTR)
+ atmel_nand_data_in(nand, instr->ctx.data.buf.in,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ else
+ atmel_nand_data_out(nand, instr->ctx.data.buf.out,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+
+ return 0;
+}
+
+static int atmel_hsmc_exec_waitrdy(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ const struct nand_op_instr *instr = subop->instrs;
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ return atmel_hsmc_nand_waitrdy(nand, instr->ctx.waitrdy.timeout_ms);
+}
+
+static const struct nand_op_parser atmel_hsmc_op_parser = NAND_OP_PARSER(
+ NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_cmd_addr,
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true)),
+ NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw,
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 0)),
+ NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw,
+ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 0)),
+ NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_waitrdy,
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+);
+
+static int atmel_hsmc_nand_exec_op(struct atmel_nand *nand,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ int ret;
+
+ if (check_only)
+ return nand_op_parser_exec_op(&nand->base,
+ &atmel_hsmc_op_parser, op, true);
+
+ atmel_hsmc_nand_select_target(nand, op->cs);
+ ret = nand_op_parser_exec_op(&nand->base, &atmel_hsmc_op_parser, op,
+ false);
+
+ return ret;
+}
+
+static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
+ bool oob_required)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_hsmc_nand_controller *nc;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ /* Falling back to CPU copy. */
+ memcpy_toio(nc->sram.virt, buf, mtd->writesize);
+
+ if (oob_required)
+ memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
+ mtd->oobsize);
+}
+
+static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
+ bool oob_required)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_hsmc_nand_controller *nc;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
+
+ if (oob_required)
+ memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,
+ mtd->oobsize);
+}
+
+static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_hsmc_nand_controller *nc;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ if (column >= 0) {
+ nc->op.addrs[nc->op.naddrs++] = column;
+
+ /*
+ * 2 address cycles for the column offset on large page NANDs.
+ */
+ if (mtd->writesize > 512)
+ nc->op.addrs[nc->op.naddrs++] = column >> 8;
+ }
+
+ if (page >= 0) {
+ nc->op.addrs[nc->op.naddrs++] = page;
+ nc->op.addrs[nc->op.naddrs++] = page >> 8;
+
+ if (chip->options & NAND_ROW_ADDR_3)
+ nc->op.addrs[nc->op.naddrs++] = page >> 16;
+ }
+}
+
+static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (raw)
+ return 0;
+
+ ret = atmel_pmecc_enable(nand->pmecc, op);
+ if (ret)
+ dev_err(nc->dev,
+ "Failed to enable ECC engine (err = %d)\n", ret);
+
+ return ret;
+}
+
+static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ if (!raw)
+ atmel_pmecc_disable(nand->pmecc);
+}
+
+static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_controller *nc;
+ struct mtd_oob_region oobregion;
+ void *eccbuf;
+ int ret, i;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (raw)
+ return 0;
+
+ ret = atmel_pmecc_wait_rdy(nand->pmecc);
+ if (ret) {
+ dev_err(nc->dev,
+ "Failed to transfer NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ mtd_ooblayout_ecc(mtd, 0, &oobregion);
+ eccbuf = chip->oob_poi + oobregion.offset;
+
+ for (i = 0; i < chip->ecc.steps; i++) {
+ atmel_pmecc_get_generated_eccbytes(nand->pmecc, i,
+ eccbuf);
+ eccbuf += chip->ecc.bytes;
+ }
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
+ bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_controller *nc;
+ struct mtd_oob_region oobregion;
+ int ret, i, max_bitflips = 0;
+ void *databuf, *eccbuf;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (raw)
+ return 0;
+
+ ret = atmel_pmecc_wait_rdy(nand->pmecc);
+ if (ret) {
+ dev_err(nc->dev,
+ "Failed to read NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ mtd_ooblayout_ecc(mtd, 0, &oobregion);
+ eccbuf = chip->oob_poi + oobregion.offset;
+ databuf = buf;
+
+ for (i = 0; i < chip->ecc.steps; i++) {
+ ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf,
+ eccbuf);
+ if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc))
+ ret = nand_check_erased_ecc_chunk(databuf,
+ chip->ecc.size,
+ eccbuf,
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+
+ if (ret >= 0) {
+ mtd->ecc_stats.corrected += ret;
+ max_bitflips = max(ret, max_bitflips);
+ } else {
+ mtd->ecc_stats.failed++;
+ }
+
+ databuf += chip->ecc.size;
+ eccbuf += chip->ecc.bytes;
+ }
+
+ return max_bitflips;
+}
+
+static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,
+ bool oob_required, int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ int ret;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
+ if (ret)
+ return ret;
+
+ nand_write_data_op(chip, buf, mtd->writesize, false);
+
+ ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+ if (ret) {
+ atmel_pmecc_disable(nand->pmecc);
+ return ret;
+ }
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false);
+}
+
+static int atmel_nand_pmecc_write_page_raw(struct nand_chip *chip,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true);
+}
+
+static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
+ bool oob_required, int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
+ if (ret)
+ return ret;
+
+ ret = nand_read_data_op(chip, buf, mtd->writesize, false, false);
+ if (ret)
+ goto out_disable;
+
+ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, false);
+ if (ret)
+ goto out_disable;
+
+ ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+out_disable:
+ atmel_nand_pmecc_disable(chip, raw);
+
+ return ret;
+}
+
+static int atmel_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false);
+}
+
+static int atmel_nand_pmecc_read_page_raw(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true);
+}
+
+static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
+ const u8 *buf, bool oob_required,
+ int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ int ret;
+
+ atmel_hsmc_nand_select_target(nand, chip->cur_cs);
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ atmel_nfc_copy_to_sram(chip, buf, false);
+
+ nc->op.cmds[0] = NAND_CMD_SEQIN;
+ nc->op.ncmds = 1;
+ atmel_nfc_set_op_addr(chip, page, 0x0);
+ nc->op.cs = nand->activecs->id;
+ nc->op.data = ATMEL_NFC_WRITE_DATA;
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
+ if (ret)
+ return ret;
+
+ ret = atmel_nfc_exec_op(nc);
+ if (ret) {
+ atmel_nand_pmecc_disable(chip, raw);
+ dev_err(nc->base.dev,
+ "Failed to transfer NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ if (ret)
+ return ret;
+
+ nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int atmel_hsmc_nand_pmecc_write_page(struct nand_chip *chip,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
+ false);
+}
+
+static int atmel_hsmc_nand_pmecc_write_page_raw(struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
+ true);
+}
+
+static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
+ bool oob_required, int page,
+ bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ int ret;
+
+ atmel_hsmc_nand_select_target(nand, chip->cur_cs);
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ /*
+ * Optimized read page accessors only work when the NAND R/B pin is
+ * connected to a native SoC R/B pin. If that's not the case, fallback
+ * to the non-optimized one.
+ */
+ if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB)
+ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page,
+ raw);
+
+ nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0;
+
+ if (mtd->writesize > 512)
+ nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART;
+
+ atmel_nfc_set_op_addr(chip, page, 0x0);
+ nc->op.cs = nand->activecs->id;
+ nc->op.data = ATMEL_NFC_READ_DATA;
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
+ if (ret)
+ return ret;
+
+ ret = atmel_nfc_exec_op(nc);
+ if (ret) {
+ atmel_nand_pmecc_disable(chip, raw);
+ dev_err(nc->base.dev,
+ "Failed to load NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ atmel_nfc_copy_from_sram(chip, buf, true);
+
+ ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ return ret;
+}
+
+static int atmel_hsmc_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
+ false);
+}
+
+static int atmel_hsmc_nand_pmecc_read_page_raw(struct nand_chip *chip,
+ u8 *buf, int oob_required,
+ int page)
+{
+ return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
+ true);
+}
+
+static int atmel_nand_pmecc_init(struct nand_chip *chip)
+{
+ const struct nand_ecc_props *requirements =
+ nanddev_get_ecc_requirements(&chip->base);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+ struct atmel_pmecc_user_req req;
+ struct device_node *dn;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (!nc->pmecc) {
+ dev_err(nc->dev, "HW ECC not supported\n");
+ return -ENOTSUPP;
+ }
+
+ dn = nand_get_flash_node(chip);
+
+ if (of_property_read_bool(dn, "nand-ecc-maximize"))
+ req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+ else if (chip->ecc.strength)
+ req.ecc.strength = chip->ecc.strength;
+ else if (requirements->strength)
+ req.ecc.strength = requirements->strength;
+ else
+ req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+
+ if (chip->ecc.size)
+ req.ecc.sectorsize = chip->ecc.size;
+ else if (requirements->step_size)
+ req.ecc.sectorsize = requirements->step_size;
+ else
+ req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
+
+ req.pagesize = mtd->writesize;
+ req.oobsize = mtd->oobsize;
+
+ if (mtd->writesize <= 512) {
+ req.ecc.bytes = 4;
+ req.ecc.ooboffset = 0;
+ } else {
+ req.ecc.bytes = mtd->oobsize - 2;
+ req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO;
+ }
+
+ nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req);
+ if (IS_ERR(nand->pmecc))
+ return PTR_ERR(nand->pmecc);
+
+ chip->ecc.algo = NAND_ECC_ALGO_BCH;
+ chip->ecc.size = req.ecc.sectorsize;
+ chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
+ chip->ecc.strength = req.ecc.strength;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
+
+ return 0;
+}
+
+static int atmel_nand_ecc_init(struct nand_chip *chip)
+{
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(chip->controller);
+
+ switch (chip->ecc.engine_type) {
+ case NAND_ECC_ENGINE_TYPE_NONE:
+ case NAND_ECC_ENGINE_TYPE_SOFT:
+ /*
+ * Nothing to do, the core will initialize everything for us.
+ */
+ break;
+
+ case NAND_ECC_ENGINE_TYPE_ON_HOST:
+ ret = atmel_nand_pmecc_init(chip);
+ if (ret)
+ return ret;
+
+ chip->ecc.read_page = atmel_nand_pmecc_read_page;
+ chip->ecc.write_page = atmel_nand_pmecc_write_page;
+ chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw;
+ chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw;
+ break;
+
+ default:
+ /* Other modes are not supported. */
+ dev_err(nc->dev, "Unsupported ECC mode: %d\n",
+ chip->ecc.engine_type);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip)
+{
+ int ret;
+
+ ret = atmel_nand_ecc_init(chip);
+ if (ret)
+ return ret;
+
+ if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
+ return 0;
+
+ /* Adjust the ECC operations for the HSMC IP. */
+ chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page;
+ chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page;
+ chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw;
+ chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw;
+
+ return 0;
+}
+
+static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
+ const struct nand_interface_config *conf,
+ struct atmel_smc_cs_conf *smcconf)
+{
+ u32 ncycles, totalcycles, timeps, mckperiodps;
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ /* DDR interface not supported. */
+ if (!nand_interface_is_sdr(conf))
+ return -ENOTSUPP;
+
+ /*
+ * tRC < 30ns implies EDO mode. This controller does not support this
+ * mode.
+ */
+ if (conf->timings.sdr.tRC_min < 30000)
+ return -ENOTSUPP;
+
+ atmel_smc_cs_conf_init(smcconf);
+
+ mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
+ mckperiodps *= 1000;
+
+ /*
+ * Set write pulse timing. This one is easy to extract:
+ *
+ * NWE_PULSE = tWP
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
+ totalcycles = ncycles;
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * The write setup timing depends on the operation done on the NAND.
+ * All operations goes through the same data bus, but the operation
+ * type depends on the address we are writing to (ALE/CLE address
+ * lines).
+ * Since we have no way to differentiate the different operations at
+ * the SMC level, we must consider the worst case (the biggest setup
+ * time among all operation types):
+ *
+ * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
+ */
+ timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
+ conf->timings.sdr.tALS_min);
+ timeps = max(timeps, conf->timings.sdr.tDS_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
+ totalcycles += ncycles;
+ ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * As for the write setup timing, the write hold timing depends on the
+ * operation done on the NAND:
+ *
+ * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
+ */
+ timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
+ conf->timings.sdr.tALH_min);
+ timeps = max3(timeps, conf->timings.sdr.tDH_min,
+ conf->timings.sdr.tWH_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ totalcycles += ncycles;
+
+ /*
+ * The write cycle timing is directly matching tWC, but is also
+ * dependent on the other timings on the setup and hold timings we
+ * calculated earlier, which gives:
+ *
+ * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
+ ncycles = max(totalcycles, ncycles);
+ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't want the CS line to be toggled between each byte/word
+ * transfer to the NAND. The only way to guarantee that is to have the
+ * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+ *
+ * NCS_WR_PULSE = NWE_CYCLE
+ */
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * As for the write setup timing, the read hold timing depends on the
+ * operation done on the NAND:
+ *
+ * NRD_HOLD = max(tREH, tRHOH)
+ */
+ timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ totalcycles = ncycles;
+
+ /*
+ * TDF = tRHZ - NRD_HOLD
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
+ ncycles -= totalcycles;
+
+ /*
+ * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
+ * we might end up with a config that does not fit in the TDF field.
+ * Just take the max value in this case and hope that the NAND is more
+ * tolerant than advertised.
+ */
+ if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
+ ncycles = ATMEL_SMC_MODE_TDF_MAX;
+ else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
+ ncycles = ATMEL_SMC_MODE_TDF_MIN;
+
+ smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
+ ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
+
+ /*
+ * Read pulse timing directly matches tRP:
+ *
+ * NRD_PULSE = tRP
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
+ totalcycles += ncycles;
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * The write cycle timing is directly matching tWC, but is also
+ * dependent on the setup and hold timings we calculated earlier,
+ * which gives:
+ *
+ * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
+ *
+ * NRD_SETUP is always 0.
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
+ ncycles = max(totalcycles, ncycles);
+ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't want the CS line to be toggled between each byte/word
+ * transfer from the NAND. The only way to guarantee that is to have
+ * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+ *
+ * NCS_RD_PULSE = NRD_CYCLE
+ */
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /* Txxx timings are directly matching tXXX ones. */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TADL_SHIFT,
+ ncycles);
+ /*
+ * Version 4 of the ONFI spec mandates that tADL be at least 400
+ * nanoseconds, but, depending on the master clock rate, 400 ns may not
+ * fit in the tADL field of the SMC reg. We need to relax the check and
+ * accept the -ERANGE return code.
+ *
+ * Note that previous versions of the ONFI spec had a lower tADL_min
+ * (100 or 200 ns). It's not clear why this timing constraint got
+ * increased but it seems most NANDs are fine with values lower than
+ * 400ns, so we should be safe.
+ */
+ if (ret && ret != -ERANGE)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TAR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TRR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TWB_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /* Attach the CS line to the NFC logic. */
+ smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
+
+ /* Set the appropriate data bus width. */
+ if (nand->base.options & NAND_BUSWIDTH_16)
+ smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
+
+ /* Operate in NRD/NWE READ/WRITEMODE. */
+ smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
+ ATMEL_SMC_MODE_WRITEMODE_NWE;
+
+ return 0;
+}
+
+static int atmel_smc_nand_setup_interface(struct atmel_nand *nand,
+ int csline,
+ const struct nand_interface_config *conf)
+{
+ struct atmel_nand_controller *nc;
+ struct atmel_smc_cs_conf smcconf;
+ struct atmel_nand_cs *cs;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ cs = &nand->cs[csline];
+ cs->smcconf = smcconf;
+ atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
+
+ return 0;
+}
+
+static int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand,
+ int csline,
+ const struct nand_interface_config *conf)
+{
+ struct atmel_hsmc_nand_controller *nc;
+ struct atmel_smc_cs_conf smcconf;
+ struct atmel_nand_cs *cs;
+ int ret;
+
+ nc = to_hsmc_nand_controller(nand->base.controller);
+
+ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ cs = &nand->cs[csline];
+ cs->smcconf = smcconf;
+
+ if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
+ cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
+
+ atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id,
+ &cs->smcconf);
+
+ return 0;
+}
+
+static int atmel_nand_setup_interface(struct nand_chip *chip, int csline,
+ const struct nand_interface_config *conf)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ const struct nand_sdr_timings *sdr;
+ struct atmel_nand_controller *nc;
+
+ sdr = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ nc = to_nand_controller(nand->base.controller);
+
+ if (csline >= nand->numcs ||
+ (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
+ return -EINVAL;
+
+ return nc->caps->ops->setup_interface(nand, csline, conf);
+}
+
+static int atmel_nand_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ return nc->caps->ops->exec_op(nand, op, check_only);
+}
+
+static void atmel_nand_init(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ mtd->dev.parent = nc->dev;
+ nand->base.controller = &nc->base;
+
+ if (!nc->mck || !nc->caps->ops->setup_interface)
+ chip->options |= NAND_KEEP_TIMINGS;
+
+ /* Default to HW ECC if pmecc is available. */
+ if (nc->pmecc)
+ chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
+}
+
+static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct atmel_smc_nand_controller *smc_nc;
+ int i;
+
+ atmel_nand_init(nc, nand);
+
+ smc_nc = to_smc_nand_controller(chip->controller);
+ if (!smc_nc->ebi_csa_regmap)
+ return;
+
+ /* Attach the CS to the NAND Flash logic. */
+ for (i = 0; i < nand->numcs; i++)
+ regmap_update_bits(smc_nc->ebi_csa_regmap,
+ smc_nc->ebi_csa->offs,
+ BIT(nand->cs[i].id), BIT(nand->cs[i].id));
+
+ if (smc_nc->ebi_csa->nfd0_on_d16)
+ regmap_update_bits(smc_nc->ebi_csa_regmap,
+ smc_nc->ebi_csa->offs,
+ smc_nc->ebi_csa->nfd0_on_d16,
+ smc_nc->ebi_csa->nfd0_on_d16);
+}
+
+static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
+ struct device_node *np,
+ int reg_cells)
+{
+ struct atmel_nand *nand;
+ struct gpio_desc *gpio;
+ int numcs, ret, i;
+
+ numcs = of_property_count_elems_of_size(np, "reg",
+ reg_cells * sizeof(u32));
+ if (numcs < 1) {
+ dev_err(nc->dev, "Missing or invalid reg property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ nand = kzalloc(struct_size(nand, cs, numcs), GFP_KERNEL);
+ if (!nand)
+ return ERR_PTR(-ENOMEM);
+
+ nand->numcs = numcs;
+
+ gpio = dev_gpiod_get(nc->dev, np, "det", GPIOD_IN, "nand-det");
+ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+ dev_err(nc->dev,
+ "Failed to get detect gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return ERR_CAST(gpio);
+ }
+
+ if (!IS_ERR(gpio))
+ nand->cdgpio = gpio;
+
+ for (i = 0; i < numcs; i++) {
+ struct resource res;
+ u32 val;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret) {
+ dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+ ret);
+ return ERR_PTR(ret);
+ }
+
+ ret = of_property_read_u32_index(np, "reg", i * reg_cells,
+ &val);
+ if (ret) {
+ dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+ ret);
+ return ERR_PTR(ret);
+ }
+
+ nand->cs[i].id = val;
+
+ nand->cs[i].io.virt = IOMEM(res.start);
+ ret = dev_request_resource(nc->dev, &res);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (!of_property_read_u32(np, "atmel,rb", &val)) {
+ if (val > ATMEL_NFC_MAX_RB_ID)
+ return ERR_PTR(-EINVAL);
+
+ nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB;
+ nand->cs[i].rb.id = val;
+ } else {
+ gpio = dev_gpiod_get_index(nc->dev, np, "rb", i, GPIOD_IN, "nand-rb");
+ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+ dev_errp_probe(nc->dev, gpio, "Failed to get detect gpio\n");
+ return ERR_CAST(gpio);
+ }
+
+ if (!IS_ERR(gpio)) {
+ nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
+ nand->cs[i].rb.gpio = gpio;
+ }
+ }
+
+ gpio = dev_gpiod_get_index(nc->dev, np, "cs", i, GPIOD_OUT_HIGH, "nand-cs");
+ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+ dev_errp_probe(nc->dev, gpio, "Failed to get CS gpio\n");
+ return ERR_CAST(gpio);
+ }
+
+ if (!IS_ERR(gpio))
+ nand->cs[i].csgpio = gpio;
+ }
+
+ nand_set_flash_node(&nand->base, np);
+
+ return nand;
+}
+
+static int
+atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ /* No card inserted, skip this NAND. */
+ if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
+ dev_info(nc->dev, "No SmartMedia card inserted.\n");
+ return 0;
+ }
+
+ nc->caps->ops->nand_init(nc, nand);
+
+ ret = nand_scan(chip, nand->numcs);
+ if (ret) {
+ dev_err(nc->dev, "NAND scan failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = add_mtd_nand_device(mtd, "nand");
+ if (ret) {
+ dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
+ nand_cleanup(chip);
+ return ret;
+ }
+
+ list_add_tail(&nand->node, &nc->chips);
+
+ return 0;
+}
+
+static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
+{
+ struct device_node *np, *nand_np;
+ struct device *dev = nc->dev;
+ int ret, reg_cells;
+ u32 val;
+
+ np = dev->of_node;
+
+ ret = of_property_read_u32(np, "#address-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells = val;
+
+ ret = of_property_read_u32(np, "#size-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #size-cells property\n");
+ return ret;
+ }
+
+ reg_cells += val;
+
+ for_each_child_of_node(np, nand_np) {
+ struct atmel_nand *nand;
+
+ nand = atmel_nand_create(nc, nand_np, reg_cells);
+ if (IS_ERR(nand))
+ return PTR_ERR(nand);
+
+ ret = atmel_nand_controller_add_nand(nc, nand);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
+{
+ clk_put(nc->mck);
+}
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9260_ebi_csa = {
+ .offs = AT91SAM9260_MATRIX_EBICSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9261_ebi_csa = {
+ .offs = AT91SAM9261_MATRIX_EBICSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9263_ebi_csa = {
+ .offs = AT91SAM9263_MATRIX_EBI0CSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9rl_ebi_csa = {
+ .offs = AT91SAM9RL_MATRIX_EBICSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9g45_ebi_csa = {
+ .offs = AT91SAM9G45_MATRIX_EBICSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9n12_ebi_csa = {
+ .offs = AT91SAM9N12_MATRIX_EBICSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg at91sam9x5_ebi_csa = {
+ .offs = AT91SAM9X5_MATRIX_EBICSA,
+};
+
+static const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = {
+ .offs = AT91_SFR_CCFG_EBICSA,
+ .nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16,
+};
+
+static const struct of_device_id __maybe_unused atmel_ebi_csa_regmap_of_ids[] = {
+ {
+ .compatible = "atmel,at91sam9260-matrix",
+ .data = &at91sam9260_ebi_csa,
+ },
+ {
+ .compatible = "atmel,at91sam9261-matrix",
+ .data = &at91sam9261_ebi_csa,
+ },
+ {
+ .compatible = "atmel,at91sam9263-matrix",
+ .data = &at91sam9263_ebi_csa,
+ },
+ {
+ .compatible = "atmel,at91sam9rl-matrix",
+ .data = &at91sam9rl_ebi_csa,
+ },
+ {
+ .compatible = "atmel,at91sam9g45-matrix",
+ .data = &at91sam9g45_ebi_csa,
+ },
+ {
+ .compatible = "atmel,at91sam9n12-matrix",
+ .data = &at91sam9n12_ebi_csa,
+ },
+ {
+ .compatible = "atmel,at91sam9x5-matrix",
+ .data = &at91sam9x5_ebi_csa,
+ },
+ {
+ .compatible = "microchip,sam9x60-sfr",
+ .data = &sam9x60_ebi_csa,
+ },
+ { /* sentinel */ },
+};
+
+static int atmel_nand_attach_chip(struct nand_chip *chip)
+{
+ struct atmel_nand_controller *nc = to_nand_controller(chip->controller);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ ret = nc->caps->ops->ecc_init(chip);
+ if (ret)
+ return ret;
+
+ if (!mtd->name) {
+ /*
+ * If the new bindings are used and the bootloader has not been
+ * updated to pass a new mtdparts parameter on the cmdline, you
+ * should define the following property in your nand node:
+ *
+ * label = "atmel_nand";
+ *
+ * This way, mtd->name will be set by the core when
+ * nand_set_flash_node() is called.
+ */
+ mtd->name = basprintf("%s:nand.%d", dev_name(nc->dev),
+ nand->cs[0].id);
+ if (!mtd->name) {
+ dev_err(nc->dev, "Failed to allocate mtd->name\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static const struct nand_controller_ops atmel_nand_controller_ops = {
+ .attach_chip = atmel_nand_attach_chip,
+ .setup_interface = atmel_nand_setup_interface,
+ .exec_op = atmel_nand_exec_op,
+};
+
+static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
+ struct device *dev,
+ const struct atmel_nand_controller_caps *caps)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ nand_controller_init(&nc->base);
+ nc->base.ops = &atmel_nand_controller_ops;
+ INIT_LIST_HEAD(&nc->chips);
+ nc->dev = dev;
+ nc->caps = caps;
+
+ dev->priv = nc;
+
+ nc->pmecc = dev_atmel_pmecc_get(dev);
+ if (IS_ERR(nc->pmecc))
+ return dev_err_probe(dev, PTR_ERR(nc->pmecc),
+ "Could not get PMECC object\n");
+
+ nc->mck = of_clk_get(dev->parent->of_node, 0);
+ if (IS_ERR(nc->mck)) {
+ dev_err(dev, "Failed to retrieve MCK clk\n");
+ ret = PTR_ERR(nc->mck);
+ goto out_release_dma;
+ }
+
+ np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
+ if (!np) {
+ dev_err(dev, "Missing or invalid atmel,smc property\n");
+ ret = -EINVAL;
+ goto out_release_dma;
+ }
+
+ nc->smc = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(nc->smc)) {
+ ret = PTR_ERR(nc->smc);
+ dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
+ goto out_release_dma;
+ }
+
+ return 0;
+
+out_release_dma:
+ return ret;
+}
+
+static int
+atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
+{
+ struct device *dev = nc->base.dev;
+ const struct of_device_id *match;
+ struct device_node *np;
+ int ret;
+
+ np = of_parse_phandle(dev->parent->of_node,
+ nc->base.caps->ebi_csa_regmap_name, 0);
+ if (!np)
+ return 0;
+
+ match = of_match_node(atmel_ebi_csa_regmap_of_ids, np);
+ if (!match) {
+ of_node_put(np);
+ return 0;
+ }
+
+ nc->ebi_csa_regmap = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(nc->ebi_csa_regmap)) {
+ ret = PTR_ERR(nc->ebi_csa_regmap);
+ dev_err(dev, "Could not get EBICSA regmap (err = %d)\n", ret);
+ return ret;
+ }
+
+ nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data;
+
+ /*
+ * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
+ * add 4 to ->ebi_csa->offs.
+ */
+ if (of_device_is_compatible(dev->parent->of_node,
+ "atmel,at91sam9263-ebi1"))
+ nc->ebi_csa->offs += 4;
+
+ return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
+{
+ struct device *dev = nc->base.dev;
+ struct device_node *np;
+ int ret;
+
+ np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
+ if (!np) {
+ dev_err(dev, "Missing or invalid atmel,smc property\n");
+ return -EINVAL;
+ }
+
+ nc->hsmc_layout = atmel_hsmc_get_reg_layout(np);
+
+ np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
+ if (!np) {
+ dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
+ return -EINVAL;
+ }
+
+ nc->io = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(nc->io)) {
+ ret = PTR_ERR(nc->io);
+ dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret);
+ return ret;
+ }
+
+ nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
+ "atmel,nfc-sram", 0);
+ if (!nc->sram.pool) {
+ dev_err(nc->base.dev, "Missing SRAM\n");
+ return -ENOMEM;
+ }
+
+ nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool,
+ ATMEL_NFC_SRAM_SIZE,
+ NULL);
+ if (!nc->sram.virt) {
+ dev_err(nc->base.dev,
+ "Could not allocate memory from the NFC SRAM pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+ struct atmel_hsmc_nand_controller *hsmc_nc;
+
+ hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
+ regmap_write(hsmc_nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+ ATMEL_HSMC_NFC_CTRL_DIS);
+
+ atmel_nand_controller_cleanup(nc);
+}
+
+static int atmel_hsmc_nand_controller_probe(struct device *dev,
+ const struct atmel_nand_controller_caps *caps)
+{
+ struct atmel_hsmc_nand_controller *nc;
+ int ret;
+
+ nc = kzalloc(sizeof(*nc), GFP_KERNEL);
+ if (!nc)
+ return -ENOMEM;
+
+ ret = atmel_nand_controller_init(&nc->base, dev, caps);
+ if (ret)
+ return ret;
+
+ ret = atmel_hsmc_nand_controller_init(nc);
+ if (ret)
+ return ret;
+
+ /* Make sure all irqs are masked . */
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
+
+ /* Initial NFC configuration. */
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+ ATMEL_HSMC_NFC_CFG_DTO_MAX);
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+ ATMEL_HSMC_NFC_CTRL_EN);
+
+ ret = atmel_nand_controller_add_nands(&nc->base);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ atmel_hsmc_nand_controller_remove(&nc->base);
+
+ return ret;
+}
+
+static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
+ .probe = atmel_hsmc_nand_controller_probe,
+ .ecc_init = atmel_hsmc_nand_ecc_init,
+ .nand_init = atmel_nand_init,
+ .setup_interface = atmel_hsmc_nand_setup_interface,
+ .exec_op = atmel_hsmc_nand_exec_op,
+};
+
+static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_hsmc_nc_ops,
+};
+
+static int atmel_smc_nand_controller_probe(struct device *dev,
+ const struct atmel_nand_controller_caps *caps)
+{
+ struct atmel_smc_nand_controller *nc;
+ int ret;
+
+ nc = kzalloc(sizeof(*nc), GFP_KERNEL);
+ if (!nc)
+ return -ENOMEM;
+
+ ret = atmel_nand_controller_init(&nc->base, dev, caps);
+ if (ret)
+ return ret;
+
+ ret = atmel_smc_nand_controller_init(nc);
+ if (ret)
+ return ret;
+
+ return atmel_nand_controller_add_nands(&nc->base);
+}
+
+/*
+ * The SMC reg layout of at91rm9200 is completely different which prevents us
+ * from re-using atmel_smc_nand_setup_interface() for the
+ * ->setup_interface() hook.
+ * At this point, there's no support for the at91rm9200 SMC IP, so we leave
+ * ->setup_interface() unassigned.
+ */
+static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
+ .probe = atmel_smc_nand_controller_probe,
+ .ecc_init = atmel_nand_ecc_init,
+ .nand_init = atmel_smc_nand_init,
+ .exec_op = atmel_smc_nand_exec_op,
+};
+
+static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ebi_csa_regmap_name = "atmel,matrix",
+ .ops = &at91rm9200_nc_ops,
+};
+
+static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+ .probe = atmel_smc_nand_controller_probe,
+ .ecc_init = atmel_nand_ecc_init,
+ .nand_init = atmel_smc_nand_init,
+ .setup_interface = atmel_smc_nand_setup_interface,
+ .exec_op = atmel_smc_nand_exec_op,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ebi_csa_regmap_name = "atmel,matrix",
+ .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
+ .ale_offs = BIT(22),
+ .cle_offs = BIT(21),
+ .ebi_csa_regmap_name = "atmel,matrix",
+ .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ebi_csa_regmap_name = "atmel,matrix",
+ .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps microchip_sam9x60_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ebi_csa_regmap_name = "microchip,sfr",
+ .ops = &atmel_smc_nc_ops,
+};
+
+static const struct of_device_id atmel_nand_controller_of_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-nand-controller",
+ .data = &atmel_rm9200_nc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9260-nand-controller",
+ .data = &atmel_sam9260_nc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9261-nand-controller",
+ .data = &atmel_sam9261_nc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9g45-nand-controller",
+ .data = &atmel_sam9g45_nc_caps,
+ },
+ {
+ .compatible = "atmel,sama5d3-nand-controller",
+ .data = &atmel_sama5_nc_caps,
+ },
+ {
+ .compatible = "microchip,sam9x60-nand-controller",
+ .data = &microchip_sam9x60_nc_caps,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids);
+
+static int atmel_nand_controller_probe(struct device *dev)
+{
+ const struct atmel_nand_controller_caps *caps;
+
+ if (dev->id_entry)
+ caps = (void *)dev->id_entry->driver_data;
+ else
+ caps = of_device_get_match_data(dev);
+
+ if (!caps) {
+ dev_err(dev, "Could not retrieve NFC caps\n");
+ return -EINVAL;
+ }
+
+ return caps->ops->probe(dev, caps);
+}
+
+static struct driver atmel_nand_controller_driver = {
+ .name = "atmel-nand-controller",
+ .of_match_table = atmel_nand_controller_of_ids,
+ .probe = atmel_nand_controller_probe,
+};
+device_platform_driver(atmel_nand_controller_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs");
+MODULE_ALIAS("platform:atmel-nand-controller");
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c
new file mode 100644
index 0000000000..1b89607a33
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/pmecc.c
@@ -0,0 +1,993 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 ATMEL
+ * Copyright 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ * Copyright 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
+ * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
+ * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * Copyright 2012 ATMEL, Hong Xu
+ *
+ * Add Nand Flash Controller support for SAMA5 SoC
+ * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
+ *
+ * The PMECC is an hardware assisted BCH engine, which means part of the
+ * ECC algorithm is left to the software. The hardware/software repartition
+ * is explained in the "PMECC Controller Functional Description" chapter in
+ * Atmel datasheets, and some of the functions in this file are directly
+ * implementing the algorithms described in the "Software Implementation"
+ * sub-section.
+ *
+ * TODO: it seems that the software BCH implementation in lib/bch.c is already
+ * providing some of the logic we are implementing here. It would be smart
+ * to expose the needed lib/bch.c helpers/functions and re-use them here.
+ */
+
+#include <linux/iopoll.h>
+#include <module.h>
+#include <linux/mtd/rawnand.h>
+#include <of.h>
+#include <of_device.h>
+#include <linux/slab.h>
+
+#include "pmecc.h"
+
+/* Galois field dimension */
+#define PMECC_GF_DIMENSION_13 13
+#define PMECC_GF_DIMENSION_14 14
+
+/* Primitive Polynomial used by PMECC */
+#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
+#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
+
+#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
+#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
+
+/* Time out value for reading PMECC status register */
+#define PMECC_MAX_TIMEOUT_MS 100
+
+/* PMECC Register Definitions */
+#define ATMEL_PMECC_CFG 0x0
+#define PMECC_CFG_BCH_STRENGTH(x) (x)
+#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0)
+#define PMECC_CFG_SECTOR512 (0 << 4)
+#define PMECC_CFG_SECTOR1024 (1 << 4)
+#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
+#define PMECC_CFG_READ_OP (0 << 12)
+#define PMECC_CFG_WRITE_OP (1 << 12)
+#define PMECC_CFG_SPARE_ENABLE BIT(16)
+#define PMECC_CFG_AUTO_ENABLE BIT(20)
+
+#define ATMEL_PMECC_SAREA 0x4
+#define ATMEL_PMECC_SADDR 0x8
+#define ATMEL_PMECC_EADDR 0xc
+
+#define ATMEL_PMECC_CLK 0x10
+#define PMECC_CLK_133MHZ (2 << 0)
+
+#define ATMEL_PMECC_CTRL 0x14
+#define PMECC_CTRL_RST BIT(0)
+#define PMECC_CTRL_DATA BIT(1)
+#define PMECC_CTRL_USER BIT(2)
+#define PMECC_CTRL_ENABLE BIT(4)
+#define PMECC_CTRL_DISABLE BIT(5)
+
+#define ATMEL_PMECC_SR 0x18
+#define PMECC_SR_BUSY BIT(0)
+#define PMECC_SR_ENABLE BIT(4)
+
+#define ATMEL_PMECC_IER 0x1c
+#define ATMEL_PMECC_IDR 0x20
+#define ATMEL_PMECC_IMR 0x24
+#define ATMEL_PMECC_ISR 0x28
+#define PMECC_ERROR_INT BIT(0)
+
+#define ATMEL_PMECC_ECC(sector, n) \
+ ((((sector) + 1) * 0x40) + (n))
+
+#define ATMEL_PMECC_REM(sector, n) \
+ ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
+
+/* PMERRLOC Register Definitions */
+#define ATMEL_PMERRLOC_ELCFG 0x0
+#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
+#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
+#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
+
+#define ATMEL_PMERRLOC_ELPRIM 0x4
+#define ATMEL_PMERRLOC_ELEN 0x8
+#define ATMEL_PMERRLOC_ELDIS 0xc
+#define PMERRLOC_DISABLE BIT(0)
+
+#define ATMEL_PMERRLOC_ELSR 0x10
+#define PMERRLOC_ELSR_BUSY BIT(0)
+
+#define ATMEL_PMERRLOC_ELIER 0x14
+#define ATMEL_PMERRLOC_ELIDR 0x18
+#define ATMEL_PMERRLOC_ELIMR 0x1c
+#define ATMEL_PMERRLOC_ELISR 0x20
+#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8)
+#define PMERRLOC_CALC_DONE BIT(0)
+
+#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28)
+
+#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs))
+
+struct atmel_pmecc_gf_tables {
+ u16 *alpha_to;
+ u16 *index_of;
+};
+
+struct atmel_pmecc_caps {
+ const int *strengths;
+ int nstrengths;
+ int el_offset;
+ bool correct_erased_chunks;
+};
+
+struct atmel_pmecc {
+ struct device *dev;
+ const struct atmel_pmecc_caps *caps;
+
+ struct {
+ void __iomem *base;
+ void __iomem *errloc;
+ } regs;
+
+ struct mutex lock;
+};
+
+struct atmel_pmecc_user_conf_cache {
+ u32 cfg;
+ u32 sarea;
+ u32 saddr;
+ u32 eaddr;
+};
+
+struct atmel_pmecc_user {
+ struct atmel_pmecc_user_conf_cache cache;
+ struct atmel_pmecc *pmecc;
+ const struct atmel_pmecc_gf_tables *gf_tables;
+ int eccbytes;
+ s16 *partial_syn;
+ s16 *si;
+ s16 *lmu;
+ s16 *smu;
+ s32 *mu;
+ s32 *dmu;
+ s32 *delta;
+ u32 isr;
+};
+
+static DEFINE_MUTEX(pmecc_gf_tables_lock);
+static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
+static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
+
+static inline int deg(unsigned int poly)
+{
+ /* polynomial degree is the most-significant bit index */
+ return fls(poly) - 1;
+}
+
+static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
+ struct atmel_pmecc_gf_tables *gf_tables)
+{
+ unsigned int i, x = 1;
+ const unsigned int k = BIT(deg(poly));
+ unsigned int nn = BIT(mm) - 1;
+
+ /* primitive polynomial must be of degree m */
+ if (k != (1u << mm))
+ return -EINVAL;
+
+ for (i = 0; i < nn; i++) {
+ gf_tables->alpha_to[i] = x;
+ gf_tables->index_of[x] = i;
+ if (i && (x == 1))
+ /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
+ return -EINVAL;
+ x <<= 1;
+ if (x & k)
+ x ^= poly;
+ }
+ gf_tables->alpha_to[nn] = 1;
+ gf_tables->index_of[0] = 0;
+
+ return 0;
+}
+
+static const struct atmel_pmecc_gf_tables *
+atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
+{
+ struct atmel_pmecc_gf_tables *gf_tables;
+ unsigned int poly, degree, table_size;
+ int ret;
+
+ if (req->ecc.sectorsize == 512) {
+ degree = PMECC_GF_DIMENSION_13;
+ poly = PMECC_GF_13_PRIMITIVE_POLY;
+ table_size = PMECC_LOOKUP_TABLE_SIZE_512;
+ } else {
+ degree = PMECC_GF_DIMENSION_14;
+ poly = PMECC_GF_14_PRIMITIVE_POLY;
+ table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
+ }
+
+ gf_tables = kzalloc(sizeof(*gf_tables) +
+ (2 * table_size * sizeof(u16)),
+ GFP_KERNEL);
+ if (!gf_tables)
+ return ERR_PTR(-ENOMEM);
+
+ gf_tables->alpha_to = (void *)(gf_tables + 1);
+ gf_tables->index_of = gf_tables->alpha_to + table_size;
+
+ ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
+ if (ret) {
+ kfree(gf_tables);
+ return ERR_PTR(ret);
+ }
+
+ return gf_tables;
+}
+
+static const struct atmel_pmecc_gf_tables *
+atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
+{
+ const struct atmel_pmecc_gf_tables **gf_tables, *ret;
+
+ mutex_lock(&pmecc_gf_tables_lock);
+ if (req->ecc.sectorsize == 512)
+ gf_tables = &pmecc_gf_tables_512;
+ else
+ gf_tables = &pmecc_gf_tables_1024;
+
+ ret = *gf_tables;
+
+ if (!ret) {
+ ret = atmel_pmecc_create_gf_tables(req);
+ if (!IS_ERR(ret))
+ *gf_tables = ret;
+ }
+ mutex_unlock(&pmecc_gf_tables_lock);
+
+ return ret;
+}
+
+static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
+ struct atmel_pmecc_user_req *req)
+{
+ int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
+
+ if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
+ return -EINVAL;
+
+ if (req->ecc.ooboffset >= 0 &&
+ req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
+ return -EINVAL;
+
+ if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
+ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
+ return -EINVAL;
+
+ if (req->pagesize > 512)
+ req->ecc.sectorsize = 1024;
+ else
+ req->ecc.sectorsize = 512;
+ }
+
+ if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
+ return -EINVAL;
+
+ if (req->pagesize % req->ecc.sectorsize)
+ return -EINVAL;
+
+ req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
+
+ max_eccbytes = req->ecc.bytes;
+
+ for (i = 0; i < pmecc->caps->nstrengths; i++) {
+ int nbytes, strength = pmecc->caps->strengths[i];
+
+ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
+ strength < req->ecc.strength)
+ continue;
+
+ nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
+ 8);
+ nbytes *= req->ecc.nsectors;
+
+ if (nbytes > max_eccbytes)
+ break;
+
+ eccstrength = strength;
+ eccbytes = nbytes;
+
+ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
+ break;
+ }
+
+ if (!eccstrength)
+ return -EINVAL;
+
+ req->ecc.bytes = eccbytes;
+ req->ecc.strength = eccstrength;
+
+ if (req->ecc.ooboffset < 0)
+ req->ecc.ooboffset = req->oobsize - eccbytes;
+
+ return 0;
+}
+
+struct atmel_pmecc_user *
+atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
+ struct atmel_pmecc_user_req *req)
+{
+ struct atmel_pmecc_user *user;
+ const struct atmel_pmecc_gf_tables *gf_tables;
+ int strength, size, ret;
+
+ ret = atmel_pmecc_prepare_user_req(pmecc, req);
+ if (ret)
+ return ERR_PTR(ret);
+
+ size = sizeof(*user);
+ size = ALIGN(size, sizeof(u16));
+ /* Reserve space for partial_syn, si and smu */
+ size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
+ (2 + req->ecc.strength + 2);
+ /* Reserve space for lmu. */
+ size += (req->ecc.strength + 1) * sizeof(u16);
+ /* Reserve space for mu, dmu and delta. */
+ size = ALIGN(size, sizeof(s32));
+ size += (req->ecc.strength + 1) * sizeof(s32) * 3;
+
+ user = kzalloc(size, GFP_KERNEL);
+ if (!user)
+ return ERR_PTR(-ENOMEM);
+
+ user->pmecc = pmecc;
+
+ user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
+ user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
+ user->lmu = user->si + ((2 * req->ecc.strength) + 1);
+ user->smu = user->lmu + (req->ecc.strength + 1);
+ user->mu = (s32 *)PTR_ALIGN(user->smu +
+ (((2 * req->ecc.strength) + 1) *
+ (req->ecc.strength + 2)),
+ sizeof(s32));
+ user->dmu = user->mu + req->ecc.strength + 1;
+ user->delta = user->dmu + req->ecc.strength + 1;
+
+ gf_tables = atmel_pmecc_get_gf_tables(req);
+ if (IS_ERR(gf_tables)) {
+ kfree(user);
+ return ERR_CAST(gf_tables);
+ }
+
+ user->gf_tables = gf_tables;
+
+ user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
+
+ for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
+ if (pmecc->caps->strengths[strength] == req->ecc.strength)
+ break;
+ }
+
+ user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
+ PMECC_CFG_NSECTORS(req->ecc.nsectors);
+
+ if (req->ecc.sectorsize == 1024)
+ user->cache.cfg |= PMECC_CFG_SECTOR1024;
+
+ user->cache.sarea = req->oobsize - 1;
+ user->cache.saddr = req->ecc.ooboffset;
+ user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
+
+ return user;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
+
+void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
+{
+ kfree(user);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
+
+static int get_strength(struct atmel_pmecc_user *user)
+{
+ const int *strengths = user->pmecc->caps->strengths;
+
+ return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
+}
+
+static int get_sectorsize(struct atmel_pmecc_user *user)
+{
+ return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512;
+}
+
+static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
+{
+ int strength = get_strength(user);
+ u32 value;
+ int i;
+
+ /* Fill odd syndromes */
+ for (i = 0; i < strength; i++) {
+ value = readl_relaxed(user->pmecc->regs.base +
+ ATMEL_PMECC_REM(sector, i / 2));
+ if (i & 1)
+ value >>= 16;
+
+ user->partial_syn[(2 * i) + 1] = value;
+ }
+}
+
+static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
+{
+ int degree = get_sectorsize(user) == 512 ? 13 : 14;
+ int cw_len = BIT(degree) - 1;
+ int strength = get_strength(user);
+ s16 *alpha_to = user->gf_tables->alpha_to;
+ s16 *index_of = user->gf_tables->index_of;
+ s16 *partial_syn = user->partial_syn;
+ s16 *si;
+ int i, j;
+
+ /*
+ * si[] is a table that holds the current syndrome value,
+ * an element of that table belongs to the field
+ */
+ si = user->si;
+
+ memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
+
+ /* Computation 2t syndromes based on S(x) */
+ /* Odd syndromes */
+ for (i = 1; i < 2 * strength; i += 2) {
+ for (j = 0; j < degree; j++) {
+ if (partial_syn[i] & BIT(j))
+ si[i] = alpha_to[i * j] ^ si[i];
+ }
+ }
+ /* Even syndrome = (Odd syndrome) ** 2 */
+ for (i = 2, j = 1; j <= strength; i = ++j << 1) {
+ if (si[j] == 0) {
+ si[i] = 0;
+ } else {
+ s16 tmp;
+
+ tmp = index_of[si[j]];
+ tmp = (tmp * 2) % cw_len;
+ si[i] = alpha_to[tmp];
+ }
+ }
+}
+
+static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
+{
+ s16 *lmu = user->lmu;
+ s16 *si = user->si;
+ s32 *mu = user->mu;
+ s32 *dmu = user->dmu;
+ s32 *delta = user->delta;
+ int degree = get_sectorsize(user) == 512 ? 13 : 14;
+ int cw_len = BIT(degree) - 1;
+ int strength = get_strength(user);
+ int num = 2 * strength + 1;
+ s16 *index_of = user->gf_tables->index_of;
+ s16 *alpha_to = user->gf_tables->alpha_to;
+ int i, j, k;
+ u32 dmu_0_count, tmp;
+ s16 *smu = user->smu;
+
+ /* index of largest delta */
+ int ro;
+ int largest;
+ int diff;
+
+ dmu_0_count = 0;
+
+ /* First Row */
+
+ /* Mu */
+ mu[0] = -1;
+
+ memset(smu, 0, sizeof(s16) * num);
+ smu[0] = 1;
+
+ /* discrepancy set to 1 */
+ dmu[0] = 1;
+ /* polynom order set to 0 */
+ lmu[0] = 0;
+ delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
+
+ /* Second Row */
+
+ /* Mu */
+ mu[1] = 0;
+ /* Sigma(x) set to 1 */
+ memset(&smu[num], 0, sizeof(s16) * num);
+ smu[num] = 1;
+
+ /* discrepancy set to S1 */
+ dmu[1] = si[1];
+
+ /* polynom order set to 0 */
+ lmu[1] = 0;
+
+ delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
+
+ /* Init the Sigma(x) last row */
+ memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
+
+ for (i = 1; i <= strength; i++) {
+ mu[i + 1] = i << 1;
+ /* Begin Computing Sigma (Mu+1) and L(mu) */
+ /* check if discrepancy is set to 0 */
+ if (dmu[i] == 0) {
+ dmu_0_count++;
+
+ tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
+ if ((strength - (lmu[i] >> 1) - 1) & 0x1)
+ tmp += 2;
+ else
+ tmp += 1;
+
+ if (dmu_0_count == tmp) {
+ for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
+ smu[(strength + 1) * num + j] =
+ smu[i * num + j];
+
+ lmu[strength + 1] = lmu[i];
+ return;
+ }
+
+ /* copy polynom */
+ for (j = 0; j <= lmu[i] >> 1; j++)
+ smu[(i + 1) * num + j] = smu[i * num + j];
+
+ /* copy previous polynom order to the next */
+ lmu[i + 1] = lmu[i];
+ } else {
+ ro = 0;
+ largest = -1;
+ /* find largest delta with dmu != 0 */
+ for (j = 0; j < i; j++) {
+ if ((dmu[j]) && (delta[j] > largest)) {
+ largest = delta[j];
+ ro = j;
+ }
+ }
+
+ /* compute difference */
+ diff = (mu[i] - mu[ro]);
+
+ /* Compute degree of the new smu polynomial */
+ if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
+ lmu[i + 1] = lmu[i];
+ else
+ lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
+
+ /* Init smu[i+1] with 0 */
+ for (k = 0; k < num; k++)
+ smu[(i + 1) * num + k] = 0;
+
+ /* Compute smu[i+1] */
+ for (k = 0; k <= lmu[ro] >> 1; k++) {
+ s16 a, b, c;
+
+ if (!(smu[ro * num + k] && dmu[i]))
+ continue;
+
+ a = index_of[dmu[i]];
+ b = index_of[dmu[ro]];
+ c = index_of[smu[ro * num + k]];
+ tmp = a + (cw_len - b) + c;
+ a = alpha_to[tmp % cw_len];
+ smu[(i + 1) * num + (k + diff)] = a;
+ }
+
+ for (k = 0; k <= lmu[i] >> 1; k++)
+ smu[(i + 1) * num + k] ^= smu[i * num + k];
+ }
+
+ /* End Computing Sigma (Mu+1) and L(mu) */
+ /* In either case compute delta */
+ delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
+
+ /* Do not compute discrepancy for the last iteration */
+ if (i >= strength)
+ continue;
+
+ for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
+ tmp = 2 * (i - 1);
+ if (k == 0) {
+ dmu[i + 1] = si[tmp + 3];
+ } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
+ s16 a, b, c;
+
+ a = index_of[smu[(i + 1) * num + k]];
+ b = si[2 * (i - 1) + 3 - k];
+ c = index_of[b];
+ tmp = a + c;
+ tmp %= cw_len;
+ dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
+ }
+ }
+ }
+}
+
+static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
+{
+ int sector_size = get_sectorsize(user);
+ int degree = sector_size == 512 ? 13 : 14;
+ struct atmel_pmecc *pmecc = user->pmecc;
+ int strength = get_strength(user);
+ int ret, roots_nbr, i, err_nbr = 0;
+ int num = (2 * strength) + 1;
+ s16 *smu = user->smu;
+ u32 val;
+
+ writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
+
+ for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
+ writel_relaxed(smu[(strength + 1) * num + i],
+ pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
+ err_nbr++;
+ }
+
+ val = (err_nbr - 1) << 16;
+ if (sector_size == 1024)
+ val |= 1;
+
+ writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
+ writel((sector_size * 8) + (degree * strength),
+ pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
+
+ ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
+ ATMEL_PMERRLOC_ELISR,
+ val, val & PMERRLOC_CALC_DONE,
+ PMECC_MAX_TIMEOUT_MS * 1000);
+ if (ret) {
+ dev_err(pmecc->dev,
+ "PMECC: Timeout to calculate error location.\n");
+ return ret;
+ }
+
+ roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
+ /* Number of roots == degree of smu hence <= cap */
+ if (roots_nbr == user->lmu[strength + 1] >> 1)
+ return err_nbr - 1;
+
+ /*
+ * Number of roots does not match the degree of smu
+ * unable to correct error.
+ */
+ return -EBADMSG;
+}
+
+int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
+ void *data, void *ecc)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ int sectorsize = get_sectorsize(user);
+ int eccbytes = user->eccbytes;
+ int i, nerrors;
+
+ if (!(user->isr & BIT(sector)))
+ return 0;
+
+ atmel_pmecc_gen_syndrome(user, sector);
+ atmel_pmecc_substitute(user);
+ atmel_pmecc_get_sigma(user);
+
+ nerrors = atmel_pmecc_err_location(user);
+ if (nerrors < 0)
+ return nerrors;
+
+ for (i = 0; i < nerrors; i++) {
+ const char *area;
+ int byte, bit;
+ u32 errpos;
+ u8 *ptr;
+
+ errpos = readl_relaxed(pmecc->regs.errloc +
+ ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
+ errpos--;
+
+ byte = errpos / 8;
+ bit = errpos % 8;
+
+ if (byte < sectorsize) {
+ ptr = data + byte;
+ area = "data";
+ } else if (byte < sectorsize + eccbytes) {
+ ptr = ecc + byte - sectorsize;
+ area = "ECC";
+ } else {
+ dev_dbg(pmecc->dev,
+ "Invalid errpos value (%d, max is %d)\n",
+ errpos, (sectorsize + eccbytes) * 8);
+ return -EINVAL;
+ }
+
+ dev_dbg(pmecc->dev,
+ "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
+ area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
+
+ *ptr ^= BIT(bit);
+ }
+
+ return nerrors;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
+
+bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
+{
+ return user->pmecc->caps->correct_erased_chunks;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
+
+void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
+ int sector, void *ecc)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ u8 *ptr = ecc;
+ int i;
+
+ for (i = 0; i < user->eccbytes; i++)
+ ptr[i] = readb_relaxed(pmecc->regs.base +
+ ATMEL_PMECC_ECC(sector, i));
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
+
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
+{
+ writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+ writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
+
+int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ u32 cfg;
+
+ if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
+ dev_err(pmecc->dev, "Bad ECC operation!");
+ return -EINVAL;
+ }
+
+ mutex_lock(&user->pmecc->lock);
+
+ cfg = user->cache.cfg;
+ if (op == NAND_ECC_WRITE)
+ cfg |= PMECC_CFG_WRITE_OP;
+ else
+ cfg |= PMECC_CFG_AUTO_ENABLE;
+
+ writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
+ writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
+ writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
+ writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
+
+ writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+ writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
+
+void atmel_pmecc_disable(struct atmel_pmecc_user *user)
+{
+ atmel_pmecc_reset(user->pmecc);
+ mutex_unlock(&user->pmecc->lock);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
+
+int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ u32 status;
+ int ret;
+
+ ret = readl_relaxed_poll_timeout(pmecc->regs.base +
+ ATMEL_PMECC_SR,
+ status, !(status & PMECC_SR_BUSY),
+ PMECC_MAX_TIMEOUT_MS * 1000);
+ if (ret) {
+ dev_err(pmecc->dev,
+ "Timeout while waiting for PMECC ready.\n");
+ return ret;
+ }
+
+ user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
+
+static struct atmel_pmecc *atmel_pmecc_create(struct device *dev,
+ const struct atmel_pmecc_caps *caps,
+ int pmecc_res_idx, int errloc_res_idx)
+{
+ struct atmel_pmecc *pmecc;
+
+ pmecc = kzalloc(sizeof(*pmecc), GFP_KERNEL);
+ if (!pmecc)
+ return ERR_PTR(-ENOMEM);
+
+ pmecc->caps = caps;
+ pmecc->dev = dev;
+ mutex_init(&pmecc->lock);
+
+ pmecc->regs.base = dev_request_mem_region_err_null(dev, pmecc_res_idx);
+ if (!pmecc->regs.base)
+ return ERR_PTR(-EINVAL);
+
+ pmecc->regs.errloc = dev_request_mem_region_err_null(dev, errloc_res_idx);
+ if (!pmecc->regs.errloc)
+ return ERR_PTR(-EINVAL);
+
+ /* Disable all interrupts before registering the PMECC handler. */
+ writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
+ atmel_pmecc_reset(pmecc);
+
+ return pmecc;
+}
+
+static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
+ struct device_node *np)
+{
+ struct device *dev;
+ struct atmel_pmecc *pmecc;
+ int ret;
+
+ dev = of_find_device_by_node(np);
+ if (!dev)
+ return ERR_PTR(-EPROBE_DEFER);
+ pmecc = dev->priv;
+ if (!pmecc) {
+ ret = -EPROBE_DEFER;
+ goto err_put_device;
+ }
+
+ return pmecc;
+
+err_put_device:
+ put_device(dev);
+ return ERR_PTR(ret);
+}
+
+static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
+
+static struct atmel_pmecc_caps at91sam9g45_caps = {
+ .strengths = atmel_pmecc_strengths,
+ .nstrengths = 5,
+ .el_offset = 0x8c,
+};
+
+static struct atmel_pmecc_caps sama5d4_caps = {
+ .strengths = atmel_pmecc_strengths,
+ .nstrengths = 5,
+ .el_offset = 0x8c,
+ .correct_erased_chunks = true,
+};
+
+static struct atmel_pmecc_caps sama5d2_caps = {
+ .strengths = atmel_pmecc_strengths,
+ .nstrengths = 6,
+ .el_offset = 0xac,
+ .correct_erased_chunks = true,
+};
+
+static const struct of_device_id __maybe_unused atmel_pmecc_legacy_match[] = {
+ { .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
+ { .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pmecc_legacy_match);
+
+struct atmel_pmecc *dev_atmel_pmecc_get(struct device *userdev)
+{
+ struct atmel_pmecc *pmecc;
+ struct device_node *np;
+
+ if (!userdev)
+ return ERR_PTR(-EINVAL);
+
+ if (!userdev->of_node)
+ return NULL;
+
+ np = of_parse_phandle(userdev->of_node, "ecc-engine", 0);
+ if (np) {
+ pmecc = atmel_pmecc_get_by_node(userdev, np);
+ of_node_put(np);
+ } else {
+ /*
+ * Support old DT bindings: in this case the PMECC iomem
+ * resources are directly defined in the user dev at position
+ * 1 and 2. Extract all relevant information from there.
+ */
+ struct device *dev = userdev;
+ const struct atmel_pmecc_caps *caps;
+ const struct of_device_id *match;
+
+ /* No PMECC engine available. */
+ if (!of_property_read_bool(userdev->of_node,
+ "atmel,has-pmecc"))
+ return NULL;
+
+ caps = &at91sam9g45_caps;
+
+ /* Find the caps associated to the NAND dev node. */
+ match = of_match_node(atmel_pmecc_legacy_match,
+ userdev->of_node);
+ if (match && match->data)
+ caps = match->data;
+
+ pmecc = atmel_pmecc_create(dev, caps, 1, 2);
+ }
+
+ return pmecc;
+}
+EXPORT_SYMBOL(dev_atmel_pmecc_get);
+
+static const struct of_device_id atmel_pmecc_match[] = {
+ { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps },
+ { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps },
+ { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pmecc_match);
+
+static int atmel_pmecc_probe(struct device *dev)
+{
+ const struct atmel_pmecc_caps *caps;
+ struct atmel_pmecc *pmecc;
+
+ caps = of_device_get_match_data(dev);
+ if (!caps) {
+ dev_err(dev, "Invalid caps\n");
+ return -EINVAL;
+ }
+
+ pmecc = atmel_pmecc_create(dev, caps, 0, 1);
+ if (IS_ERR(pmecc))
+ return PTR_ERR(pmecc);
+
+ dev->priv = pmecc;
+
+ return 0;
+}
+
+static struct driver atmel_pmecc_driver = {
+ .name = "atmel-pmecc",
+ .of_match_table = atmel_pmecc_match,
+ .probe = atmel_pmecc_probe,
+};
+device_platform_driver(atmel_pmecc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("PMECC engine driver");
+MODULE_ALIAS("platform:atmel_pmecc");
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h
new file mode 100644
index 0000000000..6178a35e9d
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/pmecc.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * © Copyright 2016 ATMEL
+ * © Copyright 2016 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ * Copyright © 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
+ * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
+ * Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * © Copyright 2012 ATMEL, Hong Xu
+ *
+ * Add Nand Flash Controller support for SAMA5 SoC
+ * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
+ */
+
+#ifndef ATMEL_PMECC_H
+#define ATMEL_PMECC_H
+
+#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0
+#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0
+#define ATMEL_PMECC_OOBOFFSET_AUTO -1
+
+struct atmel_pmecc_user_req {
+ int pagesize;
+ int oobsize;
+ struct {
+ int strength;
+ int bytes;
+ int sectorsize;
+ int nsectors;
+ int ooboffset;
+ } ecc;
+};
+
+struct atmel_pmecc *dev_atmel_pmecc_get(struct device *dev);
+
+struct atmel_pmecc_user *
+atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
+ struct atmel_pmecc_user_req *req);
+void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
+
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc);
+int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
+void atmel_pmecc_disable(struct atmel_pmecc_user *user);
+int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
+int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
+ void *data, void *ecc);
+bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
+void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
+ int sector, void *ecc);
+
+#endif /* ATMEL_PMECC_H */
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/raw/denali.h
index 7a721ffbbc..ed489d010b 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
@@ -356,7 +356,7 @@ struct denali_chip {
*/
struct denali_controller {
struct nand_controller controller;
- struct device_d *dev;
+ struct device *dev;
struct list_head chips;
unsigned long clk_rate;
unsigned long clk_x_rate;
diff --git a/drivers/mtd/nand/fsl_ifc.h b/drivers/mtd/nand/raw/fsl_ifc.h
index 4c89f569f5..4c89f569f5 100644
--- a/drivers/mtd/nand/fsl_ifc.h
+++ b/drivers/mtd/nand/raw/fsl_ifc.h
diff --git a/drivers/mtd/nand/internals.h b/drivers/mtd/nand/raw/internals.h
index e6b2282f2c..6dab25ecab 100644
--- a/drivers/mtd/nand/internals.h
+++ b/drivers/mtd/nand/raw/internals.h
@@ -90,9 +90,14 @@ void onfi_fill_interface_config(struct nand_chip *chip,
unsigned int timing_mode);
unsigned int
onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings);
+unsigned int
+onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings);
int nand_choose_best_sdr_timings(struct nand_chip *chip,
struct nand_interface_config *iface,
struct nand_sdr_timings *spec_timings);
+int nand_choose_best_nvddr_timings(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ struct nand_nvddr_timings *spec_timings);
const struct nand_interface_config *nand_get_reset_interface_config(void);
int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
new file mode 100644
index 0000000000..2774b6bb4f
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxc_nand.c
@@ -0,0 +1,1750 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
+ */
+
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+
+#define DRIVER_NAME "mxc_nand"
+
+/* Addresses for NFC registers */
+#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00)
+#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04)
+#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06)
+#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08)
+#define NFC_V1_V2_CONFIG (host->regs + 0x0a)
+#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c)
+#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e)
+#define NFC_V21_RSLTSPARE_AREA (host->regs + 0x10)
+#define NFC_V1_V2_WRPROT (host->regs + 0x12)
+#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14)
+#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16)
+#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20)
+#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24)
+#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28)
+#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c)
+#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22)
+#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26)
+#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a)
+#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e)
+#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18)
+#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a)
+#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c)
+
+#define NFC_V1_V2_ECC_STATUS_RESULT_ERM GENMASK(3, 2)
+
+#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0)
+#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2)
+#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3)
+#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4)
+#define NFC_V1_V2_CONFIG1_BIG (1 << 5)
+#define NFC_V1_V2_CONFIG1_RST (1 << 6)
+#define NFC_V1_V2_CONFIG1_CE (1 << 7)
+#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8)
+#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9)
+#define NFC_V2_CONFIG1_FP_INT (1 << 11)
+
+#define NFC_V1_V2_CONFIG2_INT (1 << 15)
+
+/*
+ * Operation modes for the NFC. Valid for v1, v2 and v3
+ * type controllers.
+ */
+#define NFC_CMD (1 << 0)
+#define NFC_ADDR (1 << 1)
+#define NFC_INPUT (1 << 2)
+#define NFC_OUTPUT (1 << 3)
+#define NFC_ID (1 << 4)
+#define NFC_STATUS (1 << 5)
+
+#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00)
+#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04)
+
+#define NFC_V3_CONFIG1 (host->regs_axi + 0x34)
+#define NFC_V3_CONFIG1_SP_EN (1 << 0)
+#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4)
+
+#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38)
+
+#define NFC_V3_LAUNCH (host->regs_axi + 0x40)
+
+#define NFC_V3_WRPROT (host->regs_ip + 0x0)
+#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0)
+#define NFC_V3_WRPROT_LOCK (1 << 1)
+#define NFC_V3_WRPROT_UNLOCK (1 << 2)
+#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6)
+
+#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04)
+
+#define NFC_V3_CONFIG2 (host->regs_ip + 0x24)
+#define NFC_V3_CONFIG2_PS_512 (0 << 0)
+#define NFC_V3_CONFIG2_PS_2048 (1 << 0)
+#define NFC_V3_CONFIG2_PS_4096 (2 << 0)
+#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2)
+#define NFC_V3_CONFIG2_ECC_EN (1 << 3)
+#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
+#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5)
+#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
+#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift)
+#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12)
+#define NFC_V3_CONFIG2_INT_MSK (1 << 15)
+#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
+#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16)
+
+#define NFC_V3_CONFIG3 (host->regs_ip + 0x28)
+#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0)
+#define NFC_V3_CONFIG3_FW8 (1 << 3)
+#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8)
+#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12)
+#define NFC_V3_CONFIG3_RBB_MODE (1 << 15)
+#define NFC_V3_CONFIG3_NO_SDMA (1 << 20)
+
+#define NFC_V3_IPC (host->regs_ip + 0x2C)
+#define NFC_V3_IPC_CREQ (1 << 0)
+#define NFC_V3_IPC_INT (1 << 31)
+
+#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34)
+
+struct mxc_nand_host;
+
+struct mxc_nand_devtype_data {
+ void (*preset)(struct mtd_info *);
+ int (*read_page)(struct nand_chip *chip);
+ void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
+ void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
+ void (*send_page)(struct mtd_info *, unsigned int);
+ void (*send_read_id)(struct mxc_nand_host *);
+ uint16_t (*get_dev_status)(struct mxc_nand_host *);
+ int (*check_int)(struct mxc_nand_host *);
+ void (*irq_control)(struct mxc_nand_host *, int);
+ u32 (*get_ecc_status)(struct nand_chip *);
+ const struct mtd_ooblayout_ops *ooblayout;
+ void (*select_chip)(struct nand_chip *chip, int cs);
+ int (*setup_interface)(struct nand_chip *chip, int csline,
+ const struct nand_interface_config *conf);
+ void (*enable_hwecc)(struct nand_chip *chip, bool enable);
+
+ /*
+ * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
+ * (CONFIG1:INT_MSK is set). To handle this the driver uses
+ * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK
+ */
+ int irqpending_quirk;
+ int needs_ip;
+
+ size_t regs_offset;
+ size_t spare0_offset;
+ size_t axi_offset;
+
+ int spare_len;
+ int eccbytes;
+ int eccsize;
+ int ppb_shift;
+};
+
+struct mxc_nand_host {
+ struct nand_chip nand;
+ struct device *dev;
+
+ void __iomem *spare0;
+ void __iomem *main_area0;
+
+ void __iomem *base;
+ void __iomem *regs;
+ void __iomem *regs_axi;
+ void __iomem *regs_ip;
+ int status_request;
+ struct clk *clk;
+ int clk_act;
+ int irq;
+ int eccsize;
+ int used_oobsize;
+ int active_cs;
+ unsigned int ecc_stats_v1;
+
+ struct completion op_completion;
+
+ void *data_buf;
+
+ const struct mxc_nand_devtype_data *devtype_data;
+};
+
+static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
+{
+ int i;
+ u32 *t = trg;
+ const __iomem u32 *s = src;
+
+ for (i = 0; i < (size >> 2); i++)
+ *t++ = __raw_readl(s++);
+}
+
+static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size)
+{
+ int i;
+ u16 *t = trg;
+ const __iomem u16 *s = src;
+
+ /* We assume that src (IO) is always 32bit aligned */
+ if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
+ memcpy32_fromio(trg, src, size);
+ return;
+ }
+
+ for (i = 0; i < (size >> 1); i++)
+ *t++ = __raw_readw(s++);
+}
+
+static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
+{
+ int i;
+ u32 __iomem *t = trg;
+ const u32 *s = src;
+
+ for (i = 0; i < (size >> 2); i++)
+ __raw_writel(*s++, t++);
+}
+
+static void memcpy16_toio(void __iomem *trg, const void *src, int size)
+{
+ int i;
+ __iomem u16 *t = trg;
+ const u16 *s = src;
+
+ /* We assume that trg (IO) is always 32bit aligned */
+ if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
+ memcpy32_toio(trg, src, size);
+ return;
+ }
+
+ for (i = 0; i < (size >> 1); i++)
+ __raw_writew(*s++, t++);
+}
+
+/*
+ * The controller splits a page into data chunks of 512 bytes + partial oob.
+ * There are writesize / 512 such chunks, the size of the partial oob parts is
+ * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
+ * contains additionally the byte lost by rounding (if any).
+ * This function handles the needed shuffling between host->data_buf (which
+ * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
+ * spare) and the NFC buffer.
+ */
+static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(this);
+ u16 i, oob_chunk_size;
+ u16 num_chunks = mtd->writesize / 512;
+
+ u8 *d = buf;
+ u8 __iomem *s = host->spare0;
+ u16 sparebuf_size = host->devtype_data->spare_len;
+
+ /* size of oob chunk for all but possibly the last one */
+ oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
+
+ if (bfrom) {
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ host->used_oobsize - i * oob_chunk_size);
+ } else {
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ host->used_oobsize - i * oob_chunk_size);
+ }
+}
+
+static int check_int_v3(struct mxc_nand_host *host)
+{
+ uint32_t tmp;
+
+ tmp = readl(NFC_V3_IPC);
+ if (!(tmp & NFC_V3_IPC_INT))
+ return 0;
+
+ tmp &= ~NFC_V3_IPC_INT;
+ writel(tmp, NFC_V3_IPC);
+
+ return 1;
+}
+
+static int check_int_v1_v2(struct mxc_nand_host *host)
+{
+ uint32_t tmp;
+
+ tmp = readw(NFC_V1_V2_CONFIG2);
+ if (!(tmp & NFC_V1_V2_CONFIG2_INT))
+ return 0;
+
+ if (!host->devtype_data->irqpending_quirk)
+ writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
+
+ return 1;
+}
+
+static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
+{
+ uint16_t tmp;
+
+ tmp = readw(NFC_V1_V2_CONFIG1);
+
+ if (activate)
+ tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK;
+ else
+ tmp |= NFC_V1_V2_CONFIG1_INT_MSK;
+
+ writew(tmp, NFC_V1_V2_CONFIG1);
+}
+
+static void irq_control_v3(struct mxc_nand_host *host, int activate)
+{
+ uint32_t tmp;
+
+ tmp = readl(NFC_V3_CONFIG2);
+
+ if (activate)
+ tmp &= ~NFC_V3_CONFIG2_INT_MSK;
+ else
+ tmp |= NFC_V3_CONFIG2_INT_MSK;
+
+ writel(tmp, NFC_V3_CONFIG2);
+}
+
+static u32 get_ecc_status_v1(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ unsigned int ecc_stats, max_bitflips = 0;
+ int no_subpages, i;
+
+ no_subpages = mtd->writesize >> 9;
+
+ ecc_stats = host->ecc_stats_v1;
+
+ for (i = 0; i < no_subpages; i++) {
+ switch (ecc_stats & 0x3) {
+ case 0:
+ default:
+ break;
+ case 1:
+ mtd->ecc_stats.corrected++;
+ max_bitflips = 1;
+ break;
+ case 2:
+ mtd->ecc_stats.failed++;
+ break;
+ }
+
+ ecc_stats >>= 2;
+ }
+
+ return max_bitflips;
+}
+
+static u32 get_ecc_status_v2_v3(struct nand_chip *chip, unsigned int ecc_stat)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ u8 ecc_bit_mask, err_limit;
+ unsigned int max_bitflips = 0;
+ int no_subpages, err;
+
+ ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
+ err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
+
+ no_subpages = mtd->writesize >> 9;
+
+ do {
+ err = ecc_stat & ecc_bit_mask;
+ if (err > err_limit) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += err;
+ max_bitflips = max_t(unsigned int, max_bitflips, err);
+ }
+
+ ecc_stat >>= 4;
+ } while (--no_subpages);
+
+ return max_bitflips;
+}
+
+static u32 get_ecc_status_v2(struct nand_chip *chip)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ u32 ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
+
+ return get_ecc_status_v2_v3(chip, ecc_stat);
+}
+
+static u32 get_ecc_status_v3(struct nand_chip *chip)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ u32 ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT);
+
+ return get_ecc_status_v2_v3(chip, ecc_stat);
+}
+
+/* This function polls the NANDFC to wait for the basic operation to
+ * complete by checking the INT bit of config2 register.
+ */
+static int wait_op_done(struct mxc_nand_host *host, int useirq)
+{
+ int ret = 0;
+ int max_retries = 8000;
+ int done;
+
+ /*
+ * If operation is already complete, don't bother to setup an irq or a
+ * loop.
+ */
+ if (host->devtype_data->check_int(host))
+ return 0;
+
+ do {
+ udelay(1);
+
+ done = host->devtype_data->check_int(host);
+ if (done)
+ break;
+
+ } while (--max_retries);
+
+ if (!done) {
+ dev_dbg(host->dev, "timeout polling for completion\n");
+ ret = -ETIMEDOUT;
+ }
+
+ WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq);
+
+ return ret;
+}
+
+static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
+{
+ /* fill command */
+ writel(cmd, NFC_V3_FLASH_CMD);
+
+ /* send out command */
+ writel(NFC_CMD, NFC_V3_LAUNCH);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, useirq);
+}
+
+/* This function issues the specified command to the NAND device and
+ * waits for completion. */
+static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
+{
+ dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq);
+
+ writew(cmd, NFC_V1_V2_FLASH_CMD);
+ writew(NFC_CMD, NFC_V1_V2_CONFIG2);
+
+ if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) {
+ int max_retries = 100;
+ /* Reset completion is indicated by NFC_CONFIG2 */
+ /* being set to 0 */
+ while (max_retries-- > 0) {
+ if (readw(NFC_V1_V2_CONFIG2) == 0) {
+ break;
+ }
+ udelay(1);
+ }
+ if (max_retries < 0)
+ dev_dbg(host->dev, "%s: RESET failed\n", __func__);
+ } else {
+ /* Wait for operation to complete */
+ wait_op_done(host, useirq);
+ }
+}
+
+static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
+{
+ /* fill address */
+ writel(addr, NFC_V3_FLASH_ADDR0);
+
+ /* send out address */
+ writel(NFC_ADDR, NFC_V3_LAUNCH);
+
+ wait_op_done(host, 0);
+}
+
+/* This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command. */
+static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast)
+{
+ dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast);
+
+ writew(addr, NFC_V1_V2_FLASH_ADDR);
+ writew(NFC_ADDR, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, islast);
+}
+
+static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint32_t tmp;
+
+ tmp = readl(NFC_V3_CONFIG1);
+ tmp &= ~(7 << 4);
+ writel(tmp, NFC_V3_CONFIG1);
+
+ /* transfer data from NFC ram to nand */
+ writel(ops, NFC_V3_LAUNCH);
+
+ wait_op_done(host, false);
+}
+
+static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ /* NANDFC buffer 0 is used for page read/write */
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+
+ writew(ops, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+}
+
+static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int bufs, i;
+
+ if (mtd->writesize > 512)
+ bufs = 4;
+ else
+ bufs = 1;
+
+ for (i = 0; i < bufs; i++) {
+
+ /* NANDFC buffer 0 is used for page read/write */
+ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
+
+ writew(ops, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+ }
+}
+
+static void send_read_id_v3(struct mxc_nand_host *host)
+{
+ /* Read ID into main buffer */
+ writel(NFC_ID, NFC_V3_LAUNCH);
+
+ wait_op_done(host, true);
+
+ memcpy32_fromio(host->data_buf, host->main_area0, 16);
+}
+
+/* Request the NANDFC to perform a read of the NAND device ID. */
+static void send_read_id_v1_v2(struct mxc_nand_host *host)
+{
+ /* NANDFC buffer 0 is used for device ID output */
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+
+ writew(NFC_ID, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+
+ memcpy32_fromio(host->data_buf, host->main_area0, 16);
+}
+
+static uint16_t get_dev_status_v3(struct mxc_nand_host *host)
+{
+ writew(NFC_STATUS, NFC_V3_LAUNCH);
+ wait_op_done(host, true);
+
+ return readl(NFC_V3_CONFIG1) >> 16;
+}
+
+/* This function requests the NANDFC to perform a read of the
+ * NAND device status and returns the current status. */
+static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
+{
+ void __iomem *main_buf = host->main_area0;
+ uint32_t store;
+ uint16_t ret;
+
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+
+ /*
+ * The device status is stored in main_area0. To
+ * prevent corruption of the buffer save the value
+ * and restore it afterwards.
+ */
+ store = readl(main_buf);
+
+ writew(NFC_STATUS, NFC_V1_V2_CONFIG2);
+ wait_op_done(host, true);
+
+ ret = readw(main_buf);
+
+ writel(store, main_buf);
+
+ return ret;
+}
+
+static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint16_t config1;
+
+ if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
+ return;
+
+ config1 = readw(NFC_V1_V2_CONFIG1);
+
+ if (enable)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+ else
+ config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+}
+
+static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint32_t config2;
+
+ if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
+ return;
+
+ config2 = readl(NFC_V3_CONFIG2);
+
+ if (enable)
+ config2 |= NFC_V3_CONFIG2_ECC_EN;
+ else
+ config2 &= ~NFC_V3_CONFIG2_ECC_EN;
+
+ writel(config2, NFC_V3_CONFIG2);
+}
+
+static int mxc_nand_read_page_v1(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int no_subpages;
+ int i;
+ unsigned int ecc_stats = 0;
+
+ no_subpages = mtd->writesize >> 9;
+
+ for (i = 0; i < no_subpages; i++) {
+ /* NANDFC buffer 0 is used for page read/write */
+ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
+
+ writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+
+ ecc_stats |= FIELD_GET(NFC_V1_V2_ECC_STATUS_RESULT_ERM,
+ readw(NFC_V1_V2_ECC_STATUS_RESULT)) << i * 2;
+ }
+
+ host->ecc_stats_v1 = ecc_stats;
+
+ return 0;
+}
+
+static int mxc_nand_read_page_v2_v3(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ host->devtype_data->send_page(mtd, NFC_OUTPUT);
+
+ return 0;
+}
+
+static int mxc_nand_read_page(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int ret;
+
+ host->devtype_data->enable_hwecc(chip, true);
+
+ ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required)
+ copy_spare(mtd, true, chip->oob_poi);
+
+ return host->devtype_data->get_ecc_status(chip);
+}
+
+static int mxc_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int ret;
+
+ host->devtype_data->enable_hwecc(chip, false);
+
+ ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required)
+ copy_spare(mtd, true, chip->oob_poi);
+
+ return 0;
+}
+
+static int mxc_nand_read_oob(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int ret;
+
+ host->devtype_data->enable_hwecc(chip, false);
+
+ ret = nand_read_page_op(chip, page, 0, host->data_buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ copy_spare(mtd, true, chip->oob_poi);
+
+ return 0;
+}
+
+static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf,
+ bool ecc, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ host->devtype_data->enable_hwecc(chip, ecc);
+
+ return nand_prog_page_op(chip, page, 0, buf, mtd->writesize);
+}
+
+static int mxc_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ return mxc_nand_write_page(chip, buf, true, page);
+}
+
+static int mxc_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ return mxc_nand_write_page(chip, buf, false, page);
+}
+
+static int mxc_nand_write_oob(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ memset(host->data_buf, 0xff, mtd->writesize);
+
+ return mxc_nand_write_page(chip, host->data_buf, false, page);
+}
+
+/* This function is used by upper layer for select and
+ * deselect of the NAND chip */
+static void mxc_nand_select_chip_v1_v3(struct nand_chip *nand_chip, int chip)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ if (chip == -1) {
+ /* Disable the NFC clock */
+ if (host->clk_act) {
+ clk_disable_unprepare(host->clk);
+ host->clk_act = 0;
+ }
+ return;
+ }
+
+ if (!host->clk_act) {
+ /* Enable the NFC clock */
+ clk_prepare_enable(host->clk);
+ host->clk_act = 1;
+ }
+}
+
+static void mxc_nand_select_chip_v2(struct nand_chip *nand_chip, int chip)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ if (chip == -1) {
+ /* Disable the NFC clock */
+ if (host->clk_act) {
+ clk_disable_unprepare(host->clk);
+ host->clk_act = 0;
+ }
+ return;
+ }
+
+ if (!host->clk_act) {
+ /* Enable the NFC clock */
+ clk_prepare_enable(host->clk);
+ host->clk_act = 1;
+ }
+
+ host->active_cs = chip;
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+}
+
+#define MXC_V1_ECCBYTES 5
+
+static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+ if (section >= nand_chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 6;
+ oobregion->length = MXC_V1_ECCBYTES;
+
+ return 0;
+}
+
+static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+ if (section > nand_chip->ecc.steps)
+ return -ERANGE;
+
+ if (!section) {
+ if (mtd->writesize <= 512) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 2;
+ oobregion->length = 4;
+ }
+ } else {
+ oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
+ if (section < nand_chip->ecc.steps)
+ oobregion->length = (section * 16) + 6 -
+ oobregion->offset;
+ else
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = {
+ .ecc = mxc_v1_ooblayout_ecc,
+ .free = mxc_v1_ooblayout_free,
+};
+
+static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
+
+ if (section >= nand_chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * stepsize) + 7;
+ oobregion->length = nand_chip->ecc.bytes;
+
+ return 0;
+}
+
+static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
+
+ if (section >= nand_chip->ecc.steps)
+ return -ERANGE;
+
+ if (!section) {
+ if (mtd->writesize <= 512) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 2;
+ oobregion->length = 4;
+ }
+ } else {
+ oobregion->offset = section * stepsize;
+ oobregion->length = 7;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = {
+ .ecc = mxc_v2_ooblayout_ecc,
+ .free = mxc_v2_ooblayout_free,
+};
+
+/*
+ * v2 and v3 type controllers can do 4bit or 8bit ecc depending
+ * on how much oob the nand chip has. For 8bit ecc we need at least
+ * 26 bytes of oob data per 512 byte block.
+ */
+static int get_eccsize(struct mtd_info *mtd)
+{
+ int oobbytes_per_512 = 0;
+
+ oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize;
+
+ if (oobbytes_per_512 < 26)
+ return 4;
+ else
+ return 8;
+}
+
+static void preset_v1(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint16_t config1 = 0;
+
+ if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST &&
+ mtd->writesize)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+
+ if (!host->devtype_data->irqpending_quirk)
+ config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
+
+ host->eccsize = 1;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+ /* preset operation */
+
+ /* Unlock the internal RAM Buffer */
+ writew(0x2, NFC_V1_V2_CONFIG);
+
+ /* Blocks to be unlocked */
+ writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
+ writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
+
+ /* Unlock Block Command for given address range */
+ writew(0x4, NFC_V1_V2_WRPROT);
+}
+
+static int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline,
+ const struct nand_interface_config *conf)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int tRC_min_ns, tRC_ps, ret;
+ unsigned long rate, rate_round;
+ const struct nand_sdr_timings *timings;
+ u16 config1;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return -ENOTSUPP;
+
+ config1 = readw(NFC_V1_V2_CONFIG1);
+
+ tRC_min_ns = timings->tRC_min / 1000;
+ rate = 1000000000 / tRC_min_ns;
+
+ /*
+ * For tRC < 30ns we have to use EDO mode. In this case the controller
+ * does one access per clock cycle. Otherwise the controller does one
+ * access in two clock cycles, thus we have to double the rate to the
+ * controller.
+ */
+ if (tRC_min_ns < 30) {
+ rate_round = clk_round_rate(host->clk, rate);
+ config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
+ tRC_ps = 1000000000 / (rate_round / 1000);
+ } else {
+ rate *= 2;
+ rate_round = clk_round_rate(host->clk, rate);
+ config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
+ tRC_ps = 1000000000 / (rate_round / 1000 / 2);
+ }
+
+ /*
+ * The timing values compared against are from the i.MX25 Automotive
+ * datasheet, Table 50. NFC Timing Parameters
+ */
+ if (timings->tCLS_min > tRC_ps - 1000 ||
+ timings->tCLH_min > tRC_ps - 2000 ||
+ timings->tCS_min > tRC_ps - 1000 ||
+ timings->tCH_min > tRC_ps - 2000 ||
+ timings->tWP_min > tRC_ps - 1500 ||
+ timings->tALS_min > tRC_ps ||
+ timings->tALH_min > tRC_ps - 3000 ||
+ timings->tDS_min > tRC_ps ||
+ timings->tDH_min > tRC_ps - 5000 ||
+ timings->tWC_min > 2 * tRC_ps ||
+ timings->tWH_min > tRC_ps - 2500 ||
+ timings->tRR_min > 6 * tRC_ps ||
+ timings->tRP_min > 3 * tRC_ps / 2 ||
+ timings->tRC_min > 2 * tRC_ps ||
+ timings->tREH_min > (tRC_ps / 2) - 2500) {
+ dev_dbg(host->dev, "Timing out of bounds\n");
+ return -EINVAL;
+ }
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ ret = clk_set_rate(host->clk, rate);
+ if (ret)
+ return ret;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+
+ dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
+ config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
+ "normal");
+
+ return 0;
+}
+
+static void preset_v2(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint16_t config1 = 0;
+
+ config1 |= NFC_V2_CONFIG1_FP_INT;
+
+ if (!host->devtype_data->irqpending_quirk)
+ config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
+
+ if (mtd->writesize) {
+ uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
+
+ if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+
+ host->eccsize = get_eccsize(mtd);
+ if (host->eccsize == 4)
+ config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
+
+ config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6);
+ } else {
+ host->eccsize = 1;
+ }
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+ /* preset operation */
+
+ /* spare area size in 16-bit half-words */
+ writew(mtd->oobsize / 2, NFC_V21_RSLTSPARE_AREA);
+
+ /* Unlock the internal RAM Buffer */
+ writew(0x2, NFC_V1_V2_CONFIG);
+
+ /* Blocks to be unlocked */
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
+
+ /* Unlock Block Command for given address range */
+ writew(0x4, NFC_V1_V2_WRPROT);
+}
+
+static void preset_v3(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint32_t config2, config3;
+ int i, addr_phases;
+
+ writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
+ writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);
+
+ /* Unlock the internal RAM Buffer */
+ writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
+ NFC_V3_WRPROT);
+
+ /* Blocks to be unlocked */
+ for (i = 0; i < NAND_MAX_CHIPS; i++)
+ writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
+
+ writel(0, NFC_V3_IPC);
+
+ config2 = NFC_V3_CONFIG2_ONE_CYCLE |
+ NFC_V3_CONFIG2_2CMD_PHASES |
+ NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
+ NFC_V3_CONFIG2_ST_CMD(0x70) |
+ NFC_V3_CONFIG2_INT_MSK |
+ NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
+
+ addr_phases = fls(chip->pagemask) >> 3;
+
+ if (mtd->writesize == 2048) {
+ config2 |= NFC_V3_CONFIG2_PS_2048;
+ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
+ } else if (mtd->writesize == 4096) {
+ config2 |= NFC_V3_CONFIG2_PS_4096;
+ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
+ } else {
+ config2 |= NFC_V3_CONFIG2_PS_512;
+ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
+ }
+
+ if (mtd->writesize) {
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
+ config2 |= NFC_V3_CONFIG2_ECC_EN;
+
+ config2 |= NFC_V3_CONFIG2_PPB(
+ ffs(mtd->erasesize / mtd->writesize) - 6,
+ host->devtype_data->ppb_shift);
+ host->eccsize = get_eccsize(mtd);
+ if (host->eccsize == 8)
+ config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
+ }
+
+ writel(config2, NFC_V3_CONFIG2);
+
+ config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
+ NFC_V3_CONFIG3_NO_SDMA |
+ NFC_V3_CONFIG3_RBB_MODE |
+ NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+ NFC_V3_CONFIG3_ADD_OP(0);
+
+ if (!(chip->options & NAND_BUSWIDTH_16))
+ config3 |= NFC_V3_CONFIG3_FW8;
+
+ writel(config3, NFC_V3_CONFIG3);
+
+ writel(0, NFC_V3_DELAY_LINE);
+}
+
+/*
+ * The generic flash bbt descriptors overlap with our ecc
+ * hardware, so define some i.MX specific ones.
+ */
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+/* v1 + irqpending_quirk: i.MX21 */
+static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
+ .preset = preset_v1,
+ .read_page = mxc_nand_read_page_v1,
+ .send_cmd = send_cmd_v1_v2,
+ .send_addr = send_addr_v1_v2,
+ .send_page = send_page_v1,
+ .send_read_id = send_read_id_v1_v2,
+ .get_dev_status = get_dev_status_v1_v2,
+ .check_int = check_int_v1_v2,
+ .irq_control = irq_control_v1_v2,
+ .get_ecc_status = get_ecc_status_v1,
+ .ooblayout = &mxc_v1_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
+ .irqpending_quirk = 1,
+ .needs_ip = 0,
+ .regs_offset = 0xe00,
+ .spare0_offset = 0x800,
+ .spare_len = 16,
+ .eccbytes = 3,
+ .eccsize = 1,
+};
+
+/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
+static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
+ .preset = preset_v1,
+ .read_page = mxc_nand_read_page_v1,
+ .send_cmd = send_cmd_v1_v2,
+ .send_addr = send_addr_v1_v2,
+ .send_page = send_page_v1,
+ .send_read_id = send_read_id_v1_v2,
+ .get_dev_status = get_dev_status_v1_v2,
+ .check_int = check_int_v1_v2,
+ .irq_control = irq_control_v1_v2,
+ .get_ecc_status = get_ecc_status_v1,
+ .ooblayout = &mxc_v1_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
+ .irqpending_quirk = 0,
+ .needs_ip = 0,
+ .regs_offset = 0xe00,
+ .spare0_offset = 0x800,
+ .axi_offset = 0,
+ .spare_len = 16,
+ .eccbytes = 3,
+ .eccsize = 1,
+};
+
+/* v21: i.MX25, i.MX35 */
+static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
+ .preset = preset_v2,
+ .read_page = mxc_nand_read_page_v2_v3,
+ .send_cmd = send_cmd_v1_v2,
+ .send_addr = send_addr_v1_v2,
+ .send_page = send_page_v2,
+ .send_read_id = send_read_id_v1_v2,
+ .get_dev_status = get_dev_status_v1_v2,
+ .check_int = check_int_v1_v2,
+ .irq_control = irq_control_v1_v2,
+ .get_ecc_status = get_ecc_status_v2,
+ .ooblayout = &mxc_v2_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v2,
+ .setup_interface = mxc_nand_v2_setup_interface,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
+ .irqpending_quirk = 0,
+ .needs_ip = 0,
+ .regs_offset = 0x1e00,
+ .spare0_offset = 0x1000,
+ .axi_offset = 0,
+ .spare_len = 64,
+ .eccbytes = 9,
+ .eccsize = 0,
+};
+
+/* v3.2a: i.MX51 */
+static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
+ .preset = preset_v3,
+ .read_page = mxc_nand_read_page_v2_v3,
+ .send_cmd = send_cmd_v3,
+ .send_addr = send_addr_v3,
+ .send_page = send_page_v3,
+ .send_read_id = send_read_id_v3,
+ .get_dev_status = get_dev_status_v3,
+ .check_int = check_int_v3,
+ .irq_control = irq_control_v3,
+ .get_ecc_status = get_ecc_status_v3,
+ .ooblayout = &mxc_v2_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v3,
+ .irqpending_quirk = 0,
+ .needs_ip = 1,
+ .regs_offset = 0,
+ .spare0_offset = 0x1000,
+ .axi_offset = 0x1e00,
+ .spare_len = 64,
+ .eccbytes = 0,
+ .eccsize = 0,
+ .ppb_shift = 7,
+};
+
+/* v3.2b: i.MX53 */
+static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
+ .preset = preset_v3,
+ .read_page = mxc_nand_read_page_v2_v3,
+ .send_cmd = send_cmd_v3,
+ .send_addr = send_addr_v3,
+ .send_page = send_page_v3,
+ .send_read_id = send_read_id_v3,
+ .get_dev_status = get_dev_status_v3,
+ .check_int = check_int_v3,
+ .irq_control = irq_control_v3,
+ .get_ecc_status = get_ecc_status_v3,
+ .ooblayout = &mxc_v2_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v3,
+ .irqpending_quirk = 0,
+ .needs_ip = 1,
+ .regs_offset = 0,
+ .spare0_offset = 0x1000,
+ .axi_offset = 0x1e00,
+ .spare_len = 64,
+ .eccbytes = 0,
+ .eccsize = 0,
+ .ppb_shift = 8,
+};
+
+static inline int is_imx21_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx21_nand_devtype_data;
+}
+
+static inline int is_imx27_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx27_nand_devtype_data;
+}
+
+static inline int is_imx25_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx25_nand_devtype_data;
+}
+
+static const struct of_device_id mxcnd_dt_ids[] = {
+ { .compatible = "fsl,imx21-nand", .data = &imx21_nand_devtype_data, },
+ { .compatible = "fsl,imx27-nand", .data = &imx27_nand_devtype_data, },
+ { .compatible = "fsl,imx25-nand", .data = &imx25_nand_devtype_data, },
+ { .compatible = "fsl,imx51-nand", .data = &imx51_nand_devtype_data, },
+ { .compatible = "fsl,imx53-nand", .data = &imx53_nand_devtype_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
+
+static int mxcnd_attach_chip(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ struct device *dev = mtd->dev.parent;
+
+ chip->ecc.bytes = host->devtype_data->eccbytes;
+ host->eccsize = host->devtype_data->eccsize;
+ chip->ecc.size = 512;
+
+ chip->ecc.read_oob = mxc_nand_read_oob;
+ chip->ecc.read_page_raw = mxc_nand_read_page_raw;
+ chip->ecc.write_page_raw = mxc_nand_write_page_raw;
+
+ switch (chip->ecc.engine_type) {
+ case NAND_ECC_ENGINE_TYPE_ON_HOST:
+ mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
+ chip->ecc.read_page = mxc_nand_read_page;
+ chip->ecc.write_page = mxc_nand_write_page_ecc;
+ chip->ecc.write_oob = mxc_nand_write_oob;
+ break;
+
+ case NAND_ECC_ENGINE_TYPE_SOFT:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+ chip->bbt_td = &bbt_main_descr;
+ chip->bbt_md = &bbt_mirror_descr;
+ }
+
+ /* Allocate the right size buffer now */
+ devm_kfree(dev, (void *)host->data_buf);
+ host->data_buf = devm_kzalloc(dev, mtd->writesize + mtd->oobsize,
+ GFP_KERNEL);
+ if (!host->data_buf)
+ return -ENOMEM;
+
+ /* Call preset again, with correct writesize chip time */
+ host->devtype_data->preset(mtd);
+
+ if (!chip->ecc.bytes) {
+ if (host->eccsize == 8)
+ chip->ecc.bytes = 18;
+ else if (host->eccsize == 4)
+ chip->ecc.bytes = 9;
+ }
+
+ /*
+ * Experimentation shows that i.MX NFC can only handle up to 218 oob
+ * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
+ * into copying invalid data to/from the spare IO buffer, as this
+ * might cause ECC data corruption when doing sub-page write to a
+ * partially written page.
+ */
+ host->used_oobsize = min(mtd->oobsize, 218U);
+
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
+ if (is_imx21_nfc(host) || is_imx27_nfc(host))
+ chip->ecc.strength = 1;
+ else
+ chip->ecc.strength = (host->eccsize == 4) ? 4 : 8;
+ }
+
+ return 0;
+}
+
+static int mxcnd_setup_interface(struct nand_chip *chip, int chipnr,
+ const struct nand_interface_config *conf)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ return host->devtype_data->setup_interface(chip, chipnr, conf);
+}
+
+static int mxcnd_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, j, buf_len;
+ void *buf_read = NULL;
+ const void *buf_write = NULL;
+ const struct nand_op_instr *instr;
+ bool readid = false;
+ bool statusreq = false;
+
+ dev_dbg(host->dev, "%s: %d instructions\n", __func__, op->ninstrs);
+
+ for (i = 0; i < op->ninstrs; i++) {
+ instr = &op->instrs[i];
+
+ nand_op_trace(" ", instr);
+
+ switch (instr->type) {
+ case NAND_OP_WAITRDY_INSTR:
+ /*
+ * NFC handles R/B internally. Therefore, this function
+ * always returns status as ready.
+ */
+ break;
+ case NAND_OP_CMD_INSTR:
+ host->devtype_data->send_cmd(host, instr->ctx.cmd.opcode, true);
+
+ if (instr->ctx.cmd.opcode == NAND_CMD_READID)
+ readid = true;
+ if (instr->ctx.cmd.opcode == NAND_CMD_STATUS)
+ statusreq = true;
+
+ break;
+ case NAND_OP_ADDR_INSTR:
+ for (j = 0; j < instr->ctx.addr.naddrs; j++) {
+ bool islast = j == instr->ctx.addr.naddrs - 1;
+ host->devtype_data->send_addr(host, instr->ctx.addr.addrs[j], islast);
+ }
+ break;
+ case NAND_OP_DATA_OUT_INSTR:
+ buf_write = instr->ctx.data.buf.out;
+ buf_len = instr->ctx.data.len;
+
+ memcpy32_toio(host->main_area0, buf_write, buf_len);
+ copy_spare(mtd, false, chip->oob_poi);
+
+ host->devtype_data->send_page(mtd, NFC_INPUT);
+
+ break;
+ case NAND_OP_DATA_IN_INSTR:
+
+ buf_read = instr->ctx.data.buf.in;
+ buf_len = instr->ctx.data.len;
+
+ if (readid) {
+ host->devtype_data->send_read_id(host);
+ readid = false;
+
+ memcpy32_fromio(host->data_buf, host->main_area0, buf_len * 2);
+
+ if (chip->options & NAND_BUSWIDTH_16) {
+ u8 *bufr = buf_read;
+ u16 *bufw = host->data_buf;
+ for (j = 0; j < buf_len; j++)
+ bufr[j] = bufw[j];
+ } else {
+ memcpy(buf_read, host->data_buf, buf_len);
+ }
+ break;
+ }
+
+ if (statusreq) {
+ *(u8*)buf_read = host->devtype_data->get_dev_status(host);
+ statusreq = false;
+ break;
+ }
+
+ host->devtype_data->read_page(chip);
+
+ if (IS_ALIGNED(buf_len, 4)) {
+ memcpy32_fromio(buf_read, host->main_area0, buf_len);
+ } else {
+ memcpy32_fromio(host->data_buf, host->main_area0, mtd->writesize);
+ memcpy(buf_read, host->data_buf, buf_len);
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct nand_controller_ops mxcnd_controller_ops = {
+ .attach_chip = mxcnd_attach_chip,
+ .setup_interface = mxcnd_setup_interface,
+ .exec_op = mxcnd_exec_op,
+};
+
+/*
+ * The i.MX NAND controller has the problem that it handles the
+ * data in chunks of 512 bytes. It doesn't treat 2k NAND chips as
+ * 2048 byte data + 64 OOB, but instead:
+ *
+ * 512b data + 16b OOB +
+ * 512b data + 16b OOB +
+ * 512b data + 16b OOB +
+ * 512b data + 16b OOB
+ *
+ * This means that the factory provided bad block marker ends up
+ * in the page data at offset 2000 instead of in the OOB data.
+ *
+ * To preserve the factory bad block information we take the following
+ * strategy:
+ *
+ * - If the NAND driver detects that no flash BBT is present on 2k NAND
+ * chips it will not create one because it would do so based on the wrong
+ * BBM position
+ * - This command is used to create a flash BBT then.
+ *
+ * From this point on we can forget about the BBMs and rely completely
+ * on the flash BBT.
+ *
+ */
+static int checkbad(struct nand_chip *chip, loff_t ofs)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+ uint8_t buf[mtd->writesize + mtd->oobsize];
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+
+ ret = mtd_read_oob(mtd, ofs, &ops);
+ if (ret < 0)
+ return ret;
+
+ if (buf[2000] != 0xff)
+ return 1;
+
+ return 0;
+}
+
+static int imxnd_create_bbt(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int len, i, numblocks, ret;
+ loff_t from = 0;
+ uint8_t *bbt;
+
+ len = mtd->size >> (chip->bbt_erase_shift + 2);
+
+ /* Allocate memory (2bit per block) and clear the memory bad block table */
+ bbt = kzalloc(len, GFP_KERNEL);
+ if (!bbt)
+ return -ENOMEM;
+
+ numblocks = mtd->size >> (chip->bbt_erase_shift - 1);
+
+ for (i = 0; i < numblocks;) {
+ ret = checkbad(chip, from);
+ if (ret < 0)
+ goto out;
+
+ if (ret) {
+ bbt[i >> 3] |= 0x03 << (i & 0x6);
+ dev_info(mtd->dev.parent, "Bad eraseblock %d at 0x%08x\n",
+ i >> 1, (unsigned int)from);
+ }
+
+ i += 2;
+ from += (1 << chip->bbt_erase_shift);
+ }
+
+ chip->bbt_td->options |= NAND_BBT_CREATE;
+ chip->bbt_md->options |= NAND_BBT_CREATE;
+
+ free(chip->bbt);
+ chip->bbt = bbt;
+
+ ret = nand_update_bbt(chip, 0);
+ if (ret)
+ return ret;
+
+ ret = nand_create_bbt(chip);
+ if (ret)
+ return ret;
+
+ ret = 0;
+out:
+ free(bbt);
+
+ return ret;
+}
+
+static int mxcnd_probe(struct device *dev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct mxc_nand_host *host;
+ struct resource *iores;
+ struct mxc_nand_devtype_data* devtype;
+ int err = 0;
+
+ err = dev_get_drvdata(dev, (const void **)&devtype);
+ if (err)
+ return err;
+
+ /* Allocate memory for MTD device structure and private data */
+ host = devm_kzalloc(dev, sizeof(struct mxc_nand_host),
+ GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ /* allocate a temporary buffer for the nand_scan_ident() */
+ host->data_buf = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL);
+ if (!host->data_buf)
+ return -ENOMEM;
+
+ host->dev = dev;
+ /* structures must be linked */
+ this = &host->nand;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = dev;
+ mtd->name = DRIVER_NAME;
+
+ /* 50 us command delay time */
+ this->legacy.chip_delay = 5;
+
+ nand_set_controller_data(this, host);
+ nand_set_flash_node(this, dev->of_node);
+
+ host->clk = clk_get(dev, NULL);
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
+ host->devtype_data = devtype;
+
+ if (!host->devtype_data->setup_interface)
+ this->options |= NAND_KEEP_TIMINGS;
+
+ if (host->devtype_data->needs_ip) {
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ host->regs_ip = IOMEM(iores->start);
+
+ iores = dev_request_mem_resource(dev, 1);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ host->base = IOMEM(iores->start);
+ } else {
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ host->base = IOMEM(iores->start);
+ }
+
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
+
+ host->main_area0 = host->base;
+
+ if (host->devtype_data->regs_offset)
+ host->regs = host->base + host->devtype_data->regs_offset;
+ host->spare0 = host->base + host->devtype_data->spare0_offset;
+ if (host->devtype_data->axi_offset)
+ host->regs_axi = host->base + host->devtype_data->axi_offset;
+
+ this->legacy.select_chip = host->devtype_data->select_chip;
+
+ init_completion(&host->op_completion);
+
+ err = clk_prepare_enable(host->clk);
+ if (err)
+ return err;
+ host->clk_act = 1;
+
+ /* Scan the NAND device */
+ this->legacy.dummy_controller.ops = &mxcnd_controller_ops;
+ err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1);
+ if (err)
+ goto escan;
+
+ this->options &= ~NAND_SUBPAGE_READ;
+
+ if ((this->bbt_options & NAND_BBT_USE_FLASH) &&
+ this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) {
+ dev_info(dev, "no BBT found. creating one\n");
+ err = imxnd_create_bbt(this);
+ if (err)
+ dev_warn(dev, "Failed to create bbt: %s\n",
+ strerror(-err));
+ err = 0;
+ }
+
+ /* Register the partitions */
+ err = add_mtd_nand_device(mtd, "nand");
+ if (err)
+ goto cleanup_nand;
+
+ dev->priv = host;
+
+ return 0;
+
+cleanup_nand:
+ nand_cleanup(this);
+escan:
+ if (host->clk_act)
+ clk_disable_unprepare(host->clk);
+
+ return err;
+}
+
+static struct driver mxcnd_driver = {
+ .name = DRIVER_NAME,
+ .probe = mxcnd_probe,
+ .of_compatible = DRV_OF_COMPAT(mxcnd_dt_ids),
+};
+device_platform_driver(mxcnd_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC NAND MTD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c
index c3d4dae3cd..c3d4dae3cd 100644
--- a/drivers/mtd/nand/nand_amd.c
+++ b/drivers/mtd/nand/raw/nand_amd.c
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 4c90ad9757..810b58a0c0 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -36,171 +36,15 @@
#include <asm/byteorder.h>
#include <io.h>
#include <malloc.h>
+#include <linux/gpio/consumer.h>
#include <module.h>
#include <of_mtd.h>
+#include <linux/mtd/nand-ecc-sw-bch.h>
+#include <linux/mtd/nand-ecc-sw-hamming.h>
+#include <linux/sizes.h>
#include "internals.h"
-/* Define default oob placement schemes for large and small page devices */
-static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section > 1)
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 0;
- if (mtd->oobsize == 16)
- oobregion->length = 4;
- else
- oobregion->length = 3;
- } else {
- if (mtd->oobsize == 8)
- return -ERANGE;
-
- oobregion->offset = 6;
- oobregion->length = ecc->total - 4;
- }
-
- return 0;
-}
-
-static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 1)
- return -ERANGE;
-
- if (mtd->oobsize == 16) {
- if (section)
- return -ERANGE;
-
- oobregion->length = 8;
- oobregion->offset = 8;
- } else {
- oobregion->length = 2;
- if (!section)
- oobregion->offset = 3;
- else
- oobregion->offset = 6;
- }
-
- return 0;
-}
-
-const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
- .ecc = nand_ooblayout_ecc_sp,
- .free = nand_ooblayout_free_sp,
-};
-EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
-
-static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section || !ecc->total)
- return -ERANGE;
-
- oobregion->length = ecc->total;
- oobregion->offset = mtd->oobsize - oobregion->length;
-
- return 0;
-}
-
-static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section)
- return -ERANGE;
-
- oobregion->length = mtd->oobsize - ecc->total - 2;
- oobregion->offset = 2;
-
- return 0;
-}
-
-const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
- .ecc = nand_ooblayout_ecc_lp,
- .free = nand_ooblayout_free_lp,
-};
-EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
-
-/*
- * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
- * are placed at a fixed offset.
- */
-static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section)
- return -ERANGE;
-
- switch (mtd->oobsize) {
- case 64:
- oobregion->offset = 40;
- break;
- case 128:
- oobregion->offset = 80;
- break;
- default:
- return -EINVAL;
- }
-
- oobregion->length = ecc->total;
- if (oobregion->offset + oobregion->length > mtd->oobsize)
- return -ERANGE;
-
- return 0;
-}
-
-static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ecc_offset = 0;
-
- if (section < 0 || section > 1)
- return -ERANGE;
-
- switch (mtd->oobsize) {
- case 64:
- ecc_offset = 40;
- break;
- case 128:
- ecc_offset = 80;
- break;
- default:
- return -EINVAL;
- }
-
- if (section == 0) {
- oobregion->offset = 2;
- oobregion->length = ecc_offset - 2;
- } else {
- oobregion->offset = ecc_offset + ecc->total;
- oobregion->length = mtd->oobsize - oobregion->offset;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
- .ecc = nand_ooblayout_ecc_lp_hamming,
- .free = nand_ooblayout_free_lp_hamming,
-};
-
static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
{
int ret = 0;
@@ -399,19 +243,9 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
* @chip: NAND chip structure
*
* Lock the device and its controller for exclusive access
- *
- * Return: -EBUSY if the chip has been suspended, 0 otherwise
*/
-static int nand_get_device(struct nand_chip *chip)
+static void nand_get_device(struct nand_chip *chip)
{
- mutex_lock(&chip->lock);
- if (chip->suspended) {
- mutex_unlock(&chip->lock);
- return -EBUSY;
- }
- mutex_lock(&chip->controller->lock);
-
- return 0;
}
/**
@@ -430,6 +264,10 @@ static int nand_check_wp(struct nand_chip *chip)
if (chip->options & NAND_BROKEN_XD)
return 0;
+ /* controller responsible for NAND write protect */
+ if (chip->controller->controller_wp)
+ return 0;
+
/* Check the WP bit */
ret = nand_status_op(chip, &status);
if (ret)
@@ -636,9 +474,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
nand_erase_nand(chip, &einfo, 0);
/* Write bad block marker to OOB */
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
ret = nand_markbad_bbm(chip, ofs);
nand_release_device(chip);
@@ -658,46 +494,20 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
}
/**
- * nand_block_markgood_lowlevel - mark a block good
+ * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
* @mtd: MTD device structure
* @ofs: offset from device start
*
- * We try operations in the following order:
- * (1) erase the affected block
- * (2) check bad block marker
- * (3) update the BBT
+ * Check if the block is marked as reserved.
*/
-static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs)
+static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
- bool allow_erasebad;
- int ret;
-
- if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
- struct erase_info einfo;
-
- /* Attempt erase possibly bad block */
- allow_erasebad = mtd->allow_erasebad;
- mtd->allow_erasebad = true;
- memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
- einfo.addr = ofs;
- einfo.len = 1 << chip->phys_erase_shift;
- nand_erase_nand(chip, &einfo, 0);
- mtd->allow_erasebad = allow_erasebad;
- }
-
- /* Mark block good in BBT */
- if (chip->bbt) {
- ret = nand_markgood_bbt(chip, ofs);
- if (ret)
- return ret;
- }
-
- if (mtd->ecc_stats.badblocks > 0)
- mtd->ecc_stats.badblocks--;
+ struct nand_chip *chip = mtd_to_nand(mtd);
- return 0;
+ if (!chip->bbt)
+ return 0;
+ /* Return info from the table */
+ return nand_isreserved_bbt(chip, ofs);
}
/**
@@ -737,7 +547,7 @@ static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt)
*/
int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
{
- const struct nand_sdr_timings *timings;
+ const struct nand_interface_config *conf;
u8 status = 0;
int ret;
uint64_t start;
@@ -746,13 +556,18 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
return -ENOTSUPP;
/* Wait tWB before polling the STATUS reg. */
- timings = nand_get_sdr_timings(nand_get_interface_config(chip));
- ndelay(PSEC_TO_NSEC(timings->tWB_max));
+ conf = nand_get_interface_config(chip);
+ ndelay(NAND_COMMON_TIMING_NS(conf, tWB_max));
ret = nand_status_op(chip, NULL);
if (ret)
return ret;
+ /*
+ * +1 below is necessary because if we are now in the last fraction
+ * of jiffy and msecs_to_jiffies is 1 then we will wait only that
+ * small jiffy fraction - possibly leading to false timeout
+ */
start = get_time_ns();
do {
ret = nand_read_data_op(chip, &status, sizeof(status), true,
@@ -769,7 +584,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
* deriving a delay from the timeout value, timeout_ms/ratio).
*/
udelay(10);
- } while (!is_timeout(start, timeout_ms * MSECOND));
+ } while (!is_timeout(start, timeout_ms * MSECOND));
/*
* We have to exit READ_STATUS mode in order to read real data on the
@@ -785,6 +600,59 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
};
EXPORT_SYMBOL_GPL(nand_soft_waitrdy);
+/**
+ * nand_gpio_waitrdy - Poll R/B GPIO pin until ready
+ * @chip: NAND chip structure
+ * @gpiod: GPIO descriptor of R/B pin
+ * @timeout_ms: Timeout in ms
+ *
+ * Poll the R/B GPIO pin until it becomes ready. If that does not happen
+ * whitin the specified timeout, -ETIMEDOUT is returned.
+ *
+ * This helper is intended to be used when the controller has access to the
+ * NAND R/B pin over GPIO.
+ *
+ * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise.
+ */
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+ unsigned long timeout_ms)
+{
+ return gpiod_poll_timeout_us(gpiod, true, timeout_ms * USEC_PER_MSEC);
+};
+EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
+
+/**
+ * panic_nand_wait - [GENERIC] wait until the command is done
+ * @chip: NAND chip structure
+ * @timeo: timeout
+ *
+ * Wait for command done. This is a helper function for nand_wait used when
+ * we are in interrupt context. May happen when in panic and trying to write
+ * an oops through mtdoops.
+ */
+void panic_nand_wait(struct nand_chip *chip, unsigned long timeo)
+{
+ int i;
+ for (i = 0; i < timeo; i++) {
+ if (chip->legacy.dev_ready) {
+ if (chip->legacy.dev_ready(chip))
+ break;
+ } else {
+ int ret;
+ u8 status;
+
+ ret = nand_read_data_op(chip, &status, sizeof(status),
+ true, false);
+ if (ret)
+ return;
+
+ if (status & NAND_STATUS_READY)
+ break;
+ }
+ mdelay(1);
+ }
+}
+
static bool nand_supports_get_features(struct nand_chip *chip, int addr)
{
return (chip->parameters.supports_set_get_features &&
@@ -850,7 +718,7 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
static int nand_setup_interface(struct nand_chip *chip, int chipnr)
{
const struct nand_controller_ops *ops = chip->controller->ops;
- u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
+ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { }, request;
int ret;
if (!nand_controller_can_setup_interface(chip))
@@ -866,7 +734,12 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
if (!chip->best_interface_config)
return 0;
- tmode_param[0] = chip->best_interface_config->timings.mode;
+ request = chip->best_interface_config->timings.mode;
+ if (nand_interface_is_sdr(chip->best_interface_config))
+ request |= ONFI_DATA_INTERFACE_SDR;
+ else
+ request |= ONFI_DATA_INTERFACE_NVDDR;
+ tmode_param[0] = request;
/* Change the mode on the chip side (if supported by the NAND chip) */
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
@@ -895,9 +768,13 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
if (ret)
goto err_reset_chip;
- if (tmode_param[0] != chip->best_interface_config->timings.mode) {
- pr_warn("timing mode %d not acknowledged by the NAND chip\n",
+ if (request != tmode_param[0]) {
+ pr_warn("%s timing mode %d not acknowledged by the NAND chip\n",
+ nand_interface_is_nvddr(chip->best_interface_config) ? "NV-DDR" : "SDR",
chip->best_interface_config->timings.mode);
+ pr_debug("NAND chip would work in %s timing mode %d\n",
+ tmode_param[0] & ONFI_DATA_INTERFACE_NVDDR ? "NV-DDR" : "SDR",
+ (unsigned int)ONFI_TIMING_MODE_PARAM(tmode_param[0]));
goto err_reset_chip;
}
@@ -934,7 +811,7 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
struct nand_sdr_timings *spec_timings)
{
const struct nand_controller_ops *ops = chip->controller->ops;
- int best_mode = 0, mode, ret;
+ int best_mode = 0, mode, ret = -EOPNOTSUPP;
iface->type = NAND_SDR_IFACE;
@@ -953,7 +830,7 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
/* Fallback to slower modes */
best_mode = iface->timings.mode;
} else if (chip->parameters.onfi) {
- best_mode = fls(chip->parameters.onfi->async_timing_mode) - 1;
+ best_mode = fls(chip->parameters.onfi->sdr_timing_modes) - 1;
}
for (mode = best_mode; mode >= 0; mode--) {
@@ -961,13 +838,87 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
iface);
- if (!ret)
+ if (!ret) {
+ chip->best_interface_config = iface;
break;
+ }
}
- chip->best_interface_config = iface;
+ return ret;
+}
- return 0;
+/**
+ * nand_choose_best_nvddr_timings - Pick up the best NVDDR timings that both the
+ * NAND controller and the NAND chip support
+ * @chip: the NAND chip
+ * @iface: the interface configuration (can eventually be updated)
+ * @spec_timings: specific timings, when not fitting the ONFI specification
+ *
+ * If specific timings are provided, use them. Otherwise, retrieve supported
+ * timing modes from ONFI information.
+ */
+int nand_choose_best_nvddr_timings(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ struct nand_nvddr_timings *spec_timings)
+{
+ const struct nand_controller_ops *ops = chip->controller->ops;
+ int best_mode = 0, mode, ret = -EOPNOTSUPP;
+
+ iface->type = NAND_NVDDR_IFACE;
+
+ if (spec_timings) {
+ iface->timings.nvddr = *spec_timings;
+ iface->timings.mode = onfi_find_closest_nvddr_mode(spec_timings);
+
+ /* Verify the controller supports the requested interface */
+ ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
+ iface);
+ if (!ret) {
+ chip->best_interface_config = iface;
+ return ret;
+ }
+
+ /* Fallback to slower modes */
+ best_mode = iface->timings.mode;
+ } else if (chip->parameters.onfi) {
+ best_mode = fls(chip->parameters.onfi->nvddr_timing_modes) - 1;
+ }
+
+ for (mode = best_mode; mode >= 0; mode--) {
+ onfi_fill_interface_config(chip, iface, NAND_NVDDR_IFACE, mode);
+
+ ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
+ iface);
+ if (!ret) {
+ chip->best_interface_config = iface;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * nand_choose_best_timings - Pick up the best NVDDR or SDR timings that both
+ * NAND controller and the NAND chip support
+ * @chip: the NAND chip
+ * @iface: the interface configuration (can eventually be updated)
+ *
+ * If specific timings are provided, use them. Otherwise, retrieve supported
+ * timing modes from ONFI information.
+ */
+static int nand_choose_best_timings(struct nand_chip *chip,
+ struct nand_interface_config *iface)
+{
+ int ret;
+
+ /* Try the fastest timings: NV-DDR */
+ ret = nand_choose_best_nvddr_timings(chip, iface, NULL);
+ if (!ret)
+ return 0;
+
+ /* Fallback to SDR timings otherwise */
+ return nand_choose_best_sdr_timings(chip, iface, NULL);
}
/**
@@ -998,7 +949,7 @@ static int nand_choose_interface_config(struct nand_chip *chip)
if (chip->ops.choose_interface_config)
ret = chip->ops.choose_interface_config(chip, iface);
else
- ret = nand_choose_best_sdr_timings(chip, iface, NULL);
+ ret = nand_choose_best_timings(chip, iface);
if (ret)
kfree(iface);
@@ -1064,15 +1015,15 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, void *buf,
unsigned int len)
{
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct mtd_info *mtd = nand_to_mtd(chip);
u8 addrs[4];
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READ0, 0),
- NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_ADDR(3, addrs, NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
+ NAND_COMMON_TIMING_NS(conf, tRR_min)),
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1107,15 +1058,15 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, void *buf,
unsigned int len)
{
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
u8 addrs[5];
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READ0, 0),
NAND_OP_ADDR(4, addrs, 0),
- NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
+ NAND_COMMON_TIMING_NS(conf, tRR_min)),
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1140,6 +1091,117 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
return nand_exec_op(chip, &op);
}
+static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
+{
+ /* lun is expected to be very small */
+ return (lun * pages_per_lun) + pages_per_lun - 1;
+}
+
+static void rawnand_cap_cont_reads(struct nand_chip *chip)
+{
+ struct nand_memory_organization *memorg;
+ unsigned int ppl, first_lun, last_lun;
+
+ memorg = nanddev_get_memorg(&chip->base);
+ ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
+ first_lun = chip->cont_read.first_page / ppl;
+ last_lun = chip->cont_read.last_page / ppl;
+
+ /* Prevent sequential cache reads across LUN boundaries */
+ if (first_lun != last_lun)
+ chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
+ else
+ chip->cont_read.pause_page = chip->cont_read.last_page;
+
+ if (chip->cont_read.first_page == chip->cont_read.pause_page) {
+ chip->cont_read.first_page++;
+ chip->cont_read.pause_page = min(chip->cont_read.last_page,
+ rawnand_last_page_of_lun(ppl, first_lun + 1));
+ }
+
+ if (chip->cont_read.first_page >= chip->cont_read.last_page)
+ chip->cont_read.ongoing = false;
+}
+
+static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len, bool check_only)
+{
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
+ u8 addrs[5];
+ struct nand_op_instr start_instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_ADDR(4, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), 0),
+ NAND_OP_CMD(NAND_CMD_READCACHESEQ, NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
+ NAND_COMMON_TIMING_NS(conf, tRR_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_op_instr cont_instrs[] = {
+ NAND_OP_CMD(page == chip->cont_read.pause_page ?
+ NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ,
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
+ NAND_COMMON_TIMING_NS(conf, tRR_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation start_op = NAND_OPERATION(chip->cur_cs, start_instrs);
+ struct nand_operation cont_op = NAND_OPERATION(chip->cur_cs, cont_instrs);
+ int ret;
+
+ if (!len) {
+ start_op.ninstrs--;
+ cont_op.ninstrs--;
+ }
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
+
+ addrs[2] = page;
+ addrs[3] = page >> 8;
+
+ if (chip->options & NAND_ROW_ADDR_3) {
+ addrs[4] = page >> 16;
+ start_instrs[1].ctx.addr.naddrs++;
+ }
+
+ /* Check if cache reads are supported */
+ if (check_only) {
+ if (nand_check_op(chip, &start_op) || nand_check_op(chip, &cont_op))
+ return -EOPNOTSUPP;
+
+ return 0;
+ }
+
+ if (page == chip->cont_read.first_page)
+ ret = nand_exec_op(chip, &start_op);
+ else
+ ret = nand_exec_op(chip, &cont_op);
+ if (ret)
+ return ret;
+
+ if (!chip->cont_read.ongoing)
+ return 0;
+
+ if (page == chip->cont_read.last_page) {
+ chip->cont_read.ongoing = false;
+ } else if (page == chip->cont_read.pause_page) {
+ chip->cont_read.first_page++;
+ rawnand_cap_cont_reads(chip);
+ }
+
+ return 0;
+}
+
+static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page)
+{
+ return chip->cont_read.ongoing && page >= chip->cont_read.first_page;
+}
+
/**
* nand_read_page_op - Do a READ PAGE operation
* @chip: The NAND chip
@@ -1165,10 +1227,16 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page,
return -EINVAL;
if (nand_has_exec_op(chip)) {
- if (mtd->writesize > 512)
- return nand_lp_exec_read_page_op(chip, page,
- offset_in_page, buf,
- len);
+ if (mtd->writesize > 512) {
+ if (rawnand_cont_read_ongoing(chip, page))
+ return nand_lp_exec_cont_read_page_op(chip, page,
+ offset_in_page,
+ buf, len, false);
+ else
+ return nand_lp_exec_read_page_op(chip, page,
+ offset_in_page, buf,
+ len);
+ }
return nand_sp_exec_read_page_op(chip, page, offset_in_page,
buf, len);
@@ -1204,13 +1272,14 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
return -EINVAL;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_PARAM, 0),
- NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_ADDR(1, &page,
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
+ NAND_COMMON_TIMING_NS(conf, tRR_min)),
NAND_OP_8BIT_DATA_IN(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1259,14 +1328,14 @@ int nand_change_read_column_op(struct nand_chip *chip,
return -ENOTSUPP;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
u8 addrs[2] = {};
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
NAND_OP_ADDR(2, addrs, 0),
NAND_OP_CMD(NAND_CMD_RNDOUTSTART,
- PSEC_TO_NSEC(sdr->tCCS_min)),
+ NAND_COMMON_TIMING_NS(conf, tCCS_min)),
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1334,8 +1403,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, const void *buf,
unsigned int len, bool prog)
{
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct mtd_info *mtd = nand_to_mtd(chip);
u8 addrs[5] = {};
struct nand_op_instr instrs[] = {
@@ -1346,15 +1415,15 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
*/
NAND_OP_CMD(NAND_CMD_READ0, 0),
NAND_OP_CMD(NAND_CMD_SEQIN, 0),
- NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_ADDR(0, addrs, NAND_COMMON_TIMING_NS(conf, tADL_min)),
NAND_OP_DATA_OUT(len, buf, 0),
- NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
+ NAND_OP_CMD(NAND_CMD_PAGEPROG,
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0),
};
- struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
+ instrs);
int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
- int ret;
- u8 status;
if (naddrs < 0)
return naddrs;
@@ -1394,15 +1463,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
op.ninstrs--;
}
- ret = nand_exec_op(chip, &op);
- if (!prog || ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
-
- return status;
+ return nand_exec_op(chip, &op);
}
/**
@@ -1458,12 +1519,13 @@ int nand_prog_page_end_op(struct nand_chip *chip)
u8 status;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_PAGEPROG,
- PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max),
+ 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1508,7 +1570,8 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
unsigned int len)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int status;
+ u8 status;
+ int ret;
if (!len || !buf)
return -EINVAL;
@@ -1517,14 +1580,24 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
return -EINVAL;
if (nand_has_exec_op(chip)) {
- status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ ret = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
len, true);
+ if (ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
} else {
chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page,
page);
chip->legacy.write_buf(chip, buf, len);
chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
+ ret = chip->legacy.waitfunc(chip);
+ if (ret < 0)
+ return ret;
+
+ status = ret;
}
if (status & NAND_STATUS_FAIL)
@@ -1565,12 +1638,12 @@ int nand_change_write_column_op(struct nand_chip *chip,
return -ENOTSUPP;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
u8 addrs[2];
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_RNDIN, 0),
- NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)),
+ NAND_OP_ADDR(2, addrs, NAND_COMMON_TIMING_NS(conf, tCCS_min)),
NAND_OP_DATA_OUT(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1614,26 +1687,46 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
unsigned int len)
{
unsigned int i;
- u8 *id = buf;
+ u8 *id = buf, *ddrbuf = NULL;
if (len && !buf)
return -EINVAL;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READID, 0),
- NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_ADDR(1, &addr,
+ NAND_COMMON_TIMING_NS(conf, tADL_min)),
NAND_OP_8BIT_DATA_IN(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
+
+ /* READ_ID data bytes are received twice in NV-DDR mode */
+ if (len && nand_interface_is_nvddr(conf)) {
+ ddrbuf = kzalloc(len * 2, GFP_KERNEL);
+ if (!ddrbuf)
+ return -ENOMEM;
+
+ instrs[2].ctx.data.len *= 2;
+ instrs[2].ctx.data.buf.in = ddrbuf;
+ }
/* Drop the DATA_IN instruction if len is set to 0. */
if (!len)
op.ninstrs--;
- return nand_exec_op(chip, &op);
+ ret = nand_exec_op(chip, &op);
+ if (!ret && len && nand_interface_is_nvddr(conf)) {
+ for (i = 0; i < len; i++)
+ id[i] = ddrbuf[i * 2];
+ }
+
+ kfree(ddrbuf);
+
+ return ret;
}
chip->legacy.cmdfunc(chip, NAND_CMD_READID, addr, -1);
@@ -1659,19 +1752,31 @@ EXPORT_SYMBOL_GPL(nand_readid_op);
int nand_status_op(struct nand_chip *chip, u8 *status)
{
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
+ u8 ddrstatus[2];
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_STATUS,
- PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_COMMON_TIMING_NS(conf, tADL_min)),
NAND_OP_8BIT_DATA_IN(1, status, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
+
+ /* The status data byte will be received twice in NV-DDR mode */
+ if (status && nand_interface_is_nvddr(conf)) {
+ instrs[1].ctx.data.len *= 2;
+ instrs[1].ctx.data.buf.in = ddrstatus;
+ }
if (!status)
op.ninstrs--;
- return nand_exec_op(chip, &op);
+ ret = nand_exec_op(chip, &op);
+ if (!ret && status && nand_interface_is_nvddr(conf))
+ *status = ddrstatus[0];
+
+ return ret;
}
chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
@@ -1708,6 +1813,7 @@ int nand_exit_status_op(struct nand_chip *chip)
return 0;
}
+EXPORT_SYMBOL_GPL(nand_exit_status_op);
/**
* nand_erase_op - Do an erase operation
@@ -1728,17 +1834,19 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
u8 status;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
u8 addrs[3] = { page, page >> 8, page >> 16 };
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_ERASE1, 0),
NAND_OP_ADDR(2, addrs, 0),
NAND_OP_CMD(NAND_CMD_ERASE2,
- PSEC_TO_MSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max),
+ 0),
};
- struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
+ instrs);
if (chip->options & NAND_ROW_ADDR_3)
instrs[1].ctx.addr.naddrs++;
@@ -1787,14 +1895,17 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
int i, ret;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
- NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_ADDR(1, &feature, NAND_COMMON_TIMING_NS(conf,
+ tADL_min)),
NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data,
- PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0),
+ NAND_COMMON_TIMING_NS(conf,
+ tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max),
+ 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1830,23 +1941,37 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
static int nand_get_features_op(struct nand_chip *chip, u8 feature,
void *data)
{
- u8 *params = data;
+ u8 *params = data, ddrbuf[ONFI_SUBFEATURE_PARAM_LEN * 2];
int i;
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
- NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_ADDR(1, &feature,
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max),
+ NAND_COMMON_TIMING_NS(conf, tRR_min)),
NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
data, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
- return nand_exec_op(chip, &op);
+ /* GET_FEATURE data bytes are received twice in NV-DDR mode */
+ if (nand_interface_is_nvddr(conf)) {
+ instrs[3].ctx.data.len *= 2;
+ instrs[3].ctx.data.buf.in = ddrbuf;
+ }
+
+ ret = nand_exec_op(chip, &op);
+ if (nand_interface_is_nvddr(conf)) {
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; i++)
+ params[i] = ddrbuf[i * 2];
+ }
+
+ return ret;
}
chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, feature, -1);
@@ -1891,11 +2016,13 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms,
int nand_reset_op(struct nand_chip *chip)
{
if (nand_has_exec_op(chip)) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(nand_get_interface_config(chip));
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
+ NAND_OP_CMD(NAND_CMD_RESET,
+ NAND_COMMON_TIMING_NS(conf, tWB_max)),
+ NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tRST_max),
+ 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
@@ -1930,17 +2057,50 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
return -EINVAL;
if (nand_has_exec_op(chip)) {
+ const struct nand_interface_config *conf =
+ nand_get_interface_config(chip);
struct nand_op_instr instrs[] = {
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ u8 *ddrbuf = NULL;
+ int ret, i;
instrs[0].ctx.data.force_8bit = force_8bit;
- if (check_only)
- return nand_check_op(chip, &op);
+ /*
+ * Parameter payloads (ID, status, features, etc) do not go
+ * through the same pipeline as regular data, hence the
+ * force_8bit flag must be set and this also indicates that in
+ * case NV-DDR timings are being used the data will be received
+ * twice.
+ */
+ if (force_8bit && nand_interface_is_nvddr(conf)) {
+ ddrbuf = kzalloc(len * 2, GFP_KERNEL);
+ if (!ddrbuf)
+ return -ENOMEM;
+
+ instrs[0].ctx.data.len *= 2;
+ instrs[0].ctx.data.buf.in = ddrbuf;
+ }
- return nand_exec_op(chip, &op);
+ if (check_only) {
+ ret = nand_check_op(chip, &op);
+ kfree(ddrbuf);
+ return ret;
+ }
+
+ ret = nand_exec_op(chip, &op);
+ if (!ret && force_8bit && nand_interface_is_nvddr(conf)) {
+ u8 *dst = buf;
+
+ for (i = 0; i < len; i++)
+ dst[i] = ddrbuf[i * 2];
+ }
+
+ kfree(ddrbuf);
+
+ return ret;
}
if (check_only)
@@ -3006,6 +3166,73 @@ static int nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
}
/**
+ * nand_read_page_hwecc_oob_first - Hardware ECC page read with ECC
+ * data read from OOB area
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Hardware ECC for large page chips, which requires the ECC data to be
+ * extracted from the OOB before the actual data is read.
+ */
+int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0;
+
+ /* Read the OOB area first */
+ ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+ if (ret)
+ return ret;
+
+ /* Move read cursor to start of page */
+ ret = nand_change_read_column_op(chip, 0, NULL, 0, false);
+ if (ret)
+ return ret;
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+
+ ret = nand_read_data_op(chip, p, eccsize, false, false);
+ if (ret)
+ return ret;
+
+ stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i],
+ eccbytes, NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+EXPORT_SYMBOL_GPL(nand_read_page_hwecc_oob_first);
+
+/**
* nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
* @chip: nand chip info structure
* @buf: buffer to store read data
@@ -3129,6 +3356,51 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
return NULL;
}
+static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page,
+ u32 readlen, int col)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int first_page, last_page;
+
+ chip->cont_read.ongoing = false;
+
+ if (!chip->controller->supported_op.cont_read)
+ return;
+
+ /*
+ * Don't bother making any calculations if the length is too small.
+ * Side effect: avoids possible integer underflows below.
+ */
+ if (readlen < (2 * mtd->writesize))
+ return;
+
+ /* Derive the page where continuous read should start (the first full page read) */
+ first_page = page;
+ if (col)
+ first_page++;
+
+ /* Derive the page where continuous read should stop (the last full page read) */
+ last_page = page + ((col + readlen) / mtd->writesize) - 1;
+
+ /* Configure and enable continuous read when suitable */
+ if (first_page < last_page) {
+ chip->cont_read.first_page = first_page;
+ chip->cont_read.last_page = last_page;
+ chip->cont_read.ongoing = true;
+ /* May reset the ongoing flag */
+ rawnand_cap_cont_reads(chip);
+ }
+}
+
+static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned int page)
+{
+ if (!chip->cont_read.ongoing || page != chip->cont_read.first_page)
+ return;
+
+ chip->cont_read.first_page++;
+ rawnand_cap_cont_reads(chip);
+}
+
/**
* nand_setup_read_retry - [INTERN] Set the READ RETRY mode
* @chip: NAND chip object
@@ -3153,13 +3425,13 @@ static int nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
static void nand_wait_readrdy(struct nand_chip *chip)
{
- const struct nand_sdr_timings *sdr;
+ const struct nand_interface_config *conf;
if (!(chip->options & NAND_NEED_READRDY))
return;
- sdr = nand_get_sdr_timings(nand_get_interface_config(chip));
- WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0));
+ conf = nand_get_interface_config(chip);
+ WARN_ON(nand_wait_rdy_op(chip, NAND_COMMON_TIMING_MS(conf, tR_max), 0));
}
/**
@@ -3198,6 +3470,9 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
oob = ops->oobbuf;
oob_required = oob ? 1 : 0;
+ if (likely(ops->mode != MTD_OPS_RAW))
+ rawnand_enable_cont_reads(chip, page, readlen, col);
+
while (1) {
struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
@@ -3296,6 +3571,8 @@ read_retry:
buf += bytes;
max_bitflips = max_t(unsigned int, max_bitflips,
chip->pagecache.bitflips);
+
+ rawnand_cont_read_skip_first_page(chip, page);
}
readlen -= bytes;
@@ -3326,6 +3603,9 @@ read_retry:
}
nand_deselect_target(chip);
+ if (WARN_ON_ONCE(chip->cont_read.ongoing))
+ chip->cont_read.ongoing = false;
+
ops->retlen = ops->len - (size_t) readlen;
if (oob)
ops->oobretlen = ops->ooblen - oobreadlen;
@@ -3415,9 +3695,6 @@ int nand_write_oob_std(struct nand_chip *chip, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi,
mtd->oobsize);
}
@@ -3437,9 +3714,6 @@ static int nand_write_oob_syndrome(struct nand_chip *chip, int page)
int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps;
const uint8_t *bufpoi = chip->oob_poi;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
/*
* data-ecc-data-ecc ... ecc-oob
* or
@@ -3589,6 +3863,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_ecc_stats old_stats;
int ret;
ops->retlen = 0;
@@ -3598,9 +3873,9 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
ops->mode != MTD_OPS_RAW)
return -ENOTSUPP;
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
+
+ old_stats = mtd->ecc_stats;
if (!ops->datbuf)
ret = nand_do_read_oob(chip, from, ops);
@@ -3641,9 +3916,6 @@ int nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
if (ret)
return ret;
@@ -3682,9 +3954,6 @@ int nand_monolithic_write_page_raw(struct nand_chip *chip, const u8 *buf,
unsigned int size = mtd->writesize;
u8 *write_buf = (u8 *)buf;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
if (oob_required) {
size += mtd->oobsize;
@@ -3717,9 +3986,6 @@ static int nand_write_page_raw_syndrome(struct nand_chip *chip,
uint8_t *oob = chip->oob_poi;
int steps, size, ret;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
if (ret)
return ret;
@@ -3782,9 +4048,6 @@ static int nand_write_page_swecc(struct nand_chip *chip, const uint8_t *buf,
uint8_t *ecc_calc = chip->ecc.calc_buf;
const uint8_t *p = buf;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
/* Software ECC calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(chip, p, &ecc_calc[i]);
@@ -3814,9 +4077,6 @@ static int nand_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf,
uint8_t *ecc_calc = chip->ecc.calc_buf;
const uint8_t *p = buf;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
if (ret)
return ret;
@@ -3868,9 +4128,6 @@ static int nand_write_subpage_hwecc(struct nand_chip *chip, uint32_t offset,
int oob_bytes = mtd->oobsize / ecc_steps;
int step, ret;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
if (ret)
return ret;
@@ -3938,9 +4195,6 @@ static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf,
uint8_t *oob = chip->oob_poi;
int ret;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
if (ret)
return ret;
@@ -4007,9 +4261,6 @@ static int nand_write_page(struct nand_chip *chip, uint32_t offset,
struct mtd_info *mtd = nand_to_mtd(chip);
int status, subpage;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
chip->ecc.write_subpage)
subpage = offset || (data_len < mtd->writesize);
@@ -4056,15 +4307,12 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
int ret;
int oob_required = oob ? 1 : 0;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ops->retlen = 0;
if (!writelen)
return 0;
/* Reject writes, which are not page aligned */
- if (NOTALIGNED(to)) {
+ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
pr_notice("%s: attempt to write non page aligned data\n",
__func__);
return -EINVAL;
@@ -4174,16 +4422,11 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ int ret = 0;
ops->retlen = 0;
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -4214,9 +4457,6 @@ out:
*/
static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
return nand_erase_nand(mtd_to_nand(mtd), instr, 0);
}
@@ -4232,13 +4472,9 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
int allowbbt)
{
struct mtd_info *mtd = nand_to_mtd(chip);
-
int page, pages_per_block, ret, chipnr;
loff_t len;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)instr->addr,
(unsigned long long)instr->len);
@@ -4247,9 +4483,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
return -EINVAL;
/* Grab the lock and see if the device is available */
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
/* Shift to get first page */
page = (int)(instr->addr >> chip->page_shift);
@@ -4273,12 +4507,14 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
len = instr->len;
while (len) {
+ loff_t ofs = (loff_t)page << chip->page_shift;
+
/* Check if we have a bad block, we do not erase bad blocks! */
if (!mtd->allow_erasebad &&
nand_block_checkbad(chip, ((loff_t) page) <<
chip->page_shift, allowbbt)) {
- pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
- __func__, page);
+ pr_warn("%s: attempt to erase a bad block at 0x%08llx\n",
+ __func__, (unsigned long long)ofs);
ret = -EIO;
goto erase_exit;
}
@@ -4296,8 +4532,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
if (ret) {
pr_debug("%s: failed erase, page 0x%08x\n",
__func__, page);
- instr->fail_addr =
- ((loff_t)page << chip->page_shift);
+ instr->fail_addr = ofs;
goto erase_exit;
}
@@ -4337,7 +4572,7 @@ static void nand_sync(struct mtd_info *mtd)
pr_debug("%s: called\n", __func__);
/* Grab the lock and see if the device is available */
- WARN_ON(nand_get_device(chip));
+ nand_get_device(chip);
/* Release it and go back */
nand_release_device(chip);
}
@@ -4354,9 +4589,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
int ret;
/* Select the NAND device */
- ret = nand_get_device(chip);
- if (ret)
- return ret;
+ nand_get_device(chip);
nand_select_target(chip, chipnr);
@@ -4377,9 +4610,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
int ret;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
ret = nand_block_isbad(mtd, ofs);
if (ret) {
/* If it was bad already, return success and do nothing */
@@ -4392,32 +4622,12 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
}
/**
- * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
- */
-static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
-{
- int ret;
-
- ret = nand_block_isbad(mtd, ofs);
- if (ret < 0)
- return ret;
-
- if (!ret)
- /* If it was good already, return success and do nothing */
- return 0;
-
- return nand_block_markgood_lowlevel(mtd_to_nand(mtd), ofs);
-}
-
-/**
* nand_lock - [MTD Interface] Lock the NAND flash
* @mtd: MTD device structure
* @ofs: offset byte address
* @len: number of bytes to lock (must be a multiple of block/page size)
*/
-static int nand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
@@ -4433,7 +4643,7 @@ static int nand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
* @ofs: offset byte address
* @len: number of bytes to unlock (must be a multiple of block/page size)
*/
-static int nand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
@@ -4630,6 +4840,8 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type)
static bool find_full_id_nand(struct nand_chip *chip,
struct nand_flash_dev *type)
{
+ struct nand_device *base = &chip->base;
+ struct nand_ecc_props requirements;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg;
u8 *id_data = chip->id.data;
@@ -4651,10 +4863,11 @@ static bool find_full_id_nand(struct nand_chip *chip,
memorg->pagesize *
memorg->pages_per_eraseblock);
chip->options |= type->options;
- chip->base.eccreq.strength = NAND_ECC_STRENGTH(type);
- chip->base.eccreq.step_size = NAND_ECC_STEP(type);
+ requirements.strength = NAND_ECC_STRENGTH(type);
+ requirements.step_size = NAND_ECC_STEP(type);
+ nanddev_set_ecc_requirements(base, &requirements);
- chip->parameters.model = strdup(type->name);
+ chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
if (!chip->parameters.model)
return false;
@@ -4723,6 +4936,67 @@ nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc)
return manufacturer_desc ? manufacturer_desc->name : "Unknown";
}
+static void rawnand_check_data_only_read_support(struct nand_chip *chip)
+{
+ /* Use an arbitrary size for the check */
+ if (!nand_read_data_op(chip, NULL, SZ_512, true, true))
+ chip->controller->supported_op.data_only_read = 1;
+}
+
+static void rawnand_early_check_supported_ops(struct nand_chip *chip)
+{
+ /* The supported_op fields should not be set by individual drivers */
+ WARN_ON_ONCE(chip->controller->supported_op.data_only_read);
+
+ if (!nand_has_exec_op(chip))
+ return;
+
+ rawnand_check_data_only_read_support(chip);
+}
+
+static void rawnand_check_cont_read_support(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!chip->parameters.supports_read_cache)
+ return;
+
+ if (chip->read_retries)
+ return;
+
+ if (!nand_lp_exec_cont_read_page_op(chip, 0, 0, NULL,
+ mtd->writesize, true))
+ chip->controller->supported_op.cont_read = 1;
+}
+
+static void rawnand_late_check_supported_ops(struct nand_chip *chip)
+{
+ /* The supported_op fields should not be set by individual drivers */
+ WARN_ON_ONCE(chip->controller->supported_op.cont_read);
+
+ /*
+ * Too many devices do not support sequential cached reads with on-die
+ * ECC correction enabled, so in this case refuse to perform the
+ * automation.
+ */
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE)
+ return;
+
+ if (!nand_has_exec_op(chip))
+ return;
+
+ /*
+ * For now, continuous reads can only be used with the core page helpers.
+ * This can be extended later.
+ */
+ if (!(chip->ecc.read_page == nand_read_page_hwecc ||
+ chip->ecc.read_page == nand_read_page_syndrome ||
+ chip->ecc.read_page == nand_read_page_swecc))
+ return;
+
+ rawnand_check_cont_read_support(chip);
+}
+
/*
* Get the flash and manufacturer id and lookup if the type is supported.
*/
@@ -4755,6 +5029,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
/* Select the device */
nand_select_target(chip, 0);
+ rawnand_early_check_supported_ops(chip);
+
/* Send the command for reading device ID */
ret = nand_readid_op(chip, 0, id_data, 2);
if (ret)
@@ -4834,7 +5110,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
if (!type->name)
return -ENODEV;
- chip->parameters.model = strdup(type->name);
+ chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
if (!chip->parameters.model)
return -ENOMEM;
@@ -4913,80 +5189,139 @@ free_detect_allocation:
return ret;
}
-static const char * const nand_ecc_algos[] = {
- [NAND_ECC_HAMMING] = "hamming",
- [NAND_ECC_BCH] = "bch",
- [NAND_ECC_RS] = "rs",
-};
+static enum nand_ecc_engine_type
+of_get_rawnand_ecc_engine_type_legacy(struct device_node *np)
+{
+ const char * const nand_ecc_legacy_modes[] = {
+ [NAND_ECC_NONE] = "none",
+ [NAND_ECC_SOFT] = "soft",
+ [NAND_ECC_SOFT_BCH] = "soft_bch",
+ [NAND_ECC_HW] = "hw",
+ [NAND_ECC_HW_SYNDROME] = "hw_syndrome",
+ [NAND_ECC_ON_DIE] = "on-die",
+ };
+ enum nand_ecc_legacy_mode eng_type;
+ const char *pm;
+ int err;
+
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
+ if (err)
+ return NAND_ECC_ENGINE_TYPE_INVALID;
+
+ for (eng_type = NAND_ECC_NONE;
+ eng_type < ARRAY_SIZE(nand_ecc_legacy_modes); eng_type++) {
+ if (!strcasecmp(pm, nand_ecc_legacy_modes[eng_type])) {
+ switch (eng_type) {
+ case NAND_ECC_NONE:
+ return NAND_ECC_ENGINE_TYPE_NONE;
+ case NAND_ECC_SOFT:
+ case NAND_ECC_SOFT_BCH:
+ return NAND_ECC_ENGINE_TYPE_SOFT;
+ case NAND_ECC_HW:
+ case NAND_ECC_HW_SYNDROME:
+ return NAND_ECC_ENGINE_TYPE_ON_HOST;
+ case NAND_ECC_ON_DIE:
+ return NAND_ECC_ENGINE_TYPE_ON_DIE;
+ default:
+ break;
+ }
+ }
+ }
-static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
+ return NAND_ECC_ENGINE_TYPE_INVALID;
+}
+
+static enum nand_ecc_placement
+of_get_rawnand_ecc_placement_legacy(struct device_node *np)
{
- enum nand_ecc_algo ecc_algo;
const char *pm;
int err;
- err = of_property_read_string(np, "nand-ecc-algo", &pm);
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
if (!err) {
- for (ecc_algo = NAND_ECC_HAMMING;
- ecc_algo < ARRAY_SIZE(nand_ecc_algos);
- ecc_algo++) {
- if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
- return ecc_algo;
- }
+ if (!strcasecmp(pm, "hw_syndrome"))
+ return NAND_ECC_PLACEMENT_INTERLEAVED;
}
- /*
- * For backward compatibility we also read "nand-ecc-mode" checking
- * for some obsoleted values that were specifying ECC algorithm.
- */
+ return NAND_ECC_PLACEMENT_UNKNOWN;
+}
+
+static enum nand_ecc_algo of_get_rawnand_ecc_algo_legacy(struct device_node *np)
+{
+ const char *pm;
+ int err;
+
err = of_property_read_string(np, "nand-ecc-mode", &pm);
if (!err) {
if (!strcasecmp(pm, "soft"))
- return NAND_ECC_HAMMING;
+ return NAND_ECC_ALGO_HAMMING;
else if (!strcasecmp(pm, "soft_bch"))
- return NAND_ECC_BCH;
+ return NAND_ECC_ALGO_BCH;
}
- return NAND_ECC_UNKNOWN;
+ return NAND_ECC_ALGO_UNKNOWN;
+}
+
+static void of_get_nand_ecc_legacy_user_config(struct nand_chip *chip)
+{
+ struct device_node *dn = nand_get_flash_node(chip);
+ struct nand_ecc_props *user_conf = &chip->base.ecc.user_conf;
+
+ if (user_conf->engine_type == NAND_ECC_ENGINE_TYPE_INVALID)
+ user_conf->engine_type = of_get_rawnand_ecc_engine_type_legacy(dn);
+
+ if (user_conf->algo == NAND_ECC_ALGO_UNKNOWN)
+ user_conf->algo = of_get_rawnand_ecc_algo_legacy(dn);
+
+ if (user_conf->placement == NAND_ECC_PLACEMENT_UNKNOWN)
+ user_conf->placement = of_get_rawnand_ecc_placement_legacy(dn);
}
-static int nand_dt_init(struct nand_chip *chip)
+static int rawnand_dt_init(struct nand_chip *chip)
{
+ struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip));
struct device_node *dn = nand_get_flash_node(chip);
- enum nand_ecc_algo ecc_algo;
- int ecc_mode, ecc_strength, ecc_step;
+ int ret;
if (!dn)
return 0;
- if (of_get_nand_bus_width(dn) == 16)
+ ret = of_get_nand_bus_width(dn);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 16)
chip->options |= NAND_BUSWIDTH_16;
if (of_property_read_bool(dn, "nand-is-boot-medium"))
chip->options |= NAND_IS_BOOT_MEDIUM;
- if (of_get_nand_on_flash_bbt(dn))
+ if (of_property_read_bool(dn, "nand-on-flash-bbt"))
chip->bbt_options |= NAND_BBT_USE_FLASH;
- ecc_mode = of_get_nand_ecc_mode(dn);
- ecc_algo = of_get_nand_ecc_algo(dn);
- ecc_strength = of_get_nand_ecc_strength(dn);
- ecc_step = of_get_nand_ecc_step_size(dn);
+ of_get_nand_ecc_user_config(nand);
+ of_get_nand_ecc_legacy_user_config(chip);
- if (ecc_mode >= 0)
- chip->ecc.mode = ecc_mode;
-
- if (ecc_algo != NAND_ECC_UNKNOWN)
- chip->ecc.algo = ecc_algo;
-
- if (ecc_strength >= 0)
- chip->ecc.strength = ecc_strength;
+ /*
+ * If neither the user nor the NAND controller have requested a specific
+ * ECC engine type, we will default to NAND_ECC_ENGINE_TYPE_ON_HOST.
+ */
+ nand->ecc.defaults.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
- if (ecc_step > 0)
- chip->ecc.size = ecc_step;
+ /*
+ * Use the user requested engine type, unless there is none, in this
+ * case default to the NAND controller choice, otherwise fallback to
+ * the raw NAND default one.
+ */
+ if (nand->ecc.user_conf.engine_type != NAND_ECC_ENGINE_TYPE_INVALID)
+ chip->ecc.engine_type = nand->ecc.user_conf.engine_type;
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID)
+ chip->ecc.engine_type = nand->ecc.defaults.engine_type;
- if (of_property_read_bool(dn, "nand-ecc-maximize"))
- chip->ecc.options |= NAND_ECC_MAXIMIZE;
+ chip->ecc.placement = nand->ecc.user_conf.placement;
+ chip->ecc.algo = nand->ecc.user_conf.algo;
+ chip->ecc.strength = nand->ecc.user_conf.strength;
+ chip->ecc.size = nand->ecc.user_conf.step_size;
return 0;
}
@@ -5024,11 +5359,9 @@ int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
/* Enforce the right timings for reset/detection */
chip->current_interface_config = nand_get_reset_interface_config();
- if (IS_ENABLED(CONFIG_OFTREE)) {
- ret = nand_dt_init(chip);
- if (ret)
- return ret;
- }
+ ret = rawnand_dt_init(chip);
+ if (ret)
+ return ret;
if (!mtd->name && mtd->dev.parent)
mtd->name = strdup(dev_name(mtd->dev.parent));
@@ -5093,21 +5426,189 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip)
kfree(chip->parameters.onfi);
}
+int rawnand_sw_hamming_init(struct nand_chip *chip)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf;
+ struct nand_device *base = &chip->base;
+ int ret;
+
+ base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ base->ecc.user_conf.algo = NAND_ECC_ALGO_HAMMING;
+ base->ecc.user_conf.strength = chip->ecc.strength;
+ base->ecc.user_conf.step_size = chip->ecc.size;
+
+ ret = nand_ecc_sw_hamming_init_ctx(base);
+ if (ret)
+ return ret;
+
+ engine_conf = base->ecc.ctx.priv;
+
+ if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER)
+ engine_conf->sm_order = true;
+
+ chip->ecc.size = base->ecc.ctx.conf.step_size;
+ chip->ecc.strength = base->ecc.ctx.conf.strength;
+ chip->ecc.total = base->ecc.ctx.total;
+ chip->ecc.steps = nanddev_get_ecc_nsteps(base);
+ chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base);
+
+ return 0;
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_init);
+
+int rawnand_sw_hamming_calculate(struct nand_chip *chip,
+ const unsigned char *buf,
+ unsigned char *code)
+{
+ struct nand_device *base = &chip->base;
+
+ return nand_ecc_sw_hamming_calculate(base, buf, code);
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_calculate);
+
+int rawnand_sw_hamming_correct(struct nand_chip *chip,
+ unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc)
+{
+ struct nand_device *base = &chip->base;
+
+ return nand_ecc_sw_hamming_correct(base, buf, read_ecc, calc_ecc);
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_correct);
+
+void rawnand_sw_hamming_cleanup(struct nand_chip *chip)
+{
+ struct nand_device *base = &chip->base;
+
+ nand_ecc_sw_hamming_cleanup_ctx(base);
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_cleanup);
+
+int rawnand_sw_bch_init(struct nand_chip *chip)
+{
+ struct nand_device *base = &chip->base;
+ const struct nand_ecc_props *ecc_conf = nanddev_get_ecc_conf(base);
+ int ret;
+
+ base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ base->ecc.user_conf.algo = NAND_ECC_ALGO_BCH;
+ base->ecc.user_conf.step_size = chip->ecc.size;
+ base->ecc.user_conf.strength = chip->ecc.strength;
+
+ ret = nand_ecc_sw_bch_init_ctx(base);
+ if (ret)
+ return ret;
+
+ chip->ecc.size = ecc_conf->step_size;
+ chip->ecc.strength = ecc_conf->strength;
+ chip->ecc.total = base->ecc.ctx.total;
+ chip->ecc.steps = nanddev_get_ecc_nsteps(base);
+ chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base);
+
+ return 0;
+}
+EXPORT_SYMBOL(rawnand_sw_bch_init);
+
+static int rawnand_sw_bch_calculate(struct nand_chip *chip,
+ const unsigned char *buf,
+ unsigned char *code)
+{
+ struct nand_device *base = &chip->base;
+
+ return nand_ecc_sw_bch_calculate(base, buf, code);
+}
+
+int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ struct nand_device *base = &chip->base;
+
+ return nand_ecc_sw_bch_correct(base, buf, read_ecc, calc_ecc);
+}
+EXPORT_SYMBOL(rawnand_sw_bch_correct);
+
+void rawnand_sw_bch_cleanup(struct nand_chip *chip)
+{
+ struct nand_device *base = &chip->base;
+
+ nand_ecc_sw_bch_cleanup_ctx(base);
+}
+EXPORT_SYMBOL(rawnand_sw_bch_cleanup);
+
+static int nand_set_ecc_on_host_ops(struct nand_chip *chip)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ switch (ecc->placement) {
+ case NAND_ECC_PLACEMENT_UNKNOWN:
+ case NAND_ECC_PLACEMENT_OOB:
+ /* Use standard hwecc read page function? */
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_hwecc;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_hwecc;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->read_subpage)
+ ecc->read_subpage = nand_read_subpage;
+ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
+ ecc->write_subpage = nand_write_subpage_hwecc;
+ fallthrough;
+
+ case NAND_ECC_PLACEMENT_INTERLEAVED:
+ if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
+ (!ecc->read_page ||
+ ecc->read_page == nand_read_page_hwecc ||
+ !ecc->write_page ||
+ ecc->write_page == nand_write_page_hwecc)) {
+ WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+ return -EINVAL;
+ }
+ /* Use standard syndrome read/write page function? */
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_syndrome;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_syndrome;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw_syndrome;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw_syndrome;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_syndrome;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_syndrome;
+ break;
+
+ default:
+ pr_warn("Invalid NAND_ECC_PLACEMENT %d\n",
+ ecc->placement);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int nand_set_ecc_soft_ops(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_device *nanddev = mtd_to_nanddev(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret;
- if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SOFT))
- return -ENOSYS;
-
- if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
+ if (WARN_ON(ecc->engine_type != NAND_ECC_ENGINE_TYPE_SOFT))
return -EINVAL;
switch (ecc->algo) {
- case NAND_ECC_HAMMING:
- ecc->calculate = nand_calculate_ecc;
- ecc->correct = nand_correct_data;
+ case NAND_ECC_ALGO_HAMMING:
+ ecc->calculate = rawnand_sw_hamming_calculate;
+ ecc->correct = rawnand_sw_hamming_correct;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
@@ -5125,14 +5626,20 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC))
ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
+ ret = rawnand_sw_hamming_init(chip);
+ if (ret) {
+ WARN(1, "Hamming ECC initialization failed!\n");
+ return ret;
+ }
+
return 0;
- case NAND_ECC_BCH:
- if (!mtd_nand_has_bch()) {
+ case NAND_ECC_ALGO_BCH:
+ if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) {
WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
return -EINVAL;
}
- ecc->calculate = nand_bch_calculate_ecc;
- ecc->correct = nand_bch_correct_data;
+ ecc->calculate = rawnand_sw_bch_calculate;
+ ecc->correct = rawnand_sw_bch_correct;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
@@ -5144,55 +5651,20 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
ecc->write_oob = nand_write_oob_std;
/*
- * Board driver should supply ecc.size and ecc.strength
- * values to select how many bits are correctable.
- * Otherwise, default to 4 bits for large page devices.
- */
- if (!ecc->size && (mtd->oobsize >= 64)) {
- ecc->size = 512;
- ecc->strength = 4;
- }
-
- /*
- * if no ecc placement scheme was provided pickup the default
- * large page one.
- */
- if (!mtd->ooblayout) {
- /* handle large page devices only */
- if (mtd->oobsize < 64) {
- WARN(1, "OOB layout is required when using software BCH on small pages\n");
- return -EINVAL;
- }
-
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-
- }
-
- /*
* We can only maximize ECC config when the default layout is
* used, otherwise we don't know how many bytes can really be
* used.
*/
- if (mtd->ooblayout == &nand_ooblayout_lp_ops &&
- ecc->options & NAND_ECC_MAXIMIZE) {
- int steps, bytes;
-
- /* Always prefer 1k blocks over 512bytes ones */
- ecc->size = 1024;
- steps = mtd->writesize / ecc->size;
+ if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH &&
+ mtd->ooblayout != nand_get_large_page_ooblayout())
+ nanddev->ecc.user_conf.flags &= ~NAND_ECC_MAXIMIZE_STRENGTH;
- /* Reserve 2 bytes for the BBM */
- bytes = (mtd->oobsize - 2) / steps;
- ecc->strength = bytes * 8 / fls(8 * ecc->size);
- }
-
- /* See nand_bch_init() for details. */
- ecc->bytes = 0;
- ecc->priv = nand_bch_init(mtd);
- if (!ecc->priv) {
+ ret = rawnand_sw_bch_init(chip);
+ if (ret) {
WARN(1, "BCH ECC initialization failed!\n");
- return -EINVAL;
+ return ret;
}
+
return 0;
default:
WARN(1, "Unsupported ECC algorithm!\n");
@@ -5233,7 +5705,7 @@ nand_check_ecc_caps(struct nand_chip *chip,
ecc_bytes = caps->calc_ecc_bytes(preset_step,
preset_strength);
- if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__))
+ if (WARN_ON_ONCE(ecc_bytes < 0))
return ecc_bytes;
if (ecc_bytes * nsteps > oobavail) {
@@ -5268,12 +5740,14 @@ static int
nand_match_ecc_req(struct nand_chip *chip,
const struct nand_ecc_caps *caps, int oobavail)
{
+ const struct nand_ecc_props *requirements =
+ nanddev_get_ecc_requirements(&chip->base);
struct mtd_info *mtd = nand_to_mtd(chip);
const struct nand_ecc_step_info *stepinfo;
- int req_step = chip->base.eccreq.step_size;
- int req_strength = chip->base.eccreq.strength;
+ int req_step = requirements->step_size;
+ int req_strength = requirements->strength;
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
- int best_step, best_strength, best_ecc_bytes;
+ int best_step = 0, best_strength = 0, best_ecc_bytes = 0;
int best_ecc_bytes_total = INT_MAX;
int i, j;
@@ -5305,7 +5779,7 @@ nand_match_ecc_req(struct nand_chip *chip,
nsteps = mtd->writesize / step_size;
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
- if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__))
+ if (WARN_ON_ONCE(ecc_bytes < 0))
continue;
ecc_bytes_total = ecc_bytes * nsteps;
@@ -5354,7 +5828,7 @@ nand_maximize_ecc(struct nand_chip *chip,
int step_size, strength, nsteps, ecc_bytes, corr;
int best_corr = 0;
int best_step = 0;
- int best_strength, best_ecc_bytes;
+ int best_strength = 0, best_ecc_bytes = 0;
int i, j;
for (i = 0; i < caps->nstepinfos; i++) {
@@ -5374,7 +5848,7 @@ nand_maximize_ecc(struct nand_chip *chip,
nsteps = mtd->writesize / step_size;
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
- if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__))
+ if (WARN_ON_ONCE(ecc_bytes < 0))
continue;
if (ecc_bytes * nsteps > oobavail)
@@ -5412,11 +5886,12 @@ nand_maximize_ecc(struct nand_chip *chip,
* @caps: ECC engine caps info structure
* @oobavail: OOB size that the ECC engine can use
*
- * Choose the ECC configuration according to following logic
+ * Choose the ECC configuration according to following logic.
*
* 1. If both ECC step size and ECC strength are already set (usually by DT)
* then check if it is supported by this controller.
- * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength.
+ * 2. If the user provided the nand-ecc-maximize property, then select maximum
+ * ECC strength.
* 3. Otherwise, try to match the ECC step size and ECC strength closest
* to the chip's requirement. If available OOB size can't fit the chip
* requirement then fallback to the maximum ECC step size and ECC strength.
@@ -5427,6 +5902,7 @@ int nand_ecc_choose_conf(struct nand_chip *chip,
const struct nand_ecc_caps *caps, int oobavail)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_device *nanddev = mtd_to_nanddev(mtd);
if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize))
return -EINVAL;
@@ -5434,7 +5910,7 @@ int nand_ecc_choose_conf(struct nand_chip *chip,
if (chip->ecc.size && chip->ecc.strength)
return nand_check_ecc_caps(chip, caps, oobavail);
- if (chip->ecc.options & NAND_ECC_MAXIMIZE)
+ if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH)
return nand_maximize_ecc(chip, caps, oobavail);
if (!nand_match_ecc_req(chip, caps, oobavail))
@@ -5444,41 +5920,6 @@ int nand_ecc_choose_conf(struct nand_chip *chip,
}
EXPORT_SYMBOL_GPL(nand_ecc_choose_conf);
-/*
- * Check if the chip configuration meet the datasheet requirements.
-
- * If our configuration corrects A bits per B bytes and the minimum
- * required correction level is X bits per Y bytes, then we must ensure
- * both of the following are true:
- *
- * (1) A / B >= X / Y
- * (2) A >= X
- *
- * Requirement (1) ensures we can correct for the required bitflip density.
- * Requirement (2) ensures we can correct even when all bitflips are clumped
- * in the same sector.
- */
-static bool nand_ecc_strength_good(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int corr, ds_corr;
-
- if (ecc->size == 0 || chip->base.eccreq.step_size == 0)
- /* Not enough information */
- return true;
-
- /*
- * We get the number of corrected bits per page to compare
- * the correction density.
- */
- corr = (mtd->writesize * ecc->strength) / ecc->size;
- ds_corr = (mtd->writesize * chip->base.eccreq.strength) /
- chip->base.eccreq.step_size;
-
- return corr >= ds_corr && ecc->strength >= chip->base.eccreq.strength;
-}
-
static int rawnand_erase(struct nand_device *nand, const struct nand_pos *pos)
{
struct nand_chip *chip = container_of(nand, struct nand_chip,
@@ -5566,15 +6007,19 @@ int nand_scan_tail(struct nand_chip *chip)
* If no default placement scheme is given, select an appropriate one.
*/
if (!mtd->ooblayout &&
- !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
+ !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
+ ecc->algo == NAND_ECC_ALGO_BCH) &&
+ !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
+ ecc->algo == NAND_ECC_ALGO_HAMMING)) {
switch (mtd->oobsize) {
case 8:
case 16:
- mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
+ mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout());
break;
case 64:
case 128:
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
+ mtd_set_ooblayout(mtd,
+ nand_get_large_page_hamming_ooblayout());
break;
default:
/*
@@ -5584,9 +6029,9 @@ int nand_scan_tail(struct nand_chip *chip)
* page with ECC layout when ->oobsize <= 128 for
* compatibility reasons.
*/
- if (ecc->mode == NAND_ECC_NONE) {
+ if (ecc->engine_type == NAND_ECC_ENGINE_TYPE_NONE) {
mtd_set_ooblayout(mtd,
- &nand_ooblayout_lp_ops);
+ nand_get_large_page_ooblayout());
break;
}
@@ -5602,54 +6047,11 @@ int nand_scan_tail(struct nand_chip *chip)
* selected and we have 256 byte pagesize fallback to software ECC
*/
- switch (ecc->mode) {
- case NAND_ECC_HW:
- /* Use standard hwecc read page function? */
- if (!ecc->read_page)
- ecc->read_page = nand_read_page_hwecc;
- if (!ecc->write_page)
- ecc->write_page = nand_write_page_hwecc;
- if (!ecc->read_page_raw)
- ecc->read_page_raw = nand_read_page_raw;
- if (!ecc->write_page_raw)
- ecc->write_page_raw = nand_write_page_raw;
- if (!ecc->read_oob)
- ecc->read_oob = nand_read_oob_std;
- if (!ecc->write_oob)
- ecc->write_oob = nand_write_oob_std;
- if (!ecc->read_subpage)
- ecc->read_subpage = nand_read_subpage;
- if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
- ecc->write_subpage = nand_write_subpage_hwecc;
- case NAND_ECC_HW_SYNDROME:
- if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
- (!ecc->read_page ||
- ecc->read_page == nand_read_page_hwecc ||
- !ecc->write_page ||
- ecc->write_page == nand_write_page_hwecc)) {
- WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- if (IS_ENABLED(CONFIG_NAND_ECC_HW_SYNDROME)) {
- /* Use standard syndrome read/write page function? */
- if (!ecc->read_page)
- ecc->read_page = nand_read_page_syndrome;
- if (!ecc->write_page)
- ecc->write_page = nand_write_page_syndrome;
- if (!ecc->read_page_raw)
- ecc->read_page_raw = nand_read_page_raw_syndrome;
- if (!ecc->write_page_raw)
- ecc->write_page_raw = nand_write_page_raw_syndrome;
- if (!ecc->read_oob)
- ecc->read_oob = nand_read_oob_syndrome;
- if (!ecc->write_oob)
- ecc->write_oob = nand_write_oob_syndrome;
- } else if (ecc->mode == NAND_ECC_HW_SYNDROME) {
- WARN(1, "CONFIG_NAND_ECC_HW_SYNDROME not enabled\n");
- ret = -ENOSYS;
+ switch (ecc->engine_type) {
+ case NAND_ECC_ENGINE_TYPE_ON_HOST:
+ ret = nand_set_ecc_on_host_ops(chip);
+ if (ret)
goto err_nand_manuf_cleanup;
- }
if (mtd->writesize >= ecc->size) {
if (!ecc->strength) {
@@ -5661,17 +6063,17 @@ int nand_scan_tail(struct nand_chip *chip)
}
pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
ecc->size, mtd->writesize);
- ecc->mode = NAND_ECC_SOFT;
- ecc->algo = NAND_ECC_HAMMING;
- case NAND_ECC_SOFT:
+ ecc->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ ecc->algo = NAND_ECC_ALGO_HAMMING;
+ fallthrough;
+
+ case NAND_ECC_ENGINE_TYPE_SOFT:
ret = nand_set_ecc_soft_ops(chip);
- if (ret) {
- ret = -EINVAL;
+ if (ret)
goto err_nand_manuf_cleanup;
- }
break;
- case NAND_ECC_ON_DIE:
+ case NAND_ECC_ENGINE_TYPE_ON_DIE:
if (!ecc->read_page || !ecc->write_page) {
WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
ret = -EINVAL;
@@ -5683,8 +6085,8 @@ int nand_scan_tail(struct nand_chip *chip)
ecc->write_oob = nand_write_oob_std;
break;
- case NAND_ECC_NONE:
- pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
+ case NAND_ECC_ENGINE_TYPE_NONE:
+ pr_warn("NAND_ECC_ENGINE_TYPE_NONE selected by board driver. This is not recommended!\n");
ecc->read_page = nand_read_page_raw;
ecc->write_page = nand_write_page_raw;
ecc->read_oob = nand_read_oob_std;
@@ -5697,7 +6099,7 @@ int nand_scan_tail(struct nand_chip *chip)
break;
default:
- WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
+ WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->engine_type);
ret = -EINVAL;
goto err_nand_manuf_cleanup;
}
@@ -5725,13 +6127,19 @@ int nand_scan_tail(struct nand_chip *chip)
* Set the number of read / write steps for one page depending on ECC
* mode.
*/
- ecc->steps = mtd->writesize / ecc->size;
+ if (!ecc->steps)
+ ecc->steps = mtd->writesize / ecc->size;
if (ecc->steps * ecc->size != mtd->writesize) {
WARN(1, "Invalid ECC parameters\n");
ret = -EINVAL;
goto err_nand_manuf_cleanup;
}
- ecc->total = ecc->steps * ecc->bytes;
+
+ if (!ecc->total) {
+ ecc->total = ecc->steps * ecc->bytes;
+ chip->base.ecc.ctx.total = ecc->total;
+ }
+
if (ecc->total > mtd->oobsize) {
WARN(1, "Total number of ECC bytes exceeded oobsize\n");
ret = -EINVAL;
@@ -5749,11 +6157,11 @@ int nand_scan_tail(struct nand_chip *chip)
mtd->oobavail = ret;
/* ECC sanity check: warn if it's too weak */
- if (!nand_ecc_strength_good(chip))
+ if (!nand_ecc_is_strong_enough(&chip->base))
pr_warn("WARNING: %s: the ECC used on your system (%db/%dB) is too weak compared to the one required by the NAND chip (%db/%dB)\n",
mtd->name, chip->ecc.strength, chip->ecc.size,
- chip->base.eccreq.strength,
- chip->base.eccreq.step_size);
+ nanddev_get_ecc_requirements(&chip->base)->strength,
+ nanddev_get_ecc_requirements(&chip->base)->step_size);
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
@@ -5774,8 +6182,8 @@ int nand_scan_tail(struct nand_chip *chip)
chip->pagecache.page = -1;
/* Large page NAND with SOFT_ECC should support subpage reads */
- switch (ecc->mode) {
- case NAND_ECC_SOFT:
+ switch (ecc->engine_type) {
+ case NAND_ECC_ENGINE_TYPE_SOFT:
if (chip->page_shift > 9)
chip->options |= NAND_SUBPAGE_READ;
break;
@@ -5799,9 +6207,10 @@ int nand_scan_tail(struct nand_chip *chip)
mtd->_sync = nand_sync;
mtd->_lock = nand_lock;
mtd->_unlock = nand_unlock;
+ mtd->_block_isreserved = nand_block_isreserved;
mtd->_block_isbad = nand_block_isbad;
mtd->_block_markbad = nand_block_markbad;
- mtd->_block_markgood = nand_block_markgood;
+ mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
/*
* Initialize bitflip_threshold to its default prior scan_bbt() call.
@@ -5823,6 +6232,8 @@ int nand_scan_tail(struct nand_chip *chip)
goto err_free_interface_config;
}
+ rawnand_late_check_supported_ops(chip);
+
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
@@ -5830,10 +6241,11 @@ int nand_scan_tail(struct nand_chip *chip)
/* Build bad block table */
ret = nand_create_bbt(chip);
if (ret)
- goto err_free_interface_config;
+ goto err_free_secure_regions;
return 0;
+err_free_secure_regions:
err_free_interface_config:
kfree(chip->best_interface_config);
@@ -5912,9 +6324,12 @@ EXPORT_SYMBOL(nand_scan_with_ids);
*/
void nand_cleanup(struct nand_chip *chip)
{
- if (chip->ecc.mode == NAND_ECC_SOFT &&
- chip->ecc.algo == NAND_ECC_BCH)
- nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT) {
+ if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING)
+ rawnand_sw_hamming_cleanup(chip);
+ else if (chip->ecc.algo == NAND_ECC_ALGO_BCH)
+ rawnand_sw_bch_cleanup(chip);
+ }
nanddev_cleanup(&chip->base);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c
index a86b5b2da3..a86b5b2da3 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/raw/nand_bbt.c
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/raw/nand_denali.c
index f9896defc8..8fef992ef8 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/raw/nand_denali.c
@@ -1231,7 +1231,8 @@ int denali_chip_init(struct denali_controller *denali,
chip->bbt_options |= NAND_BBT_USE_FLASH;
chip->bbt_options |= NAND_BBT_NO_OOB;
chip->options |= NAND_NO_SUBPAGE_WRITE;
- chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+ chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
+ chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED;
chip->ecc.read_page = denali_read_page;
chip->ecc.write_page = denali_write_page;
chip->ecc.read_page_raw = denali_read_page_raw;
diff --git a/drivers/mtd/nand/nand_denali_dt.c b/drivers/mtd/nand/raw/nand_denali_dt.c
index 5cb62cd733..d21cdc9756 100644
--- a/drivers/mtd/nand/nand_denali_dt.c
+++ b/drivers/mtd/nand/raw/nand_denali_dt.c
@@ -14,6 +14,7 @@
#include <io.h>
#include <of_mtd.h>
#include <errno.h>
+#include <globalvar.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
@@ -43,6 +44,18 @@ static const struct denali_dt_data denali_socfpga_data = {
.ecc_caps = &denali_socfpga_ecc_caps,
};
+enum of_binding_name {
+ DENALI_OF_BINDING_CHIP,
+ DENALI_OF_BINDING_CONTROLLER,
+ DENALI_OF_BINDING_AUTO,
+};
+
+static const char *denali_of_binding_names[] = {
+ "chip", "controller", "auto"
+};
+
+static int denali_of_binding;
+
/*
* Older versions of the kernel driver require the partition nodes
* to be direct subnodes of the controller node. Starting with Kernel
@@ -70,28 +83,50 @@ static int denali_partition_fixup(struct mtd_info *mtd, struct device_node *root
struct denali_controller,
controller);
struct device_node *np, *mtdnp = mtd_get_of_node(mtd);
+ struct device_node *chip_np, *controller_np;
char *name;
name = of_get_reproducible_name(mtdnp);
- np = of_find_node_by_reproducible_name(root, name);
+ chip_np = of_find_node_by_reproducible_name(root, name);
free(name);
- if (np) {
- dev_info(denali->dev, "Fixing up chip node %s\n",
- np->full_name);
- } else {
- name = of_get_reproducible_name(mtdnp->parent);
- np = of_find_node_by_reproducible_name(root, name);
- free(name);
-
- if (np)
- dev_info(denali->dev, "Fixing up controller node %s\n",
- np->full_name);
- }
+ name = of_get_reproducible_name(mtdnp->parent);
+ controller_np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+
+ if (!controller_np)
+ return -EINVAL;
+
+ switch (denali_of_binding) {
+ case DENALI_OF_BINDING_CHIP:
+ if (chip_np) {
+ np = chip_np;
+ } else {
+ np = of_new_node(controller_np, mtdnp->name);
+ of_property_write_u32(np, "reg", 0);
+ chip_np = np;
+ }
+ break;
+ case DENALI_OF_BINDING_CONTROLLER:
+ np = controller_np;
+ break;
+ case DENALI_OF_BINDING_AUTO:
+ default:
+ np = chip_np ? chip_np : controller_np;
+ break;
+ };
if (!np)
return -EINVAL;
+ dev_info(denali->dev, "Fixing up %s node %pOF\n",
+ chip_np ? "chip" : "controller", np);
+
+ if (!chip_np) {
+ of_property_write_bool(np, "#size-cells", false);
+ of_property_write_bool(np, "#address-cells", false);
+ }
+
return of_fixup_partitions(np, &mtd->cdev);
}
@@ -125,10 +160,18 @@ static int denali_dt_chip_init(struct denali_controller *denali,
nand_set_flash_node(&dchip->chip, chip_np);
}
- return denali_chip_init(denali, dchip);
+ ret = denali_chip_init(denali, dchip);
+ if (ret)
+ return ret;
+
+ dev_add_param_enum(&dchip->chip.base.mtd.dev, "denali_partition_binding",
+ NULL, NULL, &denali_of_binding, denali_of_binding_names,
+ ARRAY_SIZE(denali_of_binding_names), NULL);
+
+ return 0;
}
-static int denali_dt_probe(struct device_d *ofdev)
+static int denali_dt_probe(struct device *ofdev)
{
struct resource *iores;
struct denali_dt *dt;
@@ -189,7 +232,7 @@ static int denali_dt_probe(struct device_d *ofdev)
if (ret)
goto out_disable_clk;
- for_each_child_of_node(ofdev->device_node, np) {
+ for_each_child_of_node(ofdev->of_node, np) {
ret = denali_dt_chip_init(denali, np);
if (ret)
goto out_disable_clk;
@@ -211,8 +254,9 @@ static __maybe_unused struct of_device_id denali_nand_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, denali_nand_compatible);
-static struct driver_d denali_dt_driver = {
+static struct driver denali_dt_driver = {
.name = "denali-nand-dt",
.probe = denali_dt_probe,
.of_compatible = DRV_OF_COMPAT(denali_nand_compatible)
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c
index 58fb335bb4..58fb335bb4 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/raw/nand_ecc.c
diff --git a/drivers/mtd/nand/nand_esmt.c b/drivers/mtd/nand/raw/nand_esmt.c
index 2331eb174e..4412c407ae 100644
--- a/drivers/mtd/nand/nand_esmt.c
+++ b/drivers/mtd/nand/raw/nand_esmt.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Toradex AG
*
@@ -10,27 +10,32 @@
static void esmt_nand_decode_id(struct nand_chip *chip)
{
+ struct nand_device *base = &chip->base;
+ struct nand_ecc_props requirements = {};
+
nand_decode_ext_id(chip);
/* Extract ECC requirements from 5th id byte. */
if (chip->id.len >= 5 && nand_is_slc(chip)) {
- chip->base.eccreq.step_size = 512;
+ requirements.step_size = 512;
switch (chip->id.data[4] & 0x3) {
case 0x0:
- chip->base.eccreq.strength = 4;
+ requirements.strength = 4;
break;
case 0x1:
- chip->base.eccreq.strength = 2;
+ requirements.strength = 2;
break;
case 0x2:
- chip->base.eccreq.strength = 1;
+ requirements.strength = 1;
break;
default:
WARN(1, "Could not get ECC info");
- chip->base.eccreq.step_size = 0;
+ requirements.step_size = 0;
break;
}
}
+
+ nanddev_set_ecc_requirements(base, &requirements);
}
static int esmt_nand_init(struct nand_chip *chip)
diff --git a/drivers/mtd/nand/nand_fsl_ifc.c b/drivers/mtd/nand/raw/nand_fsl_ifc.c
index 64dc9c225f..1905e7b508 100644
--- a/drivers/mtd/nand/nand_fsl_ifc.c
+++ b/drivers/mtd/nand/raw/nand_fsl_ifc.c
@@ -46,7 +46,7 @@ struct fsl_ifc_ctrl {
/* mtd information per set */
struct fsl_ifc_mtd {
- struct device_d *dev;
+ struct device *dev;
struct nand_chip chip;
struct fsl_ifc_ctrl *ctrl;
uint32_t cs; /* On which chipsel NAND is connected */
@@ -300,7 +300,7 @@ static void fsl_ifc_cmdfunc(struct nand_chip *chip, uint32_t command,
ctrl->read_bytes = mtd->writesize + mtd->oobsize;
ctrl->index += column;
- if (chip->ecc.mode == NAND_ECC_HW)
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
ctrl->eccread = 1;
fsl_ifc_do_read(chip, 0, mtd);
@@ -322,7 +322,7 @@ static void fsl_ifc_cmdfunc(struct nand_chip *chip, uint32_t command,
return;
case NAND_CMD_RNDOUT:
- if (chip->ecc.mode == NAND_ECC_HW)
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
break;
ifc_out32(ctrl->rregs + FSL_IFC_NAND_BC, 0);
set_addr(mtd, column, -1, 0);
@@ -851,7 +851,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
}
ctrl = priv->ctrl = ifc_ctrl;
- if (priv->dev->device_node) {
+ if (priv->dev->of_node) {
int bank, banks;
/* find which chip select it is connected to */
@@ -960,11 +960,11 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
if (csor & CSOR_NAND_ECC_DEC_EN) {
- nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
} else {
- nand->ecc.mode = NAND_ECC_SOFT;
- nand->ecc.algo = NAND_ECC_HAMMING;
+ nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ nand->ecc.algo = NAND_ECC_ALGO_HAMMING;
}
if (ctrl->version >= FSL_IFC_V1_1_0) {
@@ -979,7 +979,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
return 0;
}
-static int fsl_ifc_nand_probe(struct device_d *dev)
+static int fsl_ifc_nand_probe(struct device *dev)
{
struct fsl_ifc_mtd *priv;
struct resource *iores;
@@ -1025,8 +1025,9 @@ static __maybe_unused struct of_device_id fsl_nand_compatible[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, fsl_nand_compatible);
-static struct driver_d fsl_ifc_driver = {
+static struct driver fsl_ifc_driver = {
.name = "fsl_nand",
.probe = fsl_ifc_nand_probe,
.of_compatible = DRV_OF_COMPAT(fsl_nand_compatible),
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c
index 0422ed53aa..4c0ea1ffa3 100644
--- a/drivers/mtd/nand/nand_hynix.c
+++ b/drivers/mtd/nand/raw/nand_hynix.c
@@ -7,6 +7,7 @@
*/
#include <linux/sizes.h>
+#include <linux/slab.h>
#include "internals.h"
@@ -30,7 +31,6 @@ struct hynix_read_retry {
/**
* struct hynix_nand - private Hynix NAND struct
- * @nand_technology: manufacturing process expressed in picometer
* @read_retry: read-retry information
*/
struct hynix_nand {
@@ -494,34 +494,36 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip,
static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
bool valid_jedecid)
{
+ struct nand_device *base = &chip->base;
+ struct nand_ecc_props requirements = {};
u8 ecc_level = (chip->id.data[4] >> 4) & 0x7;
if (valid_jedecid) {
/* Reference: H27UCG8T2E datasheet */
- chip->base.eccreq.step_size = 1024;
+ requirements.step_size = 1024;
switch (ecc_level) {
case 0:
- chip->base.eccreq.step_size = 0;
- chip->base.eccreq.strength = 0;
+ requirements.step_size = 0;
+ requirements.strength = 0;
break;
case 1:
- chip->base.eccreq.strength = 4;
+ requirements.strength = 4;
break;
case 2:
- chip->base.eccreq.strength = 24;
+ requirements.strength = 24;
break;
case 3:
- chip->base.eccreq.strength = 32;
+ requirements.strength = 32;
break;
case 4:
- chip->base.eccreq.strength = 40;
+ requirements.strength = 40;
break;
case 5:
- chip->base.eccreq.strength = 50;
+ requirements.strength = 50;
break;
case 6:
- chip->base.eccreq.strength = 60;
+ requirements.strength = 60;
break;
default:
/*
@@ -542,14 +544,14 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
if (nand_tech < 3) {
/* > 26nm, reference: H27UBG8T2A datasheet */
if (ecc_level < 5) {
- chip->base.eccreq.step_size = 512;
- chip->base.eccreq.strength = 1 << ecc_level;
+ requirements.step_size = 512;
+ requirements.strength = 1 << ecc_level;
} else if (ecc_level < 7) {
if (ecc_level == 5)
- chip->base.eccreq.step_size = 2048;
+ requirements.step_size = 2048;
else
- chip->base.eccreq.step_size = 1024;
- chip->base.eccreq.strength = 24;
+ requirements.step_size = 1024;
+ requirements.strength = 24;
} else {
/*
* We should never reach this case, but if that
@@ -562,18 +564,20 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
} else {
/* <= 26nm, reference: H27UBG8T2B datasheet */
if (!ecc_level) {
- chip->base.eccreq.step_size = 0;
- chip->base.eccreq.strength = 0;
+ requirements.step_size = 0;
+ requirements.strength = 0;
} else if (ecc_level < 5) {
- chip->base.eccreq.step_size = 512;
- chip->base.eccreq.strength = 1 << (ecc_level - 1);
+ requirements.step_size = 512;
+ requirements.strength = 1 << (ecc_level - 1);
} else {
- chip->base.eccreq.step_size = 1024;
- chip->base.eccreq.strength = 24 +
+ requirements.step_size = 1024;
+ requirements.strength = 24 +
(8 * (ecc_level - 5));
}
}
}
+
+ nanddev_set_ecc_requirements(base, &requirements);
}
static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
@@ -709,8 +713,21 @@ static int hynix_nand_init(struct nand_chip *chip)
return ret;
}
+static void hynix_fixup_onfi_param_page(struct nand_chip *chip,
+ struct nand_onfi_params *p)
+{
+ /*
+ * Certain chips might report a 0 on sdr_timing_mode field
+ * (bytes 129-130). This has been seen on H27U4G8F2GDA-BI.
+ * According to ONFI specification, bit 0 of this field "shall be 1".
+ * Forcibly set this bit.
+ */
+ p->sdr_timing_modes |= cpu_to_le16(BIT(0));
+}
+
const struct nand_manufacturer_ops hynix_nand_manuf_ops = {
.detect = hynix_nand_decode_id,
.init = hynix_nand_init,
.cleanup = hynix_nand_cleanup,
+ .fixup_onfi_param_page = hynix_fixup_onfi_param_page,
};
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
index b9945791a9..b9945791a9 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/raw/nand_ids.c
diff --git a/drivers/mtd/nand/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c
index 48997bd5f5..2b21e2d5b5 100644
--- a/drivers/mtd/nand/nand_jedec.c
+++ b/drivers/mtd/nand/raw/nand_jedec.c
@@ -121,8 +121,8 @@ int nand_jedec_detect(struct nand_chip *chip)
ecc = &p->ecc_info[0];
if (ecc->codeword_size >= 9) {
- chip->base.eccreq.strength = ecc->ecc_bits;
- chip->base.eccreq.step_size = 1 << ecc->codeword_size;
+ chip->base.ecc.requirements.strength = ecc->ecc_bits;
+ chip->base.ecc.requirements.step_size = 1 << ecc->codeword_size;
} else {
pr_warn("Invalid codeword size\n");
}
diff --git a/drivers/mtd/nand/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c
index 074a34e7f8..074a34e7f8 100644
--- a/drivers/mtd/nand/nand_legacy.c
+++ b/drivers/mtd/nand/raw/nand_legacy.c
diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c
index bfedc789fb..7c0b2f40e3 100644
--- a/drivers/mtd/nand/nand_macronix.c
+++ b/drivers/mtd/nand/raw/nand_macronix.c
@@ -6,6 +6,7 @@
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*/
+#include <linux/slab.h>
#include <linux/bitmap.h>
#include "internals.h"
@@ -31,6 +32,16 @@
#define MXIC_CMD_POWER_DOWN 0xB9
+#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90
+#define MACRONIX_30LFXG18AC_OTP_START_PAGE 2
+#define MACRONIX_30LFXG18AC_OTP_PAGES 30
+#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112
+#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \
+ (MACRONIX_30LFXG18AC_OTP_PAGES * \
+ MACRONIX_30LFXG18AC_OTP_PAGE_SIZE)
+
+#define MACRONIX_30LFXG18AC_OTP_EN BIT(0)
+
struct nand_onfi_vendor_macronix {
u8 reserved;
u8 reliability_func;
@@ -93,14 +104,13 @@ static void macronix_nand_onfi_init(struct nand_chip *chip)
struct nand_parameters *p = &chip->parameters;
struct nand_onfi_vendor_macronix *mxic;
struct device_node *dn = nand_get_flash_node(chip);
- int rand_otp = 0;
+ int rand_otp;
int ret;
if (!p->onfi)
return;
- if (of_find_property(dn, "mxic,enable-randomizer-otp", NULL))
- rand_otp = 1;
+ rand_otp = of_property_read_bool(dn, "mxic,enable-randomizer-otp");
mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor;
/* Subpage write is prohibited in randomizer operatoin */
@@ -316,6 +326,31 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip)
chip->ops.resume = mxic_nand_resume;
}
+static void macronix_nand_setup_otp(struct nand_chip *chip)
+{
+ static const char * const supported_otp_models[] = {
+ "MX30LF1G18AC",
+ "MX30LF2G18AC",
+ "MX30LF4G18AC",
+ };
+ struct mtd_info *mtd;
+
+ if (match_string(supported_otp_models,
+ ARRAY_SIZE(supported_otp_models),
+ chip->parameters.model) < 0)
+ return;
+
+ if (!chip->parameters.supports_set_get_features)
+ return;
+
+ bitmap_set(chip->parameters.get_feature_list,
+ ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1);
+ bitmap_set(chip->parameters.set_feature_list,
+ ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1);
+
+ mtd = nand_to_mtd(chip);
+}
+
static int macronix_nand_init(struct nand_chip *chip)
{
if (nand_is_slc(chip))
@@ -325,6 +360,7 @@ static int macronix_nand_init(struct nand_chip *chip)
macronix_nand_onfi_init(chip);
macronix_nand_block_protection_support(chip);
macronix_nand_deep_power_down_support(chip);
+ macronix_nand_setup_otp(chip);
return 0;
}
diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c
index d59be7ca7b..c019288190 100644
--- a/drivers/mtd/nand/nand_micron.c
+++ b/drivers/mtd/nand/raw/nand_micron.c
@@ -6,7 +6,6 @@
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*/
-#include <common.h>
#include <linux/slab.h>
#include "internals.h"
@@ -414,6 +413,8 @@ enum {
*/
static int micron_supports_on_die_ecc(struct nand_chip *chip)
{
+ const struct nand_ecc_props *requirements =
+ nanddev_get_ecc_requirements(&chip->base);
u8 id[5];
int ret;
@@ -426,7 +427,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
/*
* We only support on-die ECC of 4/512 or 8/512
*/
- if (chip->base.eccreq.strength != 4 && chip->base.eccreq.strength != 8)
+ if (requirements->strength != 4 && requirements->strength != 8)
return MICRON_ON_DIE_UNSUPPORTED;
/* 0x2 means on-die ECC is available. */
@@ -467,7 +468,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
/*
* We only support on-die ECC of 4/512 or 8/512
*/
- if (chip->base.eccreq.strength != 4 && chip->base.eccreq.strength != 8)
+ if (requirements->strength != 4 && requirements->strength != 8)
return MICRON_ON_DIE_UNSUPPORTED;
return MICRON_ON_DIE_SUPPORTED;
@@ -475,6 +476,9 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
static int micron_nand_init(struct nand_chip *chip)
{
+ struct nand_device *base = &chip->base;
+ const struct nand_ecc_props *requirements =
+ nanddev_get_ecc_requirements(base);
struct mtd_info *mtd = nand_to_mtd(chip);
struct micron_nand *micron;
int ondie;
@@ -498,13 +502,13 @@ static int micron_nand_init(struct nand_chip *chip)
ondie = micron_supports_on_die_ecc(chip);
if (ondie == MICRON_ON_DIE_MANDATORY &&
- chip->ecc.mode != NAND_ECC_ON_DIE) {
+ chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_DIE) {
pr_err("On-die ECC forcefully enabled, not supported\n");
ret = -EINVAL;
goto err_free_manuf_data;
}
- if (chip->ecc.mode == NAND_ECC_ON_DIE) {
+ if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE) {
if (ondie == MICRON_ON_DIE_UNSUPPORTED) {
pr_err("On-die ECC selected but not supported\n");
ret = -EINVAL;
@@ -524,7 +528,7 @@ static int micron_nand_init(struct nand_chip *chip)
* That's not needed for 8-bit ECC, because the status expose
* a better approximation of the number of bitflips in a page.
*/
- if (chip->base.eccreq.strength == 4) {
+ if (requirements->strength == 4) {
micron->ecc.rawbuf = kmalloc(mtd->writesize +
mtd->oobsize,
GFP_KERNEL);
@@ -534,17 +538,17 @@ static int micron_nand_init(struct nand_chip *chip)
}
}
- if (chip->base.eccreq.strength == 4)
+ if (requirements->strength == 4)
mtd_set_ooblayout(mtd,
&micron_nand_on_die_4_ooblayout_ops);
else
mtd_set_ooblayout(mtd,
&micron_nand_on_die_8_ooblayout_ops);
- chip->ecc.bytes = chip->base.eccreq.strength * 2;
+ chip->ecc.bytes = requirements->strength * 2;
chip->ecc.size = 512;
- chip->ecc.strength = chip->base.eccreq.strength;
- chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.strength = requirements->strength;
+ chip->ecc.algo = NAND_ECC_ALGO_BCH;
chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/raw/nand_mrvl_nfc.c
index 064853b344..0e2a2b639a 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/raw/nand_mrvl_nfc.c
@@ -137,7 +137,7 @@ struct mrvl_nand_variant {
struct mrvl_nand_host {
struct nand_chip chip;
struct mtd_partition *parts;
- struct device_d *dev;
+ struct device *dev;
struct clk *core_clk;
/* calculated from mrvl_nand_flash data */
@@ -299,6 +299,7 @@ static struct of_device_id mrvl_nand_dt_ids[] = {
},
{}
};
+MODULE_DEVICE_TABLE(of, mrvl_nand_dt_ids);
/* convert nano-seconds to nand flash controller clock cycles */
static int ns2cycle(int ns, unsigned long clk_rate)
@@ -931,7 +932,7 @@ static int pxa_ecc_strength1(struct mrvl_nand_host *host,
host->spare_size = 40;
host->ecc_size = 24;
host->ecc_bch = 0;
- ecc->mode = NAND_ECC_HW;
+ ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = 512;
ecc->strength = 1;
mtd_set_ecclayout(mtd, &ecc_layout_2KB_hwecc);
@@ -943,7 +944,7 @@ static int pxa_ecc_strength1(struct mrvl_nand_host *host,
host->spare_size = 8;
host->ecc_size = 8;
host->ecc_bch = 0;
- ecc->mode = NAND_ECC_HW;
+ ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = 512;
mtd_set_ecclayout(mtd, &ecc_layout_512B_hwecc);
ecc->strength = 1;
@@ -971,7 +972,7 @@ static int pxa_ecc_strength4(struct mrvl_nand_host *host,
host->spare_size = 32;
host->ecc_size = 32;
host->ecc_bch = 1;
- ecc->mode = NAND_ECC_HW;
+ ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = 2048;
mtd_set_ecclayout(mtd, &ecc_layout_2KB_bch4bit);
ecc->strength = 16;
@@ -983,7 +984,7 @@ static int pxa_ecc_strength4(struct mrvl_nand_host *host,
host->spare_size = 32;
host->ecc_size = 32;
host->ecc_bch = 1;
- ecc->mode = NAND_ECC_HW;
+ ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = 2048;
mtd_set_ecclayout(mtd, &ecc_layout_4KB_bch4bit);
ecc->strength = 16;
@@ -1011,7 +1012,7 @@ static int pxa_ecc_strength8(struct mrvl_nand_host *host,
host->spare_size = 0;
host->ecc_size = 32;
host->ecc_bch = 1;
- ecc->mode = NAND_ECC_HW;
+ ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = 1024;
mtd_set_ecclayout(mtd, &ecc_layout_4KB_bch8bit);
ecc->strength = 16;
@@ -1123,7 +1124,7 @@ static int mrvl_nand_scan(struct nand_chip *chip)
return nand_scan_tail(chip);
}
-static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
+static struct mrvl_nand_host *alloc_nand_resource(struct device *dev)
{
struct resource *iores;
struct mrvl_nand_platform_data *pdata;
@@ -1188,7 +1189,7 @@ static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
static int mrvl_nand_probe_dt(struct mrvl_nand_host *host)
{
- struct device_node *np = host->dev->device_node;
+ struct device_node *np = host->dev->of_node;
const struct of_device_id *match;
const struct mrvl_nand_variant *variant;
@@ -1219,7 +1220,7 @@ static int mrvl_nand_probe_dt(struct mrvl_nand_host *host)
return 0;
}
-static int mrvl_nand_probe(struct device_d *dev)
+static int mrvl_nand_probe(struct device *dev)
{
struct mrvl_nand_host *host;
struct nand_chip *chip;
@@ -1250,7 +1251,7 @@ static int mrvl_nand_probe(struct device_d *dev)
return ret;
}
-static struct driver_d mrvl_nand_driver = {
+static struct driver mrvl_nand_driver = {
.name = "mrvl_nand",
.probe = mrvl_nand_probe,
.of_compatible = DRV_OF_COMPAT(mrvl_nand_dt_ids),
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/raw/nand_mxs.c
index 8991758e2b..ca3471a226 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/raw/nand_mxs.c
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/bitfield.h>
#include <of_mtd.h>
#include <common.h>
#include <dma.h>
@@ -31,130 +32,11 @@
#include <io.h>
#include <dma/apbh-dma.h>
#include <stmp-device.h>
-#include <mach/generic.h>
+#include <mach/imx/generic.h>
+#include <soc/imx/gpmi-nand.h>
#include "internals.h"
-#define MX28_BLOCK_SFTRST (1 << 31)
-#define MX28_BLOCK_CLKGATE (1 << 30)
-
-#define GPMI_CTRL0 0x00000000
-#define GPMI_CTRL0_RUN (1 << 29)
-#define GPMI_CTRL0_DEV_IRQ_EN (1 << 28)
-/* Disable for now since we don't need it and it is different on MX23.
-#define GPMI_CTRL0_LOCK_CS (1 << 27)
-*/
-#define GPMI_CTRL0_UDMA (1 << 26)
-#define GPMI_CTRL0_COMMAND_MODE_MASK (0x3 << 24)
-#define GPMI_CTRL0_COMMAND_MODE_OFFSET 24
-#define GPMI_CTRL0_COMMAND_MODE_WRITE (0x0 << 24)
-#define GPMI_CTRL0_COMMAND_MODE_READ (0x1 << 24)
-#define GPMI_CTRL0_COMMAND_MODE_READ_AND_COMPARE (0x2 << 24)
-#define GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY (0x3 << 24)
-#define GPMI_CTRL0_WORD_LENGTH (1 << 23)
-/* Careful: Is 0x3 on MX23
-#define GPMI_CTRL0_CS_MASK (0x7 << 20)
-*/
-#define GPMI_CTRL0_CS_OFFSET 20
-#define GPMI_CTRL0_ADDRESS_MASK (0x7 << 17)
-#define GPMI_CTRL0_ADDRESS_OFFSET 17
-#define GPMI_CTRL0_ADDRESS_NAND_DATA (0x0 << 17)
-#define GPMI_CTRL0_ADDRESS_NAND_CLE (0x1 << 17)
-#define GPMI_CTRL0_ADDRESS_NAND_ALE (0x2 << 17)
-#define GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16)
-#define GPMI_CTRL0_XFER_COUNT_MASK 0xffff
-#define GPMI_CTRL0_XFER_COUNT_OFFSET 0
-
-#define GPMI_CTRL1 0x00000060
-#define GPMI_CTRL1_SET 0x00000064
-#define GPMI_CTRL1_CLR 0x00000068
-#define GPMI_CTRL1_DECOUPLE_CS (1 << 24)
-#define GPMI_CTRL1_WRN_DLY(d) (((d) & 0x3) << 22)
-#define GPMI_CTRL1_TIMEOUT_IRQ_EN (1 << 20)
-#define GPMI_CTRL1_GANGED_RDYBUSY (1 << 19)
-#define GPMI_CTRL1_BCH_MODE (1 << 18)
-#define GPMI_CTRL1_DLL_ENABLE (1 << 17)
-#define GPMI_CTRL1_HALF_PERIOD (1 << 16)
-#define GPMI_CTRL1_RDN_DELAY(d) (((d) & 0xf) << 12)
-#define GPMI_CTRL1_DMA2ECC_MODE (1 << 11)
-#define GPMI_CTRL1_DEV_IRQ (1 << 10)
-#define GPMI_CTRL1_TIMEOUT_IRQ (1 << 9)
-#define GPMI_CTRL1_BURST_EN (1 << 8)
-#define GPMI_CTRL1_ABORT_WAIT_REQUEST (1 << 7)
-#define GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL_MASK (0x7 << 4)
-#define GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL_OFFSET 4
-#define GPMI_CTRL1_DEV_RESET (1 << 3)
-#define GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2)
-#define GPMI_CTRL1_CAMERA_MODE (1 << 1)
-#define GPMI_CTRL1_GPMI_MODE (1 << 0)
-
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
-
-#define GPMI_TIMING0 0x00000070
-
-#define GPMI_TIMING0_ADDRESS_SETUP(d) (((d) & 0xff) << 16)
-#define GPMI_TIMING0_DATA_HOLD(d) (((d) & 0xff) << 8)
-#define GPMI_TIMING0_DATA_SETUP(d) (((d) & 0xff) << 0)
-
-#define GPMI_TIMING1 0x00000080
-#define GPMI_TIMING1_BUSY_TIMEOUT(d) (((d) & 0xffff) << 16)
-
-#define GPMI_ECCCTRL_HANDLE_MASK (0xffff << 16)
-#define GPMI_ECCCTRL_HANDLE_OFFSET 16
-#define GPMI_ECCCTRL_ECC_CMD_MASK (0x3 << 13)
-#define GPMI_ECCCTRL_ECC_CMD_OFFSET 13
-#define GPMI_ECCCTRL_ECC_CMD_DECODE (0x0 << 13)
-#define GPMI_ECCCTRL_ECC_CMD_ENCODE (0x1 << 13)
-#define GPMI_ECCCTRL_ENABLE_ECC (1 << 12)
-#define GPMI_ECCCTRL_BUFFER_MASK_MASK 0x1ff
-#define GPMI_ECCCTRL_BUFFER_MASK_OFFSET 0
-#define GPMI_ECCCTRL_BUFFER_MASK_BCH_AUXONLY 0x100
-#define GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE 0x1ff
-
-#define GPMI_STAT 0x000000b0
-#define GPMI_STAT_READY_BUSY_OFFSET 24
-
-#define GPMI_DEBUG 0x000000c0
-#define GPMI_DEBUG_READY0_OFFSET 28
-
-#define GPMI_VERSION 0x000000d0
-#define GPMI_VERSION_MINOR_OFFSET 16
-#define GPMI_VERSION_TYPE_MX23 0x0300
-
-#define BCH_CTRL 0x00000000
-#define BCH_CTRL_COMPLETE_IRQ (1 << 0)
-#define BCH_CTRL_COMPLETE_IRQ_EN (1 << 8)
-
-#define BCH_LAYOUTSELECT 0x00000070
-
-#define BCH_FLASH0LAYOUT0 0x00000080
-#define BCH_FLASHLAYOUT0_NBLOCKS_MASK (0xff << 24)
-#define BCH_FLASHLAYOUT0_NBLOCKS_OFFSET 24
-#define BCH_FLASHLAYOUT0_META_SIZE_MASK (0xff << 16)
-#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
-#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
-#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
-#define IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET 11
-
-#define BCH_FLASH0LAYOUT1 0x00000090
-#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
-#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
-#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
-#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
-#define IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET 11
-
-#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
-
-#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
-#define MXS_NAND_METADATA_SIZE 10
-
-#define MXS_NAND_COMMAND_BUFFER_SIZE 32
-
-#define MXS_NAND_BCH_TIMEOUT 10000
-
enum gpmi_type {
GPMI_MXS,
GPMI_IMX6,
@@ -194,7 +76,7 @@ struct nand_timing {
};
struct mxs_nand_info {
- struct device_d *dev;
+ struct device *dev;
struct nand_chip nand_chip;
void __iomem *io_base;
void __iomem *bch_base;
@@ -220,7 +102,7 @@ struct mxs_nand_info {
loff_t to, struct mtd_oob_ops *ops);
/* DMA descriptors */
- struct mxs_dma_desc **desc;
+ struct mxs_dma_cmd *desc;
uint32_t desc_index;
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
@@ -236,16 +118,16 @@ static inline int mxs_nand_is_imx6(struct mxs_nand_info *info)
return info->type == GPMI_IMX6;
}
-static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
+static struct mxs_dma_cmd *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
{
- struct mxs_dma_desc *desc;
+ struct mxs_dma_cmd *desc;
if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) {
printf("MXS NAND: Too many DMA descriptors requested\n");
return NULL;
}
- desc = info->desc[info->desc_index];
+ desc = &info->desc[info->desc_index];
info->desc_index++;
return desc;
@@ -254,12 +136,11 @@ static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
static void mxs_nand_return_dma_descs(struct mxs_nand_info *info)
{
int i;
- struct mxs_dma_desc *desc;
+ struct mxs_dma_cmd *desc;
for (i = 0; i < info->desc_index; i++) {
- desc = info->desc[i];
- memset(desc, 0, sizeof(struct mxs_dma_desc));
- desc->address = (dma_addr_t)desc;
+ desc = &info->desc[i];
+ memset(desc, 0, sizeof(struct mxs_dma_cmd));
}
info->desc_index = 0;
@@ -442,7 +323,7 @@ static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info)
static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctrl)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
@@ -482,26 +363,24 @@ static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctr
/* Compile the DMA descriptor -- a descriptor that sends command. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM |
- MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
- (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET);
+ MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(3) |
+ MXS_DMA_DESC_XFER_COUNT(nand_info->cmd_queue_len);
- d->cmd.address = (dma_addr_t)nand_info->cmd_buf;
+ d->address = (dma_addr_t)nand_info->cmd_buf;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_CLE |
GPMI_CTRL0_ADDRESS_INCREMENT |
nand_info->cmd_queue_len;
- mxs_dma_desc_append(channel, d);
-
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret)
printf("MXS NAND: Error sending command (%d)\n", ret);
@@ -597,7 +476,7 @@ static void mxs_nand_swap_block_mark(struct nand_chip *chip,
static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
@@ -613,23 +492,21 @@ static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
/* Compile the DMA descriptor - a descriptor that reads data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
- (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
- (length << MXS_DMA_DESC_BYTES_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(1) |
+ MXS_DMA_DESC_XFER_COUNT(length);
- d->cmd.address = (dma_addr_t)nand_info->data_buf;
+ d->address = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_READ |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
length;
- mxs_dma_desc_append(channel, d);
-
/*
* A DMA descriptor that waits for the command to end and the chip to
* become ready.
@@ -639,23 +516,21 @@ static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
* did that and no one has re-thought it yet.
*/
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM |
- MXS_DMA_DESC_WAIT4END | (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(4);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- mxs_dma_desc_append(channel, d);
-
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
printf("MXS NAND: DMA read error\n");
goto rtn;
@@ -674,7 +549,7 @@ static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
int length)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
@@ -692,25 +567,23 @@ static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
/* Compile the DMA descriptor - a descriptor that writes data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
- (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
- (length << MXS_DMA_DESC_BYTES_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(4) |
+ MXS_DMA_DESC_XFER_COUNT(length);
- d->cmd.address = (dma_addr_t)nand_info->data_buf;
+ d->address = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
length;
- mxs_dma_desc_append(channel, d);
-
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret)
printf("MXS NAND: DMA write error\n");
@@ -732,138 +605,155 @@ static void mxs_nand_config_bch(struct nand_chip *chip, int readlen)
struct mxs_nand_info *nand_info = chip->priv;
int chunk_size;
void __iomem *bch_regs = nand_info->bch_base;
+ u32 fl0, fl1;
if (mxs_nand_is_imx6(nand_info))
chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
else
chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
- writel((mxs_nand_ecc_chunk_cnt(readlen) - 1)
- << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET |
- MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET |
- (chip->ecc.strength >> 1)
- << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET |
- chunk_size,
- bch_regs + BCH_FLASH0LAYOUT0);
-
- writel(readlen << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET |
- (chip->ecc.strength >> 1)
- << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET |
- chunk_size,
- bch_regs + BCH_FLASH0LAYOUT1);
+ fl0 = FIELD_PREP(BCH_FLASHLAYOUT0_NBLOCKS, mxs_nand_ecc_chunk_cnt(readlen) - 1);
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_META_SIZE, MXS_NAND_METADATA_SIZE);
+ if (mxs_nand_is_imx6(nand_info))
+ fl0 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT0_ECC0, chip->ecc.strength >> 1);
+ else
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_ECC0, chip->ecc.strength >> 1);
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_DATA0_SIZE, chunk_size);
+ writel(fl0, bch_regs + BCH_FLASH0LAYOUT0);
+
+ fl1 = FIELD_PREP(BCH_FLASHLAYOUT1_PAGE_SIZE, readlen);
+ if (mxs_nand_is_imx6(nand_info))
+ fl1 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT1_ECCN, chip->ecc.strength >> 1);
+ else
+ fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_ECCN, chip->ecc.strength >> 1);
+
+ fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_DATAN_SIZE, chunk_size);
+ writel(fl1, bch_regs + BCH_FLASH0LAYOUT1);
}
-/*
- * Read a page from NAND.
- */
-static int __mxs_nand_ecc_read_page(struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page,
- int readlen)
+static int mxs_nand_do_bch_read(struct nand_chip *chip, int channel, int readtotal,
+ bool randomizer, int page)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
- uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
- uint32_t corrected = 0, failed = 0;
- uint8_t *status;
- unsigned int max_bitflips = 0;
- int i, ret, readtotal, nchunks;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- readlen = roundup(readlen, MXS_NAND_CHUNK_DATA_CHUNK_SIZE);
- nchunks = mxs_nand_ecc_chunk_cnt(readlen);
- readtotal = MXS_NAND_METADATA_SIZE;
- readtotal += MXS_NAND_CHUNK_DATA_CHUNK_SIZE * nchunks;
- readtotal += DIV_ROUND_UP(13 * chip->ecc.strength * nchunks, 8);
-
- mxs_nand_config_bch(chip, readtotal);
+ struct mxs_dma_cmd *d;
+ int ret;
/* Compile the DMA descriptor - wait for ready. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END |
- (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(1);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- mxs_dma_desc_append(channel, d);
-
/* Compile the DMA descriptor - enable the BCH block and read. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
- MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(6);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_READ |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
readtotal;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] =
+ d->pio_words[1] = 0;
+ d->pio_words[2] =
GPMI_ECCCTRL_ENABLE_ECC |
GPMI_ECCCTRL_ECC_CMD_DECODE |
GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
- d->cmd.pio_words[3] = readtotal;
- d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
-
- mxs_dma_desc_append(channel, d);
+ d->pio_words[3] = readtotal;
+ d->pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+
+ if (randomizer) {
+ d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+ GPMI_ECCCTRL_RANDOMIZER_TYPE2;
+ d->pio_words[3] |= (page % 256) << 16;
+ }
/* Compile the DMA descriptor - disable the BCH block. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END |
- (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(3);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
readtotal;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] = 0;
-
- mxs_dma_desc_append(channel, d);
+ d->pio_words[1] = 0;
+ d->pio_words[2] = 0;
/* Compile the DMA descriptor - deassert the NAND lock and interrupt. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM;
- d->cmd.address = 0;
-
- mxs_dma_desc_append(channel, d);
+ d->address = 0;
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
- printf("MXS NAND: DMA read error (ecc)\n");
- goto rtn;
+ dev_err(nand_info->dev, "MXS NAND: DMA read error (ecc)\n");
+ goto out;
}
ret = mxs_nand_wait_for_bch_complete(nand_info);
if (ret) {
- printf("MXS NAND: BCH read timeout\n");
- goto rtn;
+ dev_err(nand_info->dev, "MXS NAND: BCH read timeout\n");
+ goto out;
}
+out:
+ mxs_nand_return_dma_descs(nand_info);
+
+ return ret;
+}
+
+/*
+ * Read a page from NAND.
+ */
+static int __mxs_nand_ecc_read_page(struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page,
+ int readlen)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxs_nand_info *nand_info = chip->priv;
+ uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
+ uint32_t corrected = 0, failed = 0;
+ uint8_t *status;
+ unsigned int max_bitflips = 0;
+ int i, ret, readtotal, nchunks;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ readlen = roundup(readlen, MXS_NAND_CHUNK_DATA_CHUNK_SIZE);
+ nchunks = mxs_nand_ecc_chunk_cnt(readlen);
+ readtotal = MXS_NAND_METADATA_SIZE;
+ readtotal += MXS_NAND_CHUNK_DATA_CHUNK_SIZE * nchunks;
+ readtotal += DIV_ROUND_UP(13 * chip->ecc.strength * nchunks, 8);
+
+ mxs_nand_config_bch(chip, readtotal);
+
+ mxs_nand_do_bch_read(chip, channel, readtotal, false, page);
+
/* Read DMA completed, now do the mark swapping. */
mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
@@ -943,7 +833,7 @@ static int __mxs_nand_ecc_read_page(struct nand_chip *chip,
chip->oob_poi[0] = nand_info->oob_buf[0];
ret = 0;
-rtn:
+
mxs_nand_return_dma_descs(nand_info);
mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
@@ -984,7 +874,7 @@ static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret = 0;
@@ -998,31 +888,29 @@ static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
/* Compile the DMA descriptor - write data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
- (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(6);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] =
+ d->pio_words[1] = 0;
+ d->pio_words[2] =
GPMI_ECCCTRL_ENABLE_ECC |
GPMI_ECCCTRL_ECC_CMD_ENCODE |
GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
- d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize);
- d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
-
- mxs_dma_desc_append(channel, d);
+ d->pio_words[3] = (mtd->writesize + mtd->oobsize);
+ d->pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->pio_words[5] = (dma_addr_t)nand_info->oob_buf;
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
printf("MXS NAND: DMA write error\n");
goto rtn;
@@ -1235,6 +1123,160 @@ static int mxs_nand_block_markbad(struct nand_chip *chip , loff_t ofs)
return 0;
}
+int mxs_nand_read_fcb_bch62(unsigned int block, void *buf, size_t size)
+{
+ struct nand_chip *chip;
+ struct mxs_nand_info *nand_info;
+ struct mtd_info *mtd = mxs_nand_mtd;
+ int ret;
+ int page;
+ int flips = 0;
+ uint8_t *status;
+ int i;
+
+ if (!mtd)
+ return -ENODEV;
+
+ chip = mtd_to_nand(mtd);
+ nand_info = chip->priv;
+
+ nand_select_target(chip, 0);
+
+ page = block * (mtd->erasesize / mtd->writesize);
+
+ mxs_nand_mode_fcb_62bit(nand_info->bch_base);
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ ret = mxs_nand_do_bch_read(chip, 0, BCH62_PAGESIZE, true, page);
+ if (ret)
+ goto out;
+
+ /* Read DMA completed, now do the mark swapping. */
+ mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
+
+ /* Loop over status bytes, accumulating ECC status. */
+ status = nand_info->oob_buf + 32;
+
+ for (i = 0; i < 8; i++) {
+ switch (status[i]) {
+ case 0x0:
+ break;
+ case 0xff:
+ /*
+ * A status of 0xff means the chunk is erased, but due to
+ * the randomizer we see this as random data. Explicitly
+ * memset it.
+ */
+ memset(nand_info->data_buf + 0x80 * i, 0xff, 0x80);
+ break;
+ case 0xfe:
+ ret = -EBADMSG;
+ goto out;
+ default:
+ flips += status[0];
+ break;
+ }
+ }
+
+ memcpy(buf, nand_info->data_buf, size);
+
+out:
+ mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
+ nand_deselect_target(chip);
+
+ return ret;
+}
+
+int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size)
+{
+ struct nand_chip *chip;
+ struct mtd_info *mtd = mxs_nand_mtd;
+ struct mxs_nand_info *nand_info;
+ struct mxs_dma_cmd *d;
+ uint32_t channel;
+ int ret = 0;
+ int page;
+
+ if (!mtd)
+ return -ENODEV;
+
+ if (size > BCH62_WRITESIZE)
+ return -EINVAL;
+
+ chip = mtd_to_nand(mtd);
+ nand_info = chip->priv;
+ channel = nand_info->dma_channel_base;
+
+ mxs_nand_mode_fcb_62bit(nand_info->bch_base);
+
+ nand_select_target(chip, 0);
+
+ page = block * (mtd->erasesize / mtd->writesize);
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ memset(nand_info->data_buf, 0x0, BCH62_WRITESIZE);
+ memcpy(nand_info->data_buf, buf, size);
+
+ /* Handle block mark swapping. */
+ mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
+
+ /* Compile the DMA descriptor - write data. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
+ MXS_DMA_DESC_PIO_WORDS(6);
+
+ d->address = 0;
+
+ d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE |
+ GPMI_CTRL0_WORD_LENGTH |
+ GPMI_CTRL0_ADDRESS_NAND_DATA;
+ d->pio_words[1] = 0;
+ d->pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC |
+ GPMI_ECCCTRL_ECC_CMD_ENCODE |
+ GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
+ d->pio_words[3] = BCH62_PAGESIZE;
+ d->pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+
+ d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+ GPMI_ECCCTRL_RANDOMIZER_TYPE2;
+ /*
+ * Write NAND page number needed to be randomized
+ * to GPMI_ECCCOUNT register.
+ *
+ * The value is between 0-255. For additional details
+ * check 9.6.6.4 of i.MX7D Applications Processor reference
+ */
+ d->pio_words[3] |= (page % 256) << 16;
+
+ /* Execute the DMA chain. */
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
+ if (ret) {
+ dev_err(nand_info->dev, "MXS NAND: DMA write error: %d\n", ret);
+ goto out;
+ }
+
+ ret = mxs_nand_wait_for_bch_complete(nand_info);
+ if (ret) {
+ dev_err(nand_info->dev, "MXS NAND: BCH write timeout\n");
+ goto out;
+ }
+
+out:
+ mxs_nand_return_dma_descs(nand_info);
+
+ if (!ret)
+ ret = nand_prog_page_end_op(chip);
+
+ mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
+ nand_deselect_target(chip);
+
+ return ret;
+}
+
/*
* Nominally, the purpose of this function is to look for or create the bad
* block table. In fact, since the we call this function at the very end of
@@ -1323,20 +1365,13 @@ static int mxs_nand_hw_init(struct mxs_nand_info *info)
{
void __iomem *gpmi_regs = info->io_base;
void __iomem *bch_regs = info->bch_base;
- int i = 0, ret;
+ int ret;
u32 val;
- info->desc = malloc(sizeof(struct mxs_dma_desc *) *
- MXS_NAND_DMA_DESCRIPTOR_COUNT);
+ info->desc = dma_alloc_coherent(sizeof(struct mxs_dma_cmd) * MXS_NAND_DMA_DESCRIPTOR_COUNT,
+ DMA_ADDRESS_BROKEN);
if (!info->desc)
- goto err1;
-
- /* Allocate the DMA descriptors. */
- for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) {
- info->desc[i] = mxs_dma_desc_alloc();
- if (!info->desc[i])
- goto err2;
- }
+ return -ENOMEM;
/* Reset the GPMI block. */
ret = stmp_reset_block(gpmi_regs + GPMI_CTRL0, 0);
@@ -1363,24 +1398,17 @@ static int mxs_nand_hw_init(struct mxs_nand_info *info)
writel(val, gpmi_regs + GPMI_CTRL1);
return 0;
-
-err2:
- free(info->desc);
-err1:
- for (--i; i >= 0; i--)
- mxs_dma_desc_free(info->desc[i]);
- printf("MXS NAND: Unable to allocate DMA descriptors\n");
- return -ENOMEM;
}
-static void mxs_nand_probe_dt(struct device_d *dev, struct mxs_nand_info *nand_info)
+static void mxs_nand_probe_dt(struct device *dev,
+ struct mxs_nand_info *nand_info)
{
struct nand_chip *chip = &nand_info->nand_chip;
if (!IS_ENABLED(CONFIG_OFTREE))
return;
- if (of_get_nand_on_flash_bbt(dev->device_node))
+ if (of_get_nand_on_flash_bbt(dev->of_node))
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
}
@@ -2130,7 +2158,7 @@ static void mxs_nand_setup_timing(struct mxs_nand_info *info)
}
}
-static int mxs_nand_probe(struct device_d *dev)
+static int mxs_nand_probe(struct device *dev)
{
struct resource *iores;
struct mxs_nand_info *nand_info;
@@ -2214,7 +2242,7 @@ static int mxs_nand_probe(struct device_d *dev)
chip->ecc.read_oob = mxs_nand_ecc_read_oob;
chip->ecc.write_oob = mxs_nand_ecc_write_oob;
- chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
/* first scan to find the device and get the page size */
err = nand_scan_ident(chip, 4, NULL);
@@ -2278,8 +2306,9 @@ static __maybe_unused struct of_device_id gpmi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, gpmi_dt_ids);
-static struct driver_d mxs_nand_driver = {
+static struct driver mxs_nand_driver = {
.name = "mxs_nand",
.probe = mxs_nand_probe,
.of_compatible = DRV_OF_COMPAT(gpmi_dt_ids),
diff --git a/drivers/mtd/nand/nand_omap_bch_decoder.c b/drivers/mtd/nand/raw/nand_omap_bch_decoder.c
index eb51e608e4..eb51e608e4 100644
--- a/drivers/mtd/nand/nand_omap_bch_decoder.c
+++ b/drivers/mtd/nand/raw/nand_omap_bch_decoder.c
diff --git a/drivers/mtd/nand/nand_omap_bch_decoder.h b/drivers/mtd/nand/raw/nand_omap_bch_decoder.h
index a8c71f77f8..a8c71f77f8 100644
--- a/drivers/mtd/nand/nand_omap_bch_decoder.h
+++ b/drivers/mtd/nand/raw/nand_omap_bch_decoder.h
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/raw/nand_omap_gpmc.c
index 9c4f267196..2b1fb07f93 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/raw/nand_omap_gpmc.c
@@ -12,7 +12,7 @@
* A typical device registration is as follows:
*
* @code
- * static struct device_d my_nand_device = {
+ * static struct device my_nand_device = {
* .name = "gpmc_nand",
* .id = some identifier you need to show.. e.g. "gpmc_nand0"
* .resource[0].start = GPMC base address
@@ -54,7 +54,7 @@
* Copyright (c) 2004 Micron Technology Inc.
* Copyright (c) 2004 David Brownell
*
- *
+ *
*/
#include <common.h>
@@ -68,8 +68,8 @@
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <io.h>
-#include <mach/gpmc.h>
-#include <mach/gpmc_nand.h>
+#include <mach/omap/gpmc.h>
+#include <mach/omap/gpmc_nand.h>
#include <platform_data/elm.h>
#include "nand_omap_bch_decoder.h"
@@ -109,7 +109,7 @@ static const char *ecc_mode_strings[] = {
/** internal structure maintained for nand information */
struct gpmc_nand_info {
- struct device_d *pdev;
+ struct device *pdev;
struct gpmc_nand_platform_data *pdata;
struct nand_chip nand;
int gpmc_cs;
@@ -1054,7 +1054,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
nand->ecc.write_page = NULL;
nand->ecc.read_oob = NULL;
nand->ecc.write_oob = NULL;
- nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
nand->options &= ~NAND_SUBPAGE_READ;
}
@@ -1138,7 +1138,8 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
break;
case OMAP_ECC_SOFT:
minfo->ecclayout = NULL;
- nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ nand->ecc.algo = NAND_ECC_ALGO_HAMMING;
oinfo->nand.ecc.strength = 1;
break;
default:
@@ -1184,7 +1185,7 @@ static int gpmc_set_buswidth(struct nand_chip *chip, int buswidth)
*
* @return -failure reason or give 0
*/
-static int gpmc_nand_probe(struct device_d *pdev)
+static int gpmc_nand_probe(struct device *pdev)
{
struct resource *iores;
struct gpmc_nand_info *oinfo;
@@ -1343,7 +1344,7 @@ out_release_mem:
}
/** GMPC nand driver -> device registered by platforms */
-static struct driver_d gpmc_nand_driver = {
+static struct driver gpmc_nand_driver = {
.name = "gpmc_nand",
.probe = gpmc_nand_probe,
};
diff --git a/drivers/mtd/nand/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c
index c6b187be02..9dc2ee5fcf 100644
--- a/drivers/mtd/nand/nand_onfi.c
+++ b/drivers/mtd/nand/raw/nand_onfi.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
@@ -35,6 +35,8 @@ u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
struct nand_onfi_params *p)
{
+ struct nand_device *base = &chip->base;
+ struct nand_ecc_props requirements;
struct onfi_ext_param_page *ep;
struct onfi_ext_section *s;
struct onfi_ext_ecc_info *ecc;
@@ -95,8 +97,10 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
goto ext_out;
}
- chip->base.eccreq.strength = ecc->ecc_bits;
- chip->base.eccreq.step_size = 1 << ecc->codeword_size;
+ requirements.strength = ecc->ecc_bits;
+ requirements.step_size = 1 << ecc->codeword_size;
+ nanddev_set_ecc_requirements(base, &requirements);
+
ret = 0;
ext_out:
@@ -140,6 +144,7 @@ static void nand_bit_wise_majority(const void **srcbufs,
*/
int nand_onfi_detect(struct nand_chip *chip)
{
+ struct nand_device *base = &chip->base;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg;
struct nand_onfi_params *p = NULL, *pbuf;
@@ -162,8 +167,7 @@ int nand_onfi_detect(struct nand_chip *chip)
if (!pbuf)
return -ENOMEM;
- if (!nand_has_exec_op(chip) ||
- !nand_read_data_op(chip, &pbuf[0], sizeof(*pbuf), true, true))
+ if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read)
use_datain = true;
for (i = 0; i < ONFI_PARAM_PAGES; i++) {
@@ -232,7 +236,7 @@ int nand_onfi_detect(struct nand_chip *chip)
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
sanitize_string(p->model, sizeof(p->model));
- chip->parameters.model = strdup(p->model);
+ chip->parameters.model = kstrdup(p->model, GFP_KERNEL);
if (!chip->parameters.model) {
ret = -ENOMEM;
goto free_onfi_param_page;
@@ -266,8 +270,12 @@ int nand_onfi_detect(struct nand_chip *chip)
chip->options |= NAND_BUSWIDTH_16;
if (p->ecc_bits != 0xff) {
- chip->base.eccreq.strength = p->ecc_bits;
- chip->base.eccreq.step_size = 512;
+ struct nand_ecc_props requirements = {
+ .strength = p->ecc_bits,
+ .step_size = 512,
+ };
+
+ nanddev_set_ecc_requirements(base, &requirements);
} else if (onfi_version >= 21 &&
(le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
@@ -296,6 +304,9 @@ int nand_onfi_detect(struct nand_chip *chip)
ONFI_FEATURE_ADDR_TIMING_MODE, 1);
}
+ if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_READ_CACHE)
+ chip->parameters.supports_read_cache = true;
+
onfi = kzalloc(sizeof(*onfi), GFP_KERNEL);
if (!onfi) {
ret = -ENOMEM;
@@ -307,7 +318,10 @@ int nand_onfi_detect(struct nand_chip *chip)
onfi->tBERS = le16_to_cpu(p->t_bers);
onfi->tR = le16_to_cpu(p->t_r);
onfi->tCCS = le16_to_cpu(p->t_ccs);
- onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode);
+ onfi->fast_tCAD = le16_to_cpu(p->nvddr_nvddr2_features) & BIT(0);
+ onfi->sdr_timing_modes = le16_to_cpu(p->sdr_timing_modes);
+ if (le16_to_cpu(p->features) & ONFI_FEATURE_NV_DDR)
+ onfi->nvddr_timing_modes = le16_to_cpu(p->nvddr_timing_modes);
onfi->vendor_revision = le16_to_cpu(p->vendor_revision);
memcpy(onfi->vendor, p->vendor, sizeof(p->vendor));
chip->parameters.onfi = onfi;
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/raw/nand_orion.c
index bb41b006e2..c7fa4a08e9 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/raw/nand_orion.c
@@ -73,10 +73,10 @@ static void orion_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
buf[i++] = readb(io_base);
}
-static int orion_nand_probe(struct device_d *dev)
+static int orion_nand_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *dev_node = dev->device_node;
+ struct device_node *dev_node = dev->of_node;
struct orion_nand *priv;
struct mtd_info *mtd;
struct nand_chip *chip;
@@ -117,7 +117,8 @@ static int orion_nand_probe(struct device_d *dev)
chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W = io_base;
chip->legacy.cmd_ctrl = orion_nand_cmd_ctrl;
chip->legacy.read_buf = orion_nand_read_buf;
- chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+ chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
WARN(width > 16, "%d bit bus width out of range", width);
if (width == 16)
@@ -147,8 +148,9 @@ static __maybe_unused struct of_device_id orion_nand_compatible[] = {
{ .compatible = "marvell,orion-nand", },
{},
};
+MODULE_DEVICE_TABLE(of, orion_nand_compatible);
-static struct driver_d orion_nand_driver = {
+static struct driver orion_nand_driver = {
.name = "orion_nand",
.probe = orion_nand_probe,
.of_compatible = DRV_OF_COMPAT(orion_nand_compatible),
diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c
index 3a4a19e808..0be6b75638 100644
--- a/drivers/mtd/nand/nand_samsung.c
+++ b/drivers/mtd/nand/raw/nand_samsung.c
@@ -10,6 +10,8 @@
static void samsung_nand_decode_id(struct nand_chip *chip)
{
+ struct nand_device *base = &chip->base;
+ struct nand_ecc_props requirements = {};
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg;
@@ -71,23 +73,23 @@ static void samsung_nand_decode_id(struct nand_chip *chip)
/* Extract ECC requirements from 5th id byte*/
extid = (chip->id.data[4] >> 4) & 0x07;
if (extid < 5) {
- chip->base.eccreq.step_size = 512;
- chip->base.eccreq.strength = 1 << extid;
+ requirements.step_size = 512;
+ requirements.strength = 1 << extid;
} else {
- chip->base.eccreq.step_size = 1024;
+ requirements.step_size = 1024;
switch (extid) {
case 5:
- chip->base.eccreq.strength = 24;
+ requirements.strength = 24;
break;
case 6:
- chip->base.eccreq.strength = 40;
+ requirements.strength = 40;
break;
case 7:
- chip->base.eccreq.strength = 60;
+ requirements.strength = 60;
break;
default:
WARN(1, "Could not decode ECC info");
- chip->base.eccreq.step_size = 0;
+ requirements.step_size = 0;
}
}
} else {
@@ -97,8 +99,8 @@ static void samsung_nand_decode_id(struct nand_chip *chip)
switch (chip->id.data[1]) {
/* K9F4G08U0D-S[I|C]B0(T00) */
case 0xDC:
- chip->base.eccreq.step_size = 512;
- chip->base.eccreq.strength = 1;
+ requirements.step_size = 512;
+ requirements.strength = 1;
break;
/* K9F1G08U0E 21nm chips do not support subpage write */
@@ -112,6 +114,8 @@ static void samsung_nand_decode_id(struct nand_chip *chip)
}
}
}
+
+ nanddev_set_ecc_requirements(base, &requirements);
}
static int samsung_nand_init(struct nand_chip *chip)
diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c
index 1338133e81..a10dad927e 100644
--- a/drivers/mtd/nand/nand_timings.c
+++ b/drivers/mtd/nand/raw/nand_timings.c
@@ -292,6 +292,261 @@ static const struct nand_interface_config onfi_sdr_timings[] = {
},
};
+static const struct nand_interface_config onfi_nvddr_timings[] = {
+ /* Mode 0 */
+ {
+ .type = NAND_NVDDR_IFACE,
+ .timings.mode = 0,
+ .timings.nvddr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tAC_min = 3000,
+ .tAC_max = 25000,
+ .tADL_min = 400000,
+ .tCAD_min = 45000,
+ .tCAH_min = 10000,
+ .tCALH_min = 10000,
+ .tCALS_min = 10000,
+ .tCAS_min = 10000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCK_min = 50000,
+ .tCS_min = 35000,
+ .tDH_min = 5000,
+ .tDQSCK_min = 3000,
+ .tDQSCK_max = 25000,
+ .tDQSD_min = 0,
+ .tDQSD_max = 18000,
+ .tDQSHZ_max = 20000,
+ .tDQSQ_max = 5000,
+ .tDS_min = 5000,
+ .tDSC_min = 50000,
+ .tFEAT_max = 1000000,
+ .tITC_max = 1000000,
+ .tQHS_max = 6000,
+ .tRHW_min = 100000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWHR_min = 80000,
+ .tWRCK_min = 20000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 1 */
+ {
+ .type = NAND_NVDDR_IFACE,
+ .timings.mode = 1,
+ .timings.nvddr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tAC_min = 3000,
+ .tAC_max = 25000,
+ .tADL_min = 400000,
+ .tCAD_min = 45000,
+ .tCAH_min = 5000,
+ .tCALH_min = 5000,
+ .tCALS_min = 5000,
+ .tCAS_min = 5000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCK_min = 30000,
+ .tCS_min = 25000,
+ .tDH_min = 2500,
+ .tDQSCK_min = 3000,
+ .tDQSCK_max = 25000,
+ .tDQSD_min = 0,
+ .tDQSD_max = 18000,
+ .tDQSHZ_max = 20000,
+ .tDQSQ_max = 2500,
+ .tDS_min = 3000,
+ .tDSC_min = 30000,
+ .tFEAT_max = 1000000,
+ .tITC_max = 1000000,
+ .tQHS_max = 3000,
+ .tRHW_min = 100000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWHR_min = 80000,
+ .tWRCK_min = 20000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 2 */
+ {
+ .type = NAND_NVDDR_IFACE,
+ .timings.mode = 2,
+ .timings.nvddr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tAC_min = 3000,
+ .tAC_max = 25000,
+ .tADL_min = 400000,
+ .tCAD_min = 45000,
+ .tCAH_min = 4000,
+ .tCALH_min = 4000,
+ .tCALS_min = 4000,
+ .tCAS_min = 4000,
+ .tCEH_min = 20000,
+ .tCH_min = 4000,
+ .tCK_min = 20000,
+ .tCS_min = 15000,
+ .tDH_min = 1700,
+ .tDQSCK_min = 3000,
+ .tDQSCK_max = 25000,
+ .tDQSD_min = 0,
+ .tDQSD_max = 18000,
+ .tDQSHZ_max = 20000,
+ .tDQSQ_max = 1700,
+ .tDS_min = 2000,
+ .tDSC_min = 20000,
+ .tFEAT_max = 1000000,
+ .tITC_max = 1000000,
+ .tQHS_max = 2000,
+ .tRHW_min = 100000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWHR_min = 80000,
+ .tWRCK_min = 20000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 3 */
+ {
+ .type = NAND_NVDDR_IFACE,
+ .timings.mode = 3,
+ .timings.nvddr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tAC_min = 3000,
+ .tAC_max = 25000,
+ .tADL_min = 400000,
+ .tCAD_min = 45000,
+ .tCAH_min = 3000,
+ .tCALH_min = 3000,
+ .tCALS_min = 3000,
+ .tCAS_min = 3000,
+ .tCEH_min = 20000,
+ .tCH_min = 3000,
+ .tCK_min = 15000,
+ .tCS_min = 15000,
+ .tDH_min = 1300,
+ .tDQSCK_min = 3000,
+ .tDQSCK_max = 25000,
+ .tDQSD_min = 0,
+ .tDQSD_max = 18000,
+ .tDQSHZ_max = 20000,
+ .tDQSQ_max = 1300,
+ .tDS_min = 1500,
+ .tDSC_min = 15000,
+ .tFEAT_max = 1000000,
+ .tITC_max = 1000000,
+ .tQHS_max = 1500,
+ .tRHW_min = 100000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWHR_min = 80000,
+ .tWRCK_min = 20000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 4 */
+ {
+ .type = NAND_NVDDR_IFACE,
+ .timings.mode = 4,
+ .timings.nvddr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tAC_min = 3000,
+ .tAC_max = 25000,
+ .tADL_min = 400000,
+ .tCAD_min = 45000,
+ .tCAH_min = 2500,
+ .tCALH_min = 2500,
+ .tCALS_min = 2500,
+ .tCAS_min = 2500,
+ .tCEH_min = 20000,
+ .tCH_min = 2500,
+ .tCK_min = 12000,
+ .tCS_min = 15000,
+ .tDH_min = 1100,
+ .tDQSCK_min = 3000,
+ .tDQSCK_max = 25000,
+ .tDQSD_min = 0,
+ .tDQSD_max = 18000,
+ .tDQSHZ_max = 20000,
+ .tDQSQ_max = 1000,
+ .tDS_min = 1100,
+ .tDSC_min = 12000,
+ .tFEAT_max = 1000000,
+ .tITC_max = 1000000,
+ .tQHS_max = 1200,
+ .tRHW_min = 100000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWHR_min = 80000,
+ .tWRCK_min = 20000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 5 */
+ {
+ .type = NAND_NVDDR_IFACE,
+ .timings.mode = 5,
+ .timings.nvddr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
+ .tAC_min = 3000,
+ .tAC_max = 25000,
+ .tADL_min = 400000,
+ .tCAD_min = 45000,
+ .tCAH_min = 2000,
+ .tCALH_min = 2000,
+ .tCALS_min = 2000,
+ .tCAS_min = 2000,
+ .tCEH_min = 20000,
+ .tCH_min = 2000,
+ .tCK_min = 10000,
+ .tCS_min = 15000,
+ .tDH_min = 900,
+ .tDQSCK_min = 3000,
+ .tDQSCK_max = 25000,
+ .tDQSD_min = 0,
+ .tDQSD_max = 18000,
+ .tDQSHZ_max = 20000,
+ .tDQSQ_max = 850,
+ .tDS_min = 900,
+ .tDSC_min = 10000,
+ .tFEAT_max = 1000000,
+ .tITC_max = 1000000,
+ .tQHS_max = 1000,
+ .tRHW_min = 100000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWHR_min = 80000,
+ .tWRCK_min = 20000,
+ .tWW_min = 100000,
+ },
+ },
+};
+
/* All NAND chips share the same reset data interface: SDR mode 0 */
const struct nand_interface_config *nand_get_reset_interface_config(void)
{
@@ -346,23 +601,60 @@ onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings)
}
/**
- * onfi_fill_interface_config - Initialize an interface config from a given
- * ONFI mode
+ * onfi_find_closest_nvddr_mode - Derive the closest ONFI NVDDR timing mode
+ * given a set of timings
+ * @spec_timings: the timings to challenge
+ */
+unsigned int
+onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings)
+{
+ const struct nand_nvddr_timings *onfi_timings;
+ int mode;
+
+ for (mode = ARRAY_SIZE(onfi_nvddr_timings) - 1; mode > 0; mode--) {
+ onfi_timings = &onfi_nvddr_timings[mode].timings.nvddr;
+
+ if (spec_timings->tCCS_min <= onfi_timings->tCCS_min &&
+ spec_timings->tAC_min <= onfi_timings->tAC_min &&
+ spec_timings->tADL_min <= onfi_timings->tADL_min &&
+ spec_timings->tCAD_min <= onfi_timings->tCAD_min &&
+ spec_timings->tCAH_min <= onfi_timings->tCAH_min &&
+ spec_timings->tCALH_min <= onfi_timings->tCALH_min &&
+ spec_timings->tCALS_min <= onfi_timings->tCALS_min &&
+ spec_timings->tCAS_min <= onfi_timings->tCAS_min &&
+ spec_timings->tCEH_min <= onfi_timings->tCEH_min &&
+ spec_timings->tCH_min <= onfi_timings->tCH_min &&
+ spec_timings->tCK_min <= onfi_timings->tCK_min &&
+ spec_timings->tCS_min <= onfi_timings->tCS_min &&
+ spec_timings->tDH_min <= onfi_timings->tDH_min &&
+ spec_timings->tDQSCK_min <= onfi_timings->tDQSCK_min &&
+ spec_timings->tDQSD_min <= onfi_timings->tDQSD_min &&
+ spec_timings->tDS_min <= onfi_timings->tDS_min &&
+ spec_timings->tDSC_min <= onfi_timings->tDSC_min &&
+ spec_timings->tRHW_min <= onfi_timings->tRHW_min &&
+ spec_timings->tRR_min <= onfi_timings->tRR_min &&
+ spec_timings->tWHR_min <= onfi_timings->tWHR_min &&
+ spec_timings->tWRCK_min <= onfi_timings->tWRCK_min &&
+ spec_timings->tWW_min <= onfi_timings->tWW_min)
+ return mode;
+ }
+
+ return 0;
+}
+
+/*
+ * onfi_fill_sdr_interface_config - Initialize a SDR interface config from a
+ * given ONFI mode
* @chip: The NAND chip
* @iface: The interface configuration to fill
- * @type: The interface type
* @timing_mode: The ONFI timing mode
*/
-void onfi_fill_interface_config(struct nand_chip *chip,
- struct nand_interface_config *iface,
- enum nand_interface_type type,
- unsigned int timing_mode)
+static void onfi_fill_sdr_interface_config(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ unsigned int timing_mode)
{
struct onfi_params *onfi = chip->parameters.onfi;
- if (WARN_ON(type != NAND_SDR_IFACE))
- return;
-
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings)))
return;
@@ -387,6 +679,64 @@ void onfi_fill_interface_config(struct nand_chip *chip,
}
/**
+ * onfi_fill_nvddr_interface_config - Initialize a NVDDR interface config from a
+ * given ONFI mode
+ * @chip: The NAND chip
+ * @iface: The interface configuration to fill
+ * @timing_mode: The ONFI timing mode
+ */
+static void onfi_fill_nvddr_interface_config(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ unsigned int timing_mode)
+{
+ struct onfi_params *onfi = chip->parameters.onfi;
+
+ if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_nvddr_timings)))
+ return;
+
+ *iface = onfi_nvddr_timings[timing_mode];
+
+ /*
+ * Initialize timings that cannot be deduced from timing mode:
+ * tPROG, tBERS, tR, tCCS and tCAD.
+ * These information are part of the ONFI parameter page.
+ */
+ if (onfi) {
+ struct nand_nvddr_timings *timings = &iface->timings.nvddr;
+
+ /* microseconds -> picoseconds */
+ timings->tPROG_max = 1000000ULL * onfi->tPROG;
+ timings->tBERS_max = 1000000ULL * onfi->tBERS;
+ timings->tR_max = 1000000ULL * onfi->tR;
+
+ /* nanoseconds -> picoseconds */
+ timings->tCCS_min = 1000UL * onfi->tCCS;
+
+ if (onfi->fast_tCAD)
+ timings->tCAD_min = 25000;
+ }
+}
+
+/**
+ * onfi_fill_interface_config - Initialize an interface config from a given
+ * ONFI mode
+ * @chip: The NAND chip
+ * @iface: The interface configuration to fill
+ * @type: The interface type
+ * @timing_mode: The ONFI timing mode
+ */
+void onfi_fill_interface_config(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ enum nand_interface_type type,
+ unsigned int timing_mode)
+{
+ if (type == NAND_SDR_IFACE)
+ return onfi_fill_sdr_interface_config(chip, iface, timing_mode);
+ else
+ return onfi_fill_nvddr_interface_config(chip, iface, timing_mode);
+}
+
+/**
* onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
* timings according to the given ONFI timing mode
* @mode: ONFI timing mode
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c
index 21a5dbc7e0..5b38fa7bdb 100644
--- a/drivers/mtd/nand/nand_toshiba.c
+++ b/drivers/mtd/nand/raw/nand_toshiba.c
@@ -140,11 +140,13 @@ static void toshiba_nand_benand_init(struct nand_chip *chip)
chip->options |= NAND_SUBPAGE_READ;
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
}
static void toshiba_nand_decode_id(struct nand_chip *chip)
{
+ struct nand_device *base = &chip->base;
+ struct nand_ecc_props requirements = {};
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg;
@@ -175,23 +177,25 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
* - 24nm: 8 bit ECC for each 512Byte is required.
*/
if (chip->id.len >= 6 && nand_is_slc(chip)) {
- chip->base.eccreq.step_size = 512;
+ requirements.step_size = 512;
switch (chip->id.data[5] & 0x7) {
case 0x4:
- chip->base.eccreq.strength = 1;
+ requirements.strength = 1;
break;
case 0x5:
- chip->base.eccreq.strength = 4;
+ requirements.strength = 4;
break;
case 0x6:
- chip->base.eccreq.strength = 8;
+ requirements.strength = 8;
break;
default:
WARN(1, "Could not get ECC info");
- chip->base.eccreq.step_size = 0;
+ requirements.step_size = 0;
break;
}
}
+
+ nanddev_set_ecc_requirements(base, &requirements);
}
static int
@@ -252,17 +256,18 @@ static int toshiba_nand_init(struct nand_chip *chip)
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
/* Check that chip is BENAND and ECC mode is on-die */
- if (nand_is_slc(chip) && chip->ecc.mode == NAND_ECC_ON_DIE &&
+ if (nand_is_slc(chip) &&
+ chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE &&
chip->id.data[4] & TOSHIBA_NAND_ID4_IS_BENAND)
toshiba_nand_benand_init(chip);
- if (!strcmp("TC58TEG5DCLTA00", chip->parameters.model))
- return -EINVAL; /* MLC, not yet supported in barebox */
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
sizeof("TC58NVG0S3E") - 1))
tc58nvg0s3e_init(chip);
- if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
- sizeof("TH58NVG2S3HBAI4") - 1))
+ if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
+ sizeof("TH58NVG2S3HBAI4") - 1)) ||
+ (!strncmp("TH58NVG3S0HBAI4", chip->parameters.model,
+ sizeof("TH58NVG3S0HBAI4") - 1)))
th58nvg2s3hbai4_init(chip);
return 0;
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/raw/nomadik_nand.c
index 8f58f5997c..1be3021e99 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/raw/nomadik_nand.c
@@ -24,8 +24,8 @@
#include <linux/mtd/rawnand.h>
#include <io.h>
-#include <mach/nand.h>
-#include <mach/fsmc.h>
+#include <mach/nomadik/nand.h>
+#include <mach/nomadik/fsmc.h>
#include <errno.h>
@@ -158,7 +158,7 @@ static void nomadik_cmd_ctrl(struct nand_chip *nand, int cmd, unsigned int ctrl)
writeb(cmd, host->addr_va);
}
-static int nomadik_nand_probe(struct device_d *dev)
+static int nomadik_nand_probe(struct device *dev)
{
struct nomadik_nand_platform_data *pdata = dev->platform_data;
struct nomadik_nand_host *host;
@@ -198,7 +198,7 @@ static int nomadik_nand_probe(struct device_d *dev)
return PTR_ERR(nand->legacy.IO_ADDR_W);
nand->legacy.cmd_ctrl = nomadik_cmd_ctrl;
- nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
mtd_set_ecclayout(mtd, &nomadik_ecc_layout);
nand->ecc.calculate = nomadik_ecc512_calc;
nand->ecc.correct = nomadik_ecc512_correct;
@@ -227,7 +227,7 @@ err:
return ret;
}
-static struct driver_d nomadik_nand_driver = {
+static struct driver nomadik_nand_driver = {
.probe = nomadik_nand_probe,
.name = "nomadik_nand",
};
diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c
index 583235fc78..da731e44f3 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/raw/omap_elm.c
@@ -66,7 +66,7 @@ struct elm_registers {
};
struct elm_info {
- struct device_d *dev;
+ struct device *dev;
void __iomem *elm_base;
struct list_head list;
enum bch_ecc bch_type;
@@ -376,7 +376,7 @@ int elm_decode_bch_error_page(u8 *ecc_calc, struct elm_errorvec *err_vec)
return 0;
}
-static int elm_probe(struct device_d *dev)
+static int elm_probe(struct device *dev)
{
struct resource *res;
struct elm_info *info;
@@ -404,8 +404,9 @@ static struct of_device_id elm_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, elm_compatible);
-static struct driver_d omap_elm_driver = {
+static struct driver omap_elm_driver = {
.name = "omap-elm",
.probe = elm_probe,
.of_compatible = DRV_OF_COMPAT(elm_compatible)
diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
new file mode 100644
index 0000000000..279b864970
--- /dev/null
+++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
@@ -0,0 +1,1354 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018
+ * Author: Christophe Kerello <christophe.kerello@st.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of_address.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+#include <mfd/syscon.h>
+
+#include "internals.h"
+
+/* Bad block marker length */
+#define FMC2_BBM_LEN 2
+
+/* ECC step size */
+#define FMC2_ECC_STEP_SIZE 512
+
+/* Max requests done for a 8k nand page size */
+#define FMC2_MAX_SG 16
+
+/* Max chip enable */
+#define FMC2_MAX_CE 2
+
+#define FMC2_TIMEOUT_MS 5000
+
+/* Timings */
+#define FMC2_THIZ 1
+#define FMC2_TIO 8000
+#define FMC2_TSYNC 3000
+#define FMC2_PCR_TIMING_MASK 0xf
+#define FMC2_PMEM_PATT_TIMING_MASK 0xff
+
+/* FMC2 Controller Registers */
+#define FMC2_BCR1 0x0
+#define FMC2_PCR 0x80
+#define FMC2_SR 0x84
+#define FMC2_PMEM 0x88
+#define FMC2_PATT 0x8c
+#define FMC2_HECCR 0x94
+#define FMC2_ISR 0x184
+#define FMC2_ICR 0x188
+#define FMC2_CSQCR 0x200
+#define FMC2_CSQCFGR1 0x204
+#define FMC2_CSQCFGR2 0x208
+#define FMC2_CSQCFGR3 0x20c
+#define FMC2_CSQAR1 0x210
+#define FMC2_CSQAR2 0x214
+#define FMC2_CSQIER 0x220
+#define FMC2_CSQISR 0x224
+#define FMC2_CSQICR 0x228
+#define FMC2_CSQEMSR 0x230
+#define FMC2_BCHIER 0x250
+#define FMC2_BCHISR 0x254
+#define FMC2_BCHICR 0x258
+#define FMC2_BCHPBR1 0x260
+#define FMC2_BCHPBR2 0x264
+#define FMC2_BCHPBR3 0x268
+#define FMC2_BCHPBR4 0x26c
+#define FMC2_BCHDSR0 0x27c
+#define FMC2_BCHDSR1 0x280
+#define FMC2_BCHDSR2 0x284
+#define FMC2_BCHDSR3 0x288
+#define FMC2_BCHDSR4 0x28c
+
+/* Register: FMC2_BCR1 */
+#define FMC2_BCR1_FMC2EN BIT(31)
+
+/* Register: FMC2_PCR */
+#define FMC2_PCR_PWAITEN BIT(1)
+#define FMC2_PCR_PBKEN BIT(2)
+#define FMC2_PCR_PWID GENMASK(5, 4)
+#define FMC2_PCR_PWID_BUSWIDTH_8 0
+#define FMC2_PCR_PWID_BUSWIDTH_16 1
+#define FMC2_PCR_ECCEN BIT(6)
+#define FMC2_PCR_ECCALG BIT(8)
+#define FMC2_PCR_TCLR GENMASK(12, 9)
+#define FMC2_PCR_TCLR_DEFAULT 0xf
+#define FMC2_PCR_TAR GENMASK(16, 13)
+#define FMC2_PCR_TAR_DEFAULT 0xf
+#define FMC2_PCR_ECCSS GENMASK(19, 17)
+#define FMC2_PCR_ECCSS_512 1
+#define FMC2_PCR_ECCSS_2048 3
+#define FMC2_PCR_BCHECC BIT(24)
+#define FMC2_PCR_WEN BIT(25)
+
+/* Register: FMC2_SR */
+#define FMC2_SR_NWRF BIT(6)
+
+/* Register: FMC2_PMEM */
+#define FMC2_PMEM_MEMSET GENMASK(7, 0)
+#define FMC2_PMEM_MEMWAIT GENMASK(15, 8)
+#define FMC2_PMEM_MEMHOLD GENMASK(23, 16)
+#define FMC2_PMEM_MEMHIZ GENMASK(31, 24)
+#define FMC2_PMEM_DEFAULT 0x0a0a0a0a
+
+/* Register: FMC2_PATT */
+#define FMC2_PATT_ATTSET GENMASK(7, 0)
+#define FMC2_PATT_ATTWAIT GENMASK(15, 8)
+#define FMC2_PATT_ATTHOLD GENMASK(23, 16)
+#define FMC2_PATT_ATTHIZ GENMASK(31, 24)
+#define FMC2_PATT_DEFAULT 0x0a0a0a0a
+
+/* Register: FMC2_ISR */
+#define FMC2_ISR_IHLF BIT(1)
+
+/* Register: FMC2_BCHISR */
+#define FMC2_BCHISR_DERF BIT(1)
+#define FMC2_BCHISR_EPBRF BIT(4)
+
+/* Register: FMC2_ICR */
+#define FMC2_ICR_CIHLF BIT(1)
+
+/* Register: FMC2_CSQCR */
+#define FMC2_CSQCR_CSQSTART BIT(0)
+
+/* Register: FMC2_CSQCFGR1 */
+#define FMC2_CSQCFGR1_CMD2EN BIT(1)
+#define FMC2_CSQCFGR1_DMADEN BIT(2)
+#define FMC2_CSQCFGR1_ACYNBR GENMASK(6, 4)
+#define FMC2_CSQCFGR1_CMD1 GENMASK(15, 8)
+#define FMC2_CSQCFGR1_CMD2 GENMASK(23, 16)
+#define FMC2_CSQCFGR1_CMD1T BIT(24)
+#define FMC2_CSQCFGR1_CMD2T BIT(25)
+
+/* Register: FMC2_CSQCFGR2 */
+#define FMC2_CSQCFGR2_SQSDTEN BIT(0)
+#define FMC2_CSQCFGR2_RCMD2EN BIT(1)
+#define FMC2_CSQCFGR2_DMASEN BIT(2)
+#define FMC2_CSQCFGR2_RCMD1 GENMASK(15, 8)
+#define FMC2_CSQCFGR2_RCMD2 GENMASK(23, 16)
+#define FMC2_CSQCFGR2_RCMD1T BIT(24)
+#define FMC2_CSQCFGR2_RCMD2T BIT(25)
+
+/* Register: FMC2_CSQCFGR3 */
+#define FMC2_CSQCFGR3_SNBR GENMASK(13, 8)
+#define FMC2_CSQCFGR3_AC1T BIT(16)
+#define FMC2_CSQCFGR3_AC2T BIT(17)
+#define FMC2_CSQCFGR3_AC3T BIT(18)
+#define FMC2_CSQCFGR3_AC4T BIT(19)
+#define FMC2_CSQCFGR3_AC5T BIT(20)
+#define FMC2_CSQCFGR3_SDT BIT(21)
+#define FMC2_CSQCFGR3_RAC1T BIT(22)
+#define FMC2_CSQCFGR3_RAC2T BIT(23)
+
+/* Register: FMC2_CSQCAR1 */
+#define FMC2_CSQCAR1_ADDC1 GENMASK(7, 0)
+#define FMC2_CSQCAR1_ADDC2 GENMASK(15, 8)
+#define FMC2_CSQCAR1_ADDC3 GENMASK(23, 16)
+#define FMC2_CSQCAR1_ADDC4 GENMASK(31, 24)
+
+/* Register: FMC2_CSQCAR2 */
+#define FMC2_CSQCAR2_ADDC5 GENMASK(7, 0)
+#define FMC2_CSQCAR2_NANDCEN GENMASK(11, 10)
+#define FMC2_CSQCAR2_SAO GENMASK(31, 16)
+
+/* Register: FMC2_CSQIER */
+#define FMC2_CSQIER_TCIE BIT(0)
+
+/* Register: FMC2_CSQICR */
+#define FMC2_CSQICR_CLEAR_IRQ GENMASK(4, 0)
+
+/* Register: FMC2_CSQEMSR */
+#define FMC2_CSQEMSR_SEM GENMASK(15, 0)
+
+/* Register: FMC2_BCHIER */
+#define FMC2_BCHIER_DERIE BIT(1)
+#define FMC2_BCHIER_EPBRIE BIT(4)
+
+/* Register: FMC2_BCHICR */
+#define FMC2_BCHICR_CLEAR_IRQ GENMASK(4, 0)
+
+/* Register: FMC2_BCHDSR0 */
+#define FMC2_BCHDSR0_DUE BIT(0)
+#define FMC2_BCHDSR0_DEF BIT(1)
+#define FMC2_BCHDSR0_DEN GENMASK(7, 4)
+
+/* Register: FMC2_BCHDSR1 */
+#define FMC2_BCHDSR1_EBP1 GENMASK(12, 0)
+#define FMC2_BCHDSR1_EBP2 GENMASK(28, 16)
+
+/* Register: FMC2_BCHDSR2 */
+#define FMC2_BCHDSR2_EBP3 GENMASK(12, 0)
+#define FMC2_BCHDSR2_EBP4 GENMASK(28, 16)
+
+/* Register: FMC2_BCHDSR3 */
+#define FMC2_BCHDSR3_EBP5 GENMASK(12, 0)
+#define FMC2_BCHDSR3_EBP6 GENMASK(28, 16)
+
+/* Register: FMC2_BCHDSR4 */
+#define FMC2_BCHDSR4_EBP7 GENMASK(12, 0)
+#define FMC2_BCHDSR4_EBP8 GENMASK(28, 16)
+
+enum stm32_fmc2_ecc {
+ FMC2_ECC_HAM = 1,
+ FMC2_ECC_BCH4 = 4,
+ FMC2_ECC_BCH8 = 8
+};
+
+struct stm32_fmc2_timings {
+ u8 tclr;
+ u8 tar;
+ u8 thiz;
+ u8 twait;
+ u8 thold_mem;
+ u8 tset_mem;
+ u8 thold_att;
+ u8 tset_att;
+};
+
+struct stm32_fmc2_nand {
+ struct nand_chip chip;
+ struct gpio_desc *wp_gpio;
+ struct stm32_fmc2_timings timings;
+ int ncs;
+ int cs_used[FMC2_MAX_CE];
+};
+
+static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip)
+{
+ return container_of(chip, struct stm32_fmc2_nand, chip);
+}
+
+struct stm32_fmc2_nfc {
+ struct nand_controller base;
+ struct stm32_fmc2_nand nand;
+ struct device *dev;
+ struct device *cdev;
+ struct regmap *regmap;
+ void __iomem *data_base[FMC2_MAX_CE];
+ void __iomem *cmd_base[FMC2_MAX_CE];
+ void __iomem *addr_base[FMC2_MAX_CE];
+ struct clk *clk;
+
+ u8 cs_assigned;
+ int cs_sel;
+};
+
+static inline struct stm32_fmc2_nfc *to_stm32_nfc(struct nand_controller *base)
+{
+ return container_of(base, struct stm32_fmc2_nfc, base);
+}
+
+static void stm32_fmc2_nfc_timings_init(struct nand_chip *chip)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
+ struct stm32_fmc2_timings *timings = &nand->timings;
+ u32 pmem, patt;
+
+ /* Set tclr/tar timings */
+ regmap_update_bits(nfc->regmap, FMC2_PCR,
+ FMC2_PCR_TCLR | FMC2_PCR_TAR,
+ FIELD_PREP(FMC2_PCR_TCLR, timings->tclr) |
+ FIELD_PREP(FMC2_PCR_TAR, timings->tar));
+
+ /* Set tset/twait/thold/thiz timings in common bank */
+ pmem = FIELD_PREP(FMC2_PMEM_MEMSET, timings->tset_mem);
+ pmem |= FIELD_PREP(FMC2_PMEM_MEMWAIT, timings->twait);
+ pmem |= FIELD_PREP(FMC2_PMEM_MEMHOLD, timings->thold_mem);
+ pmem |= FIELD_PREP(FMC2_PMEM_MEMHIZ, timings->thiz);
+ regmap_write(nfc->regmap, FMC2_PMEM, pmem);
+
+ /* Set tset/twait/thold/thiz timings in attribut bank */
+ patt = FIELD_PREP(FMC2_PATT_ATTSET, timings->tset_att);
+ patt |= FIELD_PREP(FMC2_PATT_ATTWAIT, timings->twait);
+ patt |= FIELD_PREP(FMC2_PATT_ATTHOLD, timings->thold_att);
+ patt |= FIELD_PREP(FMC2_PATT_ATTHIZ, timings->thiz);
+ regmap_write(nfc->regmap, FMC2_PATT, patt);
+}
+
+static void stm32_fmc2_nfc_setup(struct nand_chip *chip)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ u32 pcr = 0, pcr_mask;
+
+ /* Configure ECC algorithm (default configuration is Hamming) */
+ pcr_mask = FMC2_PCR_ECCALG;
+ pcr_mask |= FMC2_PCR_BCHECC;
+ if (chip->ecc.strength == FMC2_ECC_BCH8) {
+ pcr |= FMC2_PCR_ECCALG;
+ pcr |= FMC2_PCR_BCHECC;
+ } else if (chip->ecc.strength == FMC2_ECC_BCH4) {
+ pcr |= FMC2_PCR_ECCALG;
+ }
+
+ /* Set buswidth */
+ pcr_mask |= FMC2_PCR_PWID;
+ if (chip->options & NAND_BUSWIDTH_16)
+ pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16);
+
+ /* Set ECC sector size */
+ pcr_mask |= FMC2_PCR_ECCSS;
+ pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_512);
+
+ regmap_update_bits(nfc->regmap, FMC2_PCR, pcr_mask, pcr);
+}
+
+static void stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
+
+ if (nand->cs_used[chipnr] == nfc->cs_sel)
+ return;
+
+ nfc->cs_sel = nand->cs_used[chipnr];
+ stm32_fmc2_nfc_setup(chip);
+ stm32_fmc2_nfc_timings_init(chip);
+}
+
+static void stm32_fmc2_nfc_set_buswidth_16(struct stm32_fmc2_nfc *nfc, bool set)
+{
+ u32 pcr;
+
+ pcr = set ? FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16) :
+ FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_8);
+
+ regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_PWID, pcr);
+}
+
+static void stm32_fmc2_nfc_set_ecc(struct stm32_fmc2_nfc *nfc, bool enable)
+{
+ regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_ECCEN,
+ enable ? FMC2_PCR_ECCEN : 0);
+}
+
+static void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc)
+{
+ regmap_write(nfc->regmap, FMC2_BCHICR, FMC2_BCHICR_CLEAR_IRQ);
+}
+
+/*
+ * Enable ECC logic and reset syndrome/parity bits previously calculated
+ * Syndrome/parity bits is cleared by setting the ECCEN bit to 0
+ */
+static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+
+ stm32_fmc2_nfc_set_ecc(nfc, false);
+
+ if (chip->ecc.strength != FMC2_ECC_HAM) {
+ regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_WEN,
+ mode == NAND_ECC_WRITE ? FMC2_PCR_WEN : 0);
+
+ stm32_fmc2_nfc_clear_bch_irq(nfc);
+ }
+
+ stm32_fmc2_nfc_set_ecc(nfc, true);
+}
+
+/*
+ * ECC Hamming calculation
+ * ECC is 3 bytes for 512 bytes of data (supports error correction up to
+ * max of 1-bit)
+ */
+static void stm32_fmc2_nfc_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
+{
+ ecc[0] = ecc_sta;
+ ecc[1] = ecc_sta >> 8;
+ ecc[2] = ecc_sta >> 16;
+}
+
+static int stm32_fmc2_nfc_ham_calculate(struct nand_chip *chip, const u8 *data,
+ u8 *ecc)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ u32 sr, heccr;
+ int ret;
+
+ ret = regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr,
+ sr & FMC2_SR_NWRF,
+ 1000 * FMC2_TIMEOUT_MS);
+ if (ret) {
+ dev_err(nfc->dev, "ham timeout\n");
+ return ret;
+ }
+
+ regmap_read(nfc->regmap, FMC2_HECCR, &heccr);
+ stm32_fmc2_nfc_ham_set_ecc(heccr, ecc);
+ stm32_fmc2_nfc_set_ecc(nfc, false);
+
+ return 0;
+}
+
+static int stm32_fmc2_nfc_ham_correct(struct nand_chip *chip, u8 *dat,
+ u8 *read_ecc, u8 *calc_ecc)
+{
+ u8 bit_position = 0, b0, b1, b2;
+ u32 byte_addr = 0, b;
+ u32 i, shifting = 1;
+
+ /* Indicate which bit and byte is faulty (if any) */
+ b0 = read_ecc[0] ^ calc_ecc[0];
+ b1 = read_ecc[1] ^ calc_ecc[1];
+ b2 = read_ecc[2] ^ calc_ecc[2];
+ b = b0 | (b1 << 8) | (b2 << 16);
+
+ /* No errors */
+ if (likely(!b))
+ return 0;
+
+ /* Calculate bit position */
+ for (i = 0; i < 3; i++) {
+ switch (b % 4) {
+ case 2:
+ bit_position += shifting;
+ break;
+ case 1:
+ break;
+ default:
+ return -EBADMSG;
+ }
+ shifting <<= 1;
+ b >>= 2;
+ }
+
+ /* Calculate byte position */
+ shifting = 1;
+ for (i = 0; i < 9; i++) {
+ switch (b % 4) {
+ case 2:
+ byte_addr += shifting;
+ break;
+ case 1:
+ break;
+ default:
+ return -EBADMSG;
+ }
+ shifting <<= 1;
+ b >>= 2;
+ }
+
+ /* Flip the bit */
+ dat[byte_addr] ^= (1 << bit_position);
+
+ return 1;
+}
+
+/*
+ * ECC BCH calculation and correction
+ * ECC is 7/13 bytes for 512 bytes of data (supports error correction up to
+ * max of 4-bit/8-bit)
+ */
+static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data,
+ u8 *ecc)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ u32 bchisr, bchpbr;
+ int ret;
+
+ /* Wait until the BCH code is ready */
+ ret = regmap_read_poll_timeout(nfc->regmap, FMC2_BCHISR, bchisr,
+ bchisr & FMC2_BCHISR_EPBRF,
+ 1000 * FMC2_TIMEOUT_MS);
+ if (ret) {
+ dev_err(nfc->dev, "bch timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Read parity bits */
+ regmap_read(nfc->regmap, FMC2_BCHPBR1, &bchpbr);
+ ecc[0] = bchpbr;
+ ecc[1] = bchpbr >> 8;
+ ecc[2] = bchpbr >> 16;
+ ecc[3] = bchpbr >> 24;
+
+ regmap_read(nfc->regmap, FMC2_BCHPBR2, &bchpbr);
+ ecc[4] = bchpbr;
+ ecc[5] = bchpbr >> 8;
+ ecc[6] = bchpbr >> 16;
+
+ if (chip->ecc.strength == FMC2_ECC_BCH8) {
+ ecc[7] = bchpbr >> 24;
+
+ regmap_read(nfc->regmap, FMC2_BCHPBR3, &bchpbr);
+ ecc[8] = bchpbr;
+ ecc[9] = bchpbr >> 8;
+ ecc[10] = bchpbr >> 16;
+ ecc[11] = bchpbr >> 24;
+
+ regmap_read(nfc->regmap, FMC2_BCHPBR4, &bchpbr);
+ ecc[12] = bchpbr;
+ }
+
+ stm32_fmc2_nfc_set_ecc(nfc, false);
+
+ return 0;
+}
+
+static int stm32_fmc2_nfc_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta)
+{
+ u32 bchdsr0 = ecc_sta[0];
+ u32 bchdsr1 = ecc_sta[1];
+ u32 bchdsr2 = ecc_sta[2];
+ u32 bchdsr3 = ecc_sta[3];
+ u32 bchdsr4 = ecc_sta[4];
+ u16 pos[8];
+ int i, den;
+ unsigned int nb_errs = 0;
+
+ /* No errors found */
+ if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF)))
+ return 0;
+
+ /* Too many errors detected */
+ if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE))
+ return -EBADMSG;
+
+ pos[0] = FIELD_GET(FMC2_BCHDSR1_EBP1, bchdsr1);
+ pos[1] = FIELD_GET(FMC2_BCHDSR1_EBP2, bchdsr1);
+ pos[2] = FIELD_GET(FMC2_BCHDSR2_EBP3, bchdsr2);
+ pos[3] = FIELD_GET(FMC2_BCHDSR2_EBP4, bchdsr2);
+ pos[4] = FIELD_GET(FMC2_BCHDSR3_EBP5, bchdsr3);
+ pos[5] = FIELD_GET(FMC2_BCHDSR3_EBP6, bchdsr3);
+ pos[6] = FIELD_GET(FMC2_BCHDSR4_EBP7, bchdsr4);
+ pos[7] = FIELD_GET(FMC2_BCHDSR4_EBP8, bchdsr4);
+
+ den = FIELD_GET(FMC2_BCHDSR0_DEN, bchdsr0);
+ for (i = 0; i < den; i++) {
+ if (pos[i] < eccsize * 8) {
+ change_bit(pos[i], (unsigned long *)dat);
+ nb_errs++;
+ }
+ }
+
+ return nb_errs;
+}
+
+static int stm32_fmc2_nfc_bch_correct(struct nand_chip *chip, u8 *dat,
+ u8 *read_ecc, u8 *calc_ecc)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ u32 bchisr, ecc_sta[5];
+ int ret;
+
+ /* Wait until the decoding error is ready */
+ ret = regmap_read_poll_timeout(nfc->regmap, FMC2_BCHISR, bchisr,
+ bchisr & FMC2_BCHISR_DERF,
+ 1000 * FMC2_TIMEOUT_MS);
+ if (ret) {
+ dev_err(nfc->dev, "bch timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ regmap_bulk_read(nfc->regmap, FMC2_BCHDSR0, ecc_sta, ARRAY_SIZE(ecc_sta));
+
+ stm32_fmc2_nfc_set_ecc(nfc, false);
+
+ return stm32_fmc2_nfc_bch_decode(chip->ecc.size, dat, ecc_sta);
+}
+
+static int stm32_fmc2_nfc_read_page(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret, i, s, stat, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ int eccstrength = chip->ecc.strength;
+ u8 *p = buf;
+ u8 *ecc_calc = chip->ecc.calc_buf;
+ u8 *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = mtd->writesize + FMC2_BBM_LEN, s = 0; s < eccsteps;
+ s++, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+
+ /* Read the nand page sector (512 bytes) */
+ ret = nand_change_read_column_op(chip, s * eccsize, p,
+ eccsize, false);
+ if (ret)
+ return ret;
+
+ /* Read the corresponding ECC bytes */
+ ret = nand_change_read_column_op(chip, i, ecc_code,
+ eccbytes, false);
+ if (ret)
+ return ret;
+
+ /* Correct the data */
+ stat = chip->ecc.correct(chip, p, ecc_code, ecc_calc);
+ if (stat == -EBADMSG)
+ /* Check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ ecc_code, eccbytes,
+ NULL, 0,
+ eccstrength);
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ /* Read oob */
+ if (oob_required) {
+ ret = nand_change_read_column_op(chip, mtd->writesize,
+ chip->oob_poi, mtd->oobsize,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ return max_bitflips;
+}
+
+static void stm32_fmc2_nfc_read_data(struct nand_chip *chip, void *buf,
+ unsigned int len, bool force_8bit)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ void __iomem *io_addr_r = nfc->data_base[nfc->cs_sel];
+
+ if (force_8bit && chip->options & NAND_BUSWIDTH_16)
+ /* Reconfigure bus width to 8-bit */
+ stm32_fmc2_nfc_set_buswidth_16(nfc, false);
+
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32))) {
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(u16)) && len) {
+ *(u8 *)buf = readb_relaxed(io_addr_r);
+ buf += sizeof(u8);
+ len -= sizeof(u8);
+ }
+
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32)) &&
+ len >= sizeof(u16)) {
+ *(u16 *)buf = readw_relaxed(io_addr_r);
+ buf += sizeof(u16);
+ len -= sizeof(u16);
+ }
+ }
+
+ /* Buf is aligned */
+ while (len >= sizeof(u32)) {
+ *(u32 *)buf = readl_relaxed(io_addr_r);
+ buf += sizeof(u32);
+ len -= sizeof(u32);
+ }
+
+ /* Read remaining bytes */
+ if (len >= sizeof(u16)) {
+ *(u16 *)buf = readw_relaxed(io_addr_r);
+ buf += sizeof(u16);
+ len -= sizeof(u16);
+ }
+
+ if (len)
+ *(u8 *)buf = readb_relaxed(io_addr_r);
+
+ if (force_8bit && chip->options & NAND_BUSWIDTH_16)
+ /* Reconfigure bus width to 16-bit */
+ stm32_fmc2_nfc_set_buswidth_16(nfc, true);
+}
+
+static void stm32_fmc2_nfc_write_data(struct nand_chip *chip, const void *buf,
+ unsigned int len, bool force_8bit)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ void __iomem *io_addr_w = nfc->data_base[nfc->cs_sel];
+
+ if (force_8bit && chip->options & NAND_BUSWIDTH_16)
+ /* Reconfigure bus width to 8-bit */
+ stm32_fmc2_nfc_set_buswidth_16(nfc, false);
+
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32))) {
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(u16)) && len) {
+ writeb_relaxed(*(u8 *)buf, io_addr_w);
+ buf += sizeof(u8);
+ len -= sizeof(u8);
+ }
+
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32)) &&
+ len >= sizeof(u16)) {
+ writew_relaxed(*(u16 *)buf, io_addr_w);
+ buf += sizeof(u16);
+ len -= sizeof(u16);
+ }
+ }
+
+ /* Buf is aligned */
+ while (len >= sizeof(u32)) {
+ writel_relaxed(*(u32 *)buf, io_addr_w);
+ buf += sizeof(u32);
+ len -= sizeof(u32);
+ }
+
+ /* Write remaining bytes */
+ if (len >= sizeof(u16)) {
+ writew_relaxed(*(u16 *)buf, io_addr_w);
+ buf += sizeof(u16);
+ len -= sizeof(u16);
+ }
+
+ if (len)
+ writeb_relaxed(*(u8 *)buf, io_addr_w);
+
+ if (force_8bit && chip->options & NAND_BUSWIDTH_16)
+ /* Reconfigure bus width to 16-bit */
+ stm32_fmc2_nfc_set_buswidth_16(nfc, true);
+}
+
+static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip,
+ unsigned long timeout_ms)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ const struct nand_sdr_timings *timings;
+ u32 isr, sr;
+
+ /* Check if there is no pending requests to the NAND flash */
+ if (regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr,
+ sr & FMC2_SR_NWRF,
+ 1000 * FMC2_TIMEOUT_MS))
+ dev_warn(nfc->dev, "Waitrdy timeout\n");
+
+ /* Wait tWB before R/B# signal is low */
+ timings = nand_get_sdr_timings(nand_get_interface_config(chip));
+ ndelay(PSEC_TO_NSEC(timings->tWB_max));
+
+ /* R/B# signal is low, clear high level flag */
+ regmap_write(nfc->regmap, FMC2_ICR, FMC2_ICR_CIHLF);
+
+ /* Wait R/B# signal is high */
+ return regmap_read_poll_timeout(nfc->regmap, FMC2_ISR, isr,
+ isr & FMC2_ISR_IHLF,
+ 1000 * FMC2_TIMEOUT_MS);
+}
+
+static int stm32_fmc2_nfc_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ const struct nand_op_instr *instr = NULL;
+ unsigned int op_id, i, timeout;
+ int ret = 0;
+
+ if (check_only)
+ return 0;
+
+ stm32_fmc2_nfc_select_chip(chip, op->cs);
+
+ for (op_id = 0; op_id < op->ninstrs; op_id++) {
+ instr = &op->instrs[op_id];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ writeb_relaxed(instr->ctx.cmd.opcode,
+ nfc->cmd_base[nfc->cs_sel]);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++)
+ writeb_relaxed(instr->ctx.addr.addrs[i],
+ nfc->addr_base[nfc->cs_sel]);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ stm32_fmc2_nfc_read_data(chip, instr->ctx.data.buf.in,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ stm32_fmc2_nfc_write_data(chip, instr->ctx.data.buf.out,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ timeout = instr->ctx.waitrdy.timeout_ms;
+ ret = stm32_fmc2_nfc_waitrdy(chip, timeout);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc)
+{
+ u32 pcr;
+
+ regmap_read(nfc->regmap, FMC2_PCR, &pcr);
+
+ /* Set CS used to undefined */
+ nfc->cs_sel = -1;
+
+ /* Enable wait feature and nand flash memory bank */
+ pcr |= FMC2_PCR_PWAITEN;
+ pcr |= FMC2_PCR_PBKEN;
+
+ /* Set buswidth to 8 bits mode for identification */
+ pcr &= ~FMC2_PCR_PWID;
+
+ /* ECC logic is disabled */
+ pcr &= ~FMC2_PCR_ECCEN;
+
+ /* Default mode */
+ pcr &= ~FMC2_PCR_ECCALG;
+ pcr &= ~FMC2_PCR_BCHECC;
+ pcr &= ~FMC2_PCR_WEN;
+
+ /* Set default ECC sector size */
+ pcr &= ~FMC2_PCR_ECCSS;
+ pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_2048);
+
+ /* Set default tclr/tar timings */
+ pcr &= ~FMC2_PCR_TCLR;
+ pcr |= FIELD_PREP(FMC2_PCR_TCLR, FMC2_PCR_TCLR_DEFAULT);
+ pcr &= ~FMC2_PCR_TAR;
+ pcr |= FIELD_PREP(FMC2_PCR_TAR, FMC2_PCR_TAR_DEFAULT);
+
+ /* Enable FMC2 controller */
+ if (nfc->dev == nfc->cdev)
+ regmap_update_bits(nfc->regmap, FMC2_BCR1,
+ FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN);
+
+ regmap_write(nfc->regmap, FMC2_PCR, pcr);
+ regmap_write(nfc->regmap, FMC2_PMEM, FMC2_PMEM_DEFAULT);
+ regmap_write(nfc->regmap, FMC2_PATT, FMC2_PATT_DEFAULT);
+}
+
+static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip,
+ const struct nand_sdr_timings *sdrt)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
+ struct stm32_fmc2_timings *tims = &nand->timings;
+ unsigned long hclk = clk_get_rate(nfc->clk);
+ unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
+ unsigned long timing, tar, tclr, thiz, twait;
+ unsigned long tset_mem, tset_att, thold_mem, thold_att;
+
+ tar = max_t(unsigned long, hclkp, sdrt->tAR_min);
+ timing = DIV_ROUND_UP(tar, hclkp) - 1;
+ tims->tar = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK);
+
+ tclr = max_t(unsigned long, hclkp, sdrt->tCLR_min);
+ timing = DIV_ROUND_UP(tclr, hclkp) - 1;
+ tims->tclr = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK);
+
+ tims->thiz = FMC2_THIZ;
+ thiz = (tims->thiz + 1) * hclkp;
+
+ /*
+ * tWAIT > tRP
+ * tWAIT > tWP
+ * tWAIT > tREA + tIO
+ */
+ twait = max_t(unsigned long, hclkp, sdrt->tRP_min);
+ twait = max_t(unsigned long, twait, sdrt->tWP_min);
+ twait = max_t(unsigned long, twait, sdrt->tREA_max + FMC2_TIO);
+ timing = DIV_ROUND_UP(twait, hclkp);
+ tims->twait = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+ /*
+ * tSETUP_MEM > tCS - tWAIT
+ * tSETUP_MEM > tALS - tWAIT
+ * tSETUP_MEM > tDS - (tWAIT - tHIZ)
+ */
+ tset_mem = hclkp;
+ if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait))
+ tset_mem = sdrt->tCS_min - twait;
+ if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait))
+ tset_mem = sdrt->tALS_min - twait;
+ if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
+ (tset_mem < sdrt->tDS_min - (twait - thiz)))
+ tset_mem = sdrt->tDS_min - (twait - thiz);
+ timing = DIV_ROUND_UP(tset_mem, hclkp);
+ tims->tset_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+ /*
+ * tHOLD_MEM > tCH
+ * tHOLD_MEM > tREH - tSETUP_MEM
+ * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
+ */
+ thold_mem = max_t(unsigned long, hclkp, sdrt->tCH_min);
+ if (sdrt->tREH_min > tset_mem &&
+ (thold_mem < sdrt->tREH_min - tset_mem))
+ thold_mem = sdrt->tREH_min - tset_mem;
+ if ((sdrt->tRC_min > tset_mem + twait) &&
+ (thold_mem < sdrt->tRC_min - (tset_mem + twait)))
+ thold_mem = sdrt->tRC_min - (tset_mem + twait);
+ if ((sdrt->tWC_min > tset_mem + twait) &&
+ (thold_mem < sdrt->tWC_min - (tset_mem + twait)))
+ thold_mem = sdrt->tWC_min - (tset_mem + twait);
+ timing = DIV_ROUND_UP(thold_mem, hclkp);
+ tims->thold_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+ /*
+ * tSETUP_ATT > tCS - tWAIT
+ * tSETUP_ATT > tCLS - tWAIT
+ * tSETUP_ATT > tALS - tWAIT
+ * tSETUP_ATT > tRHW - tHOLD_MEM
+ * tSETUP_ATT > tDS - (tWAIT - tHIZ)
+ */
+ tset_att = hclkp;
+ if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait))
+ tset_att = sdrt->tCS_min - twait;
+ if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait))
+ tset_att = sdrt->tCLS_min - twait;
+ if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait))
+ tset_att = sdrt->tALS_min - twait;
+ if (sdrt->tRHW_min > thold_mem &&
+ (tset_att < sdrt->tRHW_min - thold_mem))
+ tset_att = sdrt->tRHW_min - thold_mem;
+ if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
+ (tset_att < sdrt->tDS_min - (twait - thiz)))
+ tset_att = sdrt->tDS_min - (twait - thiz);
+ timing = DIV_ROUND_UP(tset_att, hclkp);
+ tims->tset_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+ /*
+ * tHOLD_ATT > tALH
+ * tHOLD_ATT > tCH
+ * tHOLD_ATT > tCLH
+ * tHOLD_ATT > tCOH
+ * tHOLD_ATT > tDH
+ * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM
+ * tHOLD_ATT > tADL - tSETUP_MEM
+ * tHOLD_ATT > tWH - tSETUP_MEM
+ * tHOLD_ATT > tWHR - tSETUP_MEM
+ * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
+ * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
+ */
+ thold_att = max_t(unsigned long, hclkp, sdrt->tALH_min);
+ thold_att = max_t(unsigned long, thold_att, sdrt->tCH_min);
+ thold_att = max_t(unsigned long, thold_att, sdrt->tCLH_min);
+ thold_att = max_t(unsigned long, thold_att, sdrt->tCOH_min);
+ thold_att = max_t(unsigned long, thold_att, sdrt->tDH_min);
+ if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) &&
+ (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem))
+ thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem;
+ if (sdrt->tADL_min > tset_mem &&
+ (thold_att < sdrt->tADL_min - tset_mem))
+ thold_att = sdrt->tADL_min - tset_mem;
+ if (sdrt->tWH_min > tset_mem &&
+ (thold_att < sdrt->tWH_min - tset_mem))
+ thold_att = sdrt->tWH_min - tset_mem;
+ if (sdrt->tWHR_min > tset_mem &&
+ (thold_att < sdrt->tWHR_min - tset_mem))
+ thold_att = sdrt->tWHR_min - tset_mem;
+ if ((sdrt->tRC_min > tset_att + twait) &&
+ (thold_att < sdrt->tRC_min - (tset_att + twait)))
+ thold_att = sdrt->tRC_min - (tset_att + twait);
+ if ((sdrt->tWC_min > tset_att + twait) &&
+ (thold_att < sdrt->tWC_min - (tset_att + twait)))
+ thold_att = sdrt->tWC_min - (tset_att + twait);
+ timing = DIV_ROUND_UP(thold_att, hclkp);
+ tims->thold_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+}
+
+static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr,
+ const struct nand_interface_config *conf)
+{
+ const struct nand_sdr_timings *sdrt;
+
+ sdrt = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdrt))
+ return PTR_ERR(sdrt);
+
+ if (conf->timings.mode > 3)
+ return -EOPNOTSUPP;
+
+ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ stm32_fmc2_nfc_calc_timings(chip, sdrt);
+ stm32_fmc2_nfc_timings_init(chip);
+
+ return 0;
+}
+
+static void stm32_fmc2_nfc_nand_callbacks_setup(struct nand_chip *chip)
+{
+ /*
+ * Specific callbacks to read/write a page depending on
+ * the mode (polling/sequencer) and the algo used (Hamming, BCH).
+ */
+ chip->ecc.hwctl = stm32_fmc2_nfc_hwctl;
+ if (chip->ecc.strength == FMC2_ECC_HAM) {
+ /* Hamming is used */
+ chip->ecc.calculate = stm32_fmc2_nfc_ham_calculate;
+ chip->ecc.correct = stm32_fmc2_nfc_ham_correct;
+ chip->ecc.options |= NAND_ECC_GENERIC_ERASED_CHECK;
+ } else {
+ /* BCH is used */
+ chip->ecc.calculate = stm32_fmc2_nfc_bch_calculate;
+ chip->ecc.correct = stm32_fmc2_nfc_bch_correct;
+ chip->ecc.read_page = stm32_fmc2_nfc_read_page;
+ }
+
+ /* Specific configurations depending on the algo used */
+ if (chip->ecc.strength == FMC2_ECC_HAM)
+ chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3;
+ else if (chip->ecc.strength == FMC2_ECC_BCH8)
+ chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13;
+ else
+ chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7;
+}
+
+static int stm32_fmc2_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = ecc->total;
+ oobregion->offset = FMC2_BBM_LEN;
+
+ return 0;
+}
+
+static int stm32_fmc2_nfc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = mtd->oobsize - ecc->total - FMC2_BBM_LEN;
+ oobregion->offset = ecc->total + FMC2_BBM_LEN;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops stm32_fmc2_nfc_ooblayout_ops = {
+ .ecc = stm32_fmc2_nfc_ooblayout_ecc,
+ .free = stm32_fmc2_nfc_ooblayout_free,
+};
+
+static int stm32_fmc2_nfc_calc_ecc_bytes(int step_size, int strength)
+{
+ /* Hamming */
+ if (strength == FMC2_ECC_HAM)
+ return 4;
+
+ /* BCH8 */
+ if (strength == FMC2_ECC_BCH8)
+ return 14;
+
+ /* BCH4 */
+ return 8;
+}
+
+NAND_ECC_CAPS_SINGLE(stm32_fmc2_nfc_ecc_caps, stm32_fmc2_nfc_calc_ecc_bytes,
+ FMC2_ECC_STEP_SIZE,
+ FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8);
+
+static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip)
+{
+ struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ /* Default ECC settings in case they are not set in the device tree */
+ if (!chip->ecc.size)
+ chip->ecc.size = FMC2_ECC_STEP_SIZE;
+
+ if (!chip->ecc.strength)
+ chip->ecc.strength = FMC2_ECC_BCH8;
+
+ ret = nand_ecc_choose_conf(chip, &stm32_fmc2_nfc_ecc_caps,
+ mtd->oobsize - FMC2_BBM_LEN);
+ if (ret) {
+ dev_err(nfc->dev, "no valid ECC settings set\n");
+ return ret;
+ }
+
+ if (mtd->writesize / chip->ecc.size > FMC2_MAX_SG) {
+ dev_err(nfc->dev, "nand page size is not supported\n");
+ return -EINVAL;
+ }
+
+ if (chip->bbt_options & NAND_BBT_USE_FLASH)
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+
+ stm32_fmc2_nfc_nand_callbacks_setup(chip);
+
+ mtd_set_ooblayout(mtd, &stm32_fmc2_nfc_ooblayout_ops);
+
+ stm32_fmc2_nfc_setup(chip);
+
+ return 0;
+}
+
+static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = {
+ .attach_chip = stm32_fmc2_nfc_attach_chip,
+ .exec_op = stm32_fmc2_nfc_exec_op,
+ .setup_interface = stm32_fmc2_nfc_setup_interface,
+};
+
+static void stm32_fmc2_nfc_wp_enable(struct stm32_fmc2_nand *nand)
+{
+ if (nand->wp_gpio)
+ gpiod_set_value(nand->wp_gpio, 1);
+}
+
+static void stm32_fmc2_nfc_wp_disable(struct stm32_fmc2_nand *nand)
+{
+ if (nand->wp_gpio)
+ gpiod_set_value(nand->wp_gpio, 0);
+}
+
+static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
+ struct device_node *dn)
+{
+ struct stm32_fmc2_nand *nand = &nfc->nand;
+ u32 cs;
+ int ret, i;
+
+ if (!of_get_property(dn, "reg", &nand->ncs))
+ return -EINVAL;
+
+ nand->ncs /= sizeof(u32);
+ if (!nand->ncs) {
+ dev_err(nfc->dev, "invalid reg property size\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nand->ncs; i++) {
+ ret = of_property_read_u32_index(dn, "reg", i, &cs);
+ if (ret) {
+ dev_err(nfc->dev, "could not retrieve reg property: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (cs >= FMC2_MAX_CE) {
+ dev_err(nfc->dev, "invalid reg value: %d\n", cs);
+ return -EINVAL;
+ }
+
+ if (nfc->cs_assigned & BIT(cs)) {
+ dev_err(nfc->dev, "cs already assigned: %d\n", cs);
+ return -EINVAL;
+ }
+
+ nfc->cs_assigned |= BIT(cs);
+ nand->cs_used[i] = cs;
+ }
+
+ nand->wp_gpio = dev_gpiod_get(nfc->dev, dn, "wp", GPIOD_OUT_HIGH, "wp");
+ if (IS_ERR(nand->wp_gpio)) {
+ ret = PTR_ERR(nand->wp_gpio);
+ if (ret != -ENOENT)
+ return dev_err_probe(nfc->dev, ret,
+ "failed to request WP GPIO\n");
+
+ nand->wp_gpio = NULL;
+ }
+
+ nand_set_flash_node(&nand->chip, dn);
+
+ return 0;
+}
+
+static int stm32_fmc2_nfc_parse_dt(struct stm32_fmc2_nfc *nfc)
+{
+ struct device_node *dn = nfc->dev->of_node;
+ struct device_node *child;
+ int nchips = of_get_child_count(dn);
+ int ret = 0;
+
+ if (!nchips) {
+ dev_err(nfc->dev, "NAND chip not defined\n");
+ return -EINVAL;
+ }
+
+ if (nchips > 1) {
+ dev_err(nfc->dev, "too many NAND chips defined\n");
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(dn, child) {
+ ret = stm32_fmc2_nfc_parse_child(nfc, child);
+ if (ret < 0) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int stm32_fmc2_nfc_set_cdev(struct stm32_fmc2_nfc *nfc)
+{
+ struct device *dev = nfc->dev;
+ bool ebi_found = false;
+
+ if (dev->parent && of_device_is_compatible(dev->parent->of_node,
+ "st,stm32mp1-fmc2-ebi"))
+ ebi_found = true;
+
+ if (of_device_is_compatible(dev->of_node, "st,stm32mp1-fmc2-nfc")) {
+ if (ebi_found) {
+ nfc->cdev = dev->parent;
+
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ if (ebi_found)
+ return -EINVAL;
+
+ nfc->cdev = dev;
+
+ return 0;
+}
+
+static int __init stm32_fmc2_nfc_probe(struct device *dev)
+{
+ struct stm32_fmc2_nfc *nfc;
+ struct stm32_fmc2_nand *nand;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct resource cres;
+ int chip_cs, mem_region, ret;
+ int start_region = 0;
+
+ nfc = kzalloc(sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ nfc->dev = dev;
+ nand_controller_init(&nfc->base);
+ nfc->base.ops = &stm32_fmc2_nfc_controller_ops;
+
+ ret = stm32_fmc2_nfc_set_cdev(nfc);
+ if (ret)
+ return ret;
+
+ ret = stm32_fmc2_nfc_parse_dt(nfc);
+ if (ret)
+ return ret;
+
+ ret = of_address_to_resource(nfc->cdev->of_node, 0, &cres);
+ if (ret)
+ return ret;
+
+ nfc->regmap = device_node_to_regmap(nfc->cdev->of_node);
+ if (IS_ERR(nfc->regmap))
+ return PTR_ERR(nfc->regmap);
+
+ if (nfc->dev == nfc->cdev)
+ start_region = 1;
+
+ for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE;
+ chip_cs++, mem_region += 3) {
+ if (!(nfc->cs_assigned & BIT(chip_cs)))
+ continue;
+
+ nfc->data_base[chip_cs] = of_iomap(dev->of_node, mem_region);
+ if (IS_ERR(nfc->data_base[chip_cs]))
+ return PTR_ERR(nfc->data_base[chip_cs]);
+
+ nfc->cmd_base[chip_cs] = of_iomap(dev->of_node, mem_region + 1);
+ if (IS_ERR(nfc->cmd_base[chip_cs]))
+ return PTR_ERR(nfc->cmd_base[chip_cs]);
+
+ nfc->addr_base[chip_cs] = of_iomap(dev->of_node, mem_region + 2);
+ if (IS_ERR(nfc->addr_base[chip_cs]))
+ return PTR_ERR(nfc->addr_base[chip_cs]);
+ }
+
+ nfc->clk = clk_get(nfc->cdev, NULL);
+ if (IS_ERR(nfc->clk))
+ return PTR_ERR(nfc->clk);
+
+ ret = clk_prepare_enable(nfc->clk);
+ if (ret) {
+ dev_err(dev, "can not enable the clock\n");
+ return ret;
+ }
+
+ ret = device_reset_us(dev, 2);
+ if (ret)
+ goto err_clk_disable;
+
+ stm32_fmc2_nfc_init(nfc);
+
+ nand = &nfc->nand;
+ chip = &nand->chip;
+ mtd = nand_to_mtd(chip);
+ mtd->dev.parent = dev;
+
+ chip->controller = &nfc->base;
+ chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
+
+ /* Default ECC settings */
+ chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
+ chip->ecc.size = FMC2_ECC_STEP_SIZE;
+ chip->ecc.strength = FMC2_ECC_BCH8;
+
+ stm32_fmc2_nfc_wp_disable(nand);
+
+ /* Scan to find existence of the device */
+ ret = nand_scan(chip, nand->ncs);
+ if (ret)
+ goto err_wp_enable;
+
+ ret = add_mtd_nand_device(mtd, "nand");
+ if (ret)
+ goto err_nand_cleanup;
+
+ return 0;
+
+err_nand_cleanup:
+ nand_cleanup(chip);
+
+err_wp_enable:
+ stm32_fmc2_nfc_wp_enable(nand);
+
+err_clk_disable:
+ clk_disable_unprepare(nfc->clk);
+
+ return ret;
+}
+
+static __maybe_unused struct of_device_id stm32_fmc2_nfc_match[] = {
+ { .compatible = "st,stm32mp15-fmc2", },
+ { .compatible = "st,stm32mp1-fmc2-nfc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stm32_fmc2_nfc_match);
+
+static struct driver stm32_fmc2_nfc_driver = {
+ .name = "stm32_fmc2_nfc",
+ .probe = stm32_fmc2_nfc_probe,
+ .of_compatible = DRV_OF_COMPAT(stm32_fmc2_nfc_match),
+};
+coredevice_platform_driver(stm32_fmc2_nfc_driver);
diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c
index 3b0d2332aa..2cb3d5538f 100644
--- a/drivers/mtd/nor/cfi_flash.c
+++ b/drivers/mtd/nor/cfi_flash.c
@@ -652,14 +652,14 @@ static int cfi_mtd_protect(struct flash_info *finfo, loff_t offset, size_t len,
return 0;
}
-static int cfi_mtd_lock(struct mtd_info *mtd, loff_t offset, size_t len)
+static int cfi_mtd_lock(struct mtd_info *mtd, loff_t offset, uint64_t len)
{
struct flash_info *finfo = container_of(mtd, struct flash_info, mtd);
return cfi_mtd_protect(finfo, offset, len, 1);
}
-static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t offset, size_t len)
+static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t offset, uint64_t len)
{
struct flash_info *finfo = container_of(mtd, struct flash_info, mtd);
@@ -758,7 +758,7 @@ static void cfi_info_one(struct flash_info *info)
return;
}
-static void cfi_info(struct device_d *dev)
+static void cfi_info(struct device *dev)
{
struct cfi_priv *priv = dev->priv;
int i;
@@ -965,7 +965,10 @@ static int cfi_probe_one(struct flash_info *info, int num)
return PTR_ERR(iores);
info->base = IOMEM(iores->start);
- /* TODO: either remap memory region or disable NULL pointer page */
+ /*
+ * Platforms hitting this should remap memory region, e.g. via virtual-reg
+ * device tree property or disable MMU.
+ */
if (IS_ENABLED(CONFIG_MMU) && iores->start == 0)
return -EPERM;
@@ -984,7 +987,7 @@ static int cfi_probe_one(struct flash_info *info, int num)
return 0;
}
-static int cfi_probe(struct device_d *dev)
+static int cfi_probe(struct device *dev)
{
struct cfi_priv *priv;
int i, ret;
@@ -997,7 +1000,7 @@ static int cfi_probe(struct device_d *dev)
priv->infos = xzalloc(sizeof(*priv->infos) * priv->num_devs);
priv->mtds = xzalloc(sizeof(*priv->mtds) * priv->num_devs);
- of_property_read_string(dev->device_node, "linux,mtd-name", &mtd_name);
+ of_property_read_string(dev->of_node, "linux,mtd-name", &mtd_name);
if (!mtd_name)
mtd_name = dev_name(dev);
@@ -1046,8 +1049,9 @@ static __maybe_unused struct of_device_id cfi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, cfi_dt_ids);
-static struct driver_d cfi_driver = {
+static struct driver cfi_driver = {
.name = "cfi_flash",
.probe = cfi_probe,
.of_compatible = DRV_OF_COMPAT(cfi_dt_ids),
diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h
index b8428206b9..5d3053f971 100644
--- a/drivers/mtd/nor/cfi_flash.h
+++ b/drivers/mtd/nor/cfi_flash.h
@@ -35,7 +35,7 @@ struct cfi_cmd_set;
*/
struct flash_info {
- struct device_d *dev;
+ struct device *dev;
unsigned long size; /* total bank size in bytes */
unsigned int sector_count; /* number of erase units */
unsigned long flash_id; /* combined device & manufacturer code */
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index 4ebc5bba41..c53375e0e2 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -80,7 +80,7 @@ static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
return ret;
}
-static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, size_t len)
+static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, uint64_t len)
{
if (!mtd->parent->_lock)
return -ENOSYS;
@@ -96,7 +96,7 @@ static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, size_t len)
return mtd->parent->_lock(mtd->parent, offset, len);
}
-static int mtd_part_unlock(struct mtd_info *mtd, loff_t offset, size_t len)
+static int mtd_part_unlock(struct mtd_info *mtd, loff_t offset, uint64_t len)
{
if (!mtd->parent->_unlock)
return -ENOSYS;
diff --git a/drivers/mtd/peb.c b/drivers/mtd/peb.c
index 8443ed86bc..a17d42885e 100644
--- a/drivers/mtd/peb.c
+++ b/drivers/mtd/peb.c
@@ -205,10 +205,12 @@ int mtd_peb_read(struct mtd_info *mtd, void *buf, int pnum, int offset,
return -EINVAL;
if (offset < 0 || offset + len > mtd->erasesize)
return -EINVAL;
- if (len <= 0)
+ if (len < 0)
return -EINVAL;
if (mtd_peb_is_bad(mtd, pnum))
return -EINVAL;
+ if (!len)
+ return 0;
/* Deliberately corrupt the buffer */
*((uint8_t *)buf) ^= 0xFF;
@@ -388,12 +390,14 @@ int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
return -EINVAL;
if (offset < 0 || offset + len > mtd->erasesize)
return -EINVAL;
- if (len <= 0)
+ if (len < 0)
return -EINVAL;
if (len % (mtd->writesize >> mtd->subpage_sft))
return -EINVAL;
if (mtd_peb_is_bad(mtd, pnum))
return -EINVAL;
+ if (!len)
+ return 0;
if (mtd_peb_emulate_write_failure()) {
dev_err(&mtd->dev, "Cannot write %d bytes to PEB %d:%d (emulated)\n",
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 2beb26006e..b34c69203e 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -26,4 +26,10 @@ config SPI_CADENCE_QUADSPI
help
This enables support for the Cadence Quad SPI controller and NOR flash.
+config SPI_SYNOPSYS_OCTALSPI_NOR
+ tristate "Synopsys DesignWare Octal SPI controller"
+ help
+ This enables support for the Synopsys DesignWare Octal SPI controller
+ and NOR flash.
+
endif
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 457a2f0488..61cf789182 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
+obj-$(CONFIG_SPI_SYNOPSYS_OCTALSPI_NOR) += dw-ospi-nor.o
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 98b84cab1e..763858567b 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -40,7 +40,7 @@ struct cqspi_flash_pdata {
};
struct cqspi_st {
- struct device_d *dev;
+ struct device *dev;
struct clk *l4_mp_clk;
struct clk *qspi_clk;
unsigned int sclk;
@@ -336,8 +336,7 @@ static int cqspi_command_read(struct spi_nor *nor,
if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || rxbuf == NULL) {
dev_err(nor->dev,
- "Invalid input argument, len %d rxbuf 0x%08x\n", n_rx,
- (unsigned int)rxbuf);
+ "Invalid input argument, len %d rxbuf 0x%p\n", n_rx, rxbuf);
return -EINVAL;
}
@@ -382,8 +381,7 @@ static __maybe_unused int cqspi_command_write(struct spi_nor *nor,
if (n_tx > 4 || (n_tx && txbuf == NULL)) {
dev_err(nor->dev,
- "Invalid input argument, cmdlen %d txbuf 0x%08x\n",
- n_tx, (unsigned int)txbuf);
+ "Invalid input argument, cmdlen %d txbuf 0x%p\n", n_tx, txbuf);
return -EINVAL;
}
@@ -422,7 +420,7 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
{
struct cqspi_flash_pdata *f_pdata;
struct cqspi_st *cqspi = nor->priv;
- unsigned int ahb_base = (unsigned int) cqspi->ahb_base;
+ phys_addr_t ahb_base = virt_to_phys(cqspi->ahb_base);
void __iomem *reg_base = cqspi->iobase;
unsigned int dummy_clk = 0;
unsigned int dummy_bytes;
@@ -1001,7 +999,7 @@ static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
return ret;
}
-static int cqspi_of_get_flash_pdata(struct device_d *dev,
+static int cqspi_of_get_flash_pdata(struct device *dev,
struct cqspi_flash_pdata *f_pdata,
struct device_node *np)
{
@@ -1058,8 +1056,8 @@ static int cqspi_of_get_flash_pdata(struct device_d *dev,
static int cqspi_parse_dt(struct cqspi_st *cqspi)
{
- struct device_node *np = cqspi->dev->device_node;
- struct device_d *dev = cqspi->dev;
+ struct device_node *np = cqspi->dev->of_node;
+ struct device *dev = cqspi->dev;
cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs");
@@ -1071,7 +1069,7 @@ static int cqspi_parse_dt(struct cqspi_st *cqspi)
return 0;
}
-static int cqspi_setup_flash(struct device_d *dev,
+static int cqspi_setup_flash(struct device *dev,
struct cqspi_flash_pdata *f_pdata,
struct device_node *np)
{
@@ -1101,7 +1099,7 @@ static int cqspi_setup_flash(struct device_d *dev,
dev_set_name(nor->dev, np->name);
- nor->dev->device_node = np;
+ nor->dev->of_node = np;
nor->dev->id = DEVICE_ID_SINGLE;
nor->dev->parent = dev;
ret = register_device(nor->dev);
@@ -1152,10 +1150,10 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
cqspi_controller_enable(cqspi);
}
-static int cqspi_probe(struct device_d *dev)
+static int cqspi_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct cqspi_st *cqspi;
struct cadence_qspi_platform_data *pdata = dev->platform_data;
int ret;
@@ -1203,7 +1201,7 @@ static int cqspi_probe(struct device_d *dev)
cqspi->current_cs = -1;
cqspi->sclk = 0;
- if (!dev->device_node) {
+ if (!dev->of_node) {
struct cqspi_flash_pdata *f_pdata;
f_pdata = &cqspi->f_pdata[0];
@@ -1213,7 +1211,7 @@ static int cqspi_probe(struct device_d *dev)
goto probe_failed;
} else {
/* Get flash device data */
- for_each_available_child_of_node(dev->device_node, np) {
+ for_each_available_child_of_node(dev->of_node, np) {
struct cqspi_flash_pdata *f_pdata;
unsigned int cs;
if (of_property_read_u32(np, "reg", &cs)) {
@@ -1244,8 +1242,9 @@ static __maybe_unused struct of_device_id cqspi_dt_ids[] = {
{.compatible = "cdns,qspi-nor",},
{ /* end of table */ }
};
+MODULE_DEVICE_TABLE(of, cqspi_dt_ids);
-static struct driver_d cqspi_driver = {
+static struct driver cqspi_driver = {
.name = "cadence_qspi",
.probe = cqspi_probe,
.of_compatible = DRV_OF_COMPAT(cqspi_dt_ids),
diff --git a/drivers/mtd/spi-nor/dw-ospi-nor.c b/drivers/mtd/spi-nor/dw-ospi-nor.c
new file mode 100644
index 0000000000..897f4f49a9
--- /dev/null
+++ b/drivers/mtd/spi-nor/dw-ospi-nor.c
@@ -0,0 +1,929 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2018 Vincent Chardon, Kalray Inc.
+// SPDX-FileCopyrightText: 2023 Jules Maselbas, Kalray Inc.
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/spi-nor.h>
+#include <of.h>
+#include <spi/spi.h>
+
+/* Register offsets */
+#define DW_SPI_CTRL0 0x00
+#define DW_SPI_CTRL1 0x04
+#define DW_SPI_SSIENR 0x08
+#define DW_SPI_MWCR 0x0c
+#define DW_SPI_SER 0x10
+#define DW_SPI_BAUDR 0x14
+#define DW_SPI_TXFTLR 0x18
+#define DW_SPI_RXFTLR 0x1c
+#define DW_SPI_TXFLR 0x20
+#define DW_SPI_RXFLR 0x24
+#define DW_SPI_SR 0x28
+#define DW_SPI_IMR 0x2c
+#define DW_SPI_ISR 0x30
+#define DW_SPI_RISR 0x34
+#define DW_SPI_TXOICR 0x38
+#define DW_SPI_RXOICR 0x3c
+#define DW_SPI_RXUICR 0x40
+#define DW_SPI_MSTICR 0x44
+#define DW_SPI_ICR 0x48
+#define DW_SPI_DMACR 0x4c
+#define DW_SPI_DMATDLR 0x50
+#define DW_SPI_DMARDLR 0x54
+#define DW_SPI_IDR 0x58
+#define DW_SPI_VERSION 0x5c
+#define DW_SPI_DR 0x60
+#define DW_SPI_SPI_CTRL0 0xf4
+
+/* Bit fields in CTRLR0 */
+#define SPI_DFS_OFFSET 0
+#define SPI_DFS_MASK 0x1f
+#define SPI_DFS_8_BITS 0x7
+
+#define SPI_FRF_OFFSET 6
+#define SPI_FRF_SPI 0x0
+#define SPI_FRF_SSP 0x1
+#define SPI_FRF_MICROWIRE 0x2
+#define SPI_FRF_RESV 0x3
+
+#define SPI_MODE_OFFSET 8
+#define SPI_SCPH_OFFSET 8
+#define SPI_SCOL_OFFSET 9
+
+#define SPI_TMOD_OFFSET 10
+#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET)
+#define SPI_TMOD_TR 0x0 /* xmit & recv */
+#define SPI_TMOD_TO 0x1 /* xmit only */
+#define SPI_TMOD_RO 0x2 /* recv only */
+#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
+
+#define SPI_SLVOE_OFFSET 12
+#define SPI_SRL_OFFSET 13
+#define SPI_SSTE_OFFSET 14
+
+#define SPI_CFS_OFFSET 16
+#define SPI_CFS_MASK (0xf << SPI_CFS_OFFSET)
+
+#define SPI_SPI_FRF_OFFSET 22
+#define SPI_SPI_FRF_MASK (0x3 << SPI_SPI_FRF_OFFSET)
+#define SPI_STANDARD_FORMAT 0
+#define SPI_DUAL_FORMAT 1
+#define SPI_QUAD_FORMAT 2
+#define SPI_OCTAL_FORMAT 3
+
+#define DW_SPI_CTRL1_NDF_MASK 0xffff
+
+#define SPI_TXFTLR_TXFTHR_OFFSET 16
+
+/* Bit fields in SR, 7 bits */
+#define SR_MASK 0x7f
+#define SR_BUSY BIT(0)
+#define SR_TF_NOT_FULL BIT(1)
+#define SR_TF_EMPT BIT(2)
+#define SR_RF_NOT_EMPT BIT(3)
+#define SR_RF_FULL BIT(4)
+#define SR_TX_ERR BIT(5)
+#define SR_DCOL BIT(6)
+
+/* Bit fields in ISR, IMR, RISR, 7 bits */
+#define SPI_INT_TXEI BIT(0)
+#define SPI_INT_TXOI BIT(1)
+#define SPI_INT_RXUI BIT(2)
+#define SPI_INT_RXOI BIT(3)
+#define SPI_INT_RXFI BIT(4)
+#define SPI_INT_MSTI BIT(5)
+
+/* Bit fields in DMACR */
+#define SPI_DMA_RDMAE BIT(0)
+#define SPI_DMA_TDMAE BIT(1)
+
+/* Bit fields in SPI_CTRL0 */
+#define SPI_SPI_CTRL0_INST_L8 (0x2 << 8) /* two bit value */
+#define SPI_SPI_CTRL0_WAIT_8_CYCLE (0x8 << 11)/* five bit value */
+#define SPI_SPI_CTRL0_EN_CLK_STRETCH BIT(30)
+
+#define SPI_SPI_CTRL0_ADDR_L_OFFSET 2
+#define SPI_SPI_CTRL0_ADDR_L_MASK (0xf << SPI_SPI_CTRL0_ADDR_L_OFFSET)
+#define SPI_SPI_CTRL0_ADDR_L24 0x6 /* 3 bytes address */
+#define SPI_SPI_CTRL0_ADDR_L32 0x8 /* 4 bytes address */
+
+/* TX/RX FIFO maximum size */
+#define TX_FIFO_MAX_SIZE 256
+#define RX_FIFO_MAX_SIZE 256
+
+/* TX/RX interrupt level threshold, max is 256 */
+#define SPI_INT_THRESHOLD 32
+
+#define DW_SPI_MAX_CHIPSELECT 16
+
+struct dw_spi_flash_pdata {
+ struct mtd_info mtd;
+ struct spi_nor nor;
+ u32 clk_rate;
+ int cs;
+};
+
+static inline struct dw_spi_flash_pdata *to_flash_pdata(struct spi_nor *nor)
+{
+ return container_of(nor, struct dw_spi_flash_pdata, nor);
+}
+
+struct dw_spi_nor {
+ struct device *dev;
+ struct clk *clk;
+ unsigned int sclk;
+ void __iomem *regs;
+ unsigned int master_ref_clk_hz;
+ bool clk_strech_en;
+ unsigned int tx_fifo_len;
+ int rx_fifo_len;
+ int supported_cs;
+ int current_cs;
+ struct dw_spi_flash_pdata f_pdata[DW_SPI_MAX_CHIPSELECT];
+};
+
+static u32 dw_readl(struct dw_spi_nor *dws, u32 offset)
+{
+ return readl(dws->regs + offset);
+}
+
+static void dw_writel(struct dw_spi_nor *dws, u32 offset, u32 val)
+{
+ writel(val, dws->regs + offset);
+}
+
+static void dw_spi_enable_chip(struct dw_spi_nor *dws, int enable)
+{
+ dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0));
+}
+
+/* Disable IRQ bits */
+static void dw_spi_mask_intr(struct dw_spi_nor *dws, u32 mask)
+{
+ u32 new_mask;
+
+ new_mask = dw_readl(dws, DW_SPI_IMR) & ~mask;
+ dw_writel(dws, DW_SPI_IMR, new_mask);
+}
+
+/*
+ * This does disable the SPI controller, interrupts, and re-enable the
+ * controller back. Transmit and receive FIFO buffers are cleared when the
+ * device is disabled.
+ */
+static void dw_spi_reset_chip(struct dw_spi_nor *dw_spi)
+{
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_spi_mask_intr(dw_spi, 0xff);
+ dw_spi_enable_chip(dw_spi, 1);
+}
+
+static int dw_spi_set_cs(struct dw_spi_nor *dw_spi, int cs)
+{
+ if (cs > dw_spi->supported_cs) {
+ dev_err(dw_spi->dev, "invalid chip select\n");
+ return -EINVAL;
+ }
+
+ dw_spi_enable_chip(dw_spi, 0);
+
+ if (cs == -1) /* no slave */
+ dw_writel(dw_spi, DW_SPI_SER, 0);
+ else
+ dw_writel(dw_spi, DW_SPI_SER, BIT(cs));
+ dw_spi->current_cs = cs;
+
+ dw_spi_enable_chip(dw_spi, 1);
+
+ return 0;
+}
+
+static void dw_spi_hw_init(struct dw_spi_nor *dw_spi)
+{
+ u32 ctrl0;
+ u32 spi_ctrl0;
+
+ dw_spi_reset_chip(dw_spi);
+ dw_spi_enable_chip(dw_spi, 0);
+
+ /* the line will automatically toggle between consecutive data frame */
+ ctrl0 = dw_readl(dw_spi, DW_SPI_CTRL0);
+ ctrl0 &= ~(SPI_DFS_MASK);
+ ctrl0 |= SPI_DFS_8_BITS;
+ ctrl0 &= ~(BIT(SPI_SSTE_OFFSET));
+ dw_writel(dw_spi, DW_SPI_CTRL0, ctrl0);
+
+ /* SPI_CTRL0 is initializtion */
+ spi_ctrl0 = SPI_SPI_CTRL0_INST_L8;
+ spi_ctrl0 |= SPI_SPI_CTRL0_ADDR_L32 << SPI_SPI_CTRL0_ADDR_L_OFFSET;
+ spi_ctrl0 |= SPI_SPI_CTRL0_WAIT_8_CYCLE;
+ spi_ctrl0 |= SPI_SPI_CTRL0_EN_CLK_STRETCH;
+
+ dw_writel(dw_spi, DW_SPI_SPI_CTRL0, spi_ctrl0);
+
+ dw_spi_enable_chip(dw_spi, 1);
+}
+
+static int dw_spi_of_get_flash_pdata(struct device *dev,
+ struct dw_spi_flash_pdata *f_pdata,
+ struct device_node *np)
+{
+ struct dw_spi_nor *dw_spi = dev->priv;
+ unsigned int max_clk_rate = dw_spi->master_ref_clk_hz / 2;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "spi-max-frequency", &f_pdata->clk_rate)) {
+ dev_err(dev, "couldn't determine spi-max-frequency\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(dev, "spi-max-frequency = %u\n", f_pdata->clk_rate);
+
+ /* SPI clock cannot go higher than half the master ref clock */
+ if (f_pdata->clk_rate > max_clk_rate) {
+ f_pdata->clk_rate = max_clk_rate;
+ dev_warn(dev, "limiting SPI frequency to %u\n",
+ f_pdata->clk_rate);
+ }
+
+ return 0;
+}
+
+static int dw_spi_wait_not_busy(struct dw_spi_nor *dw_spi)
+{
+ if (wait_on_timeout(100 * MSECOND,
+ !(dw_readl(dw_spi, DW_SPI_SR) & SR_BUSY))) {
+ dev_err(dw_spi->dev, "Timeout, wait not busy.\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static bool dw_spi_tx_fifo_not_full(struct dw_spi_nor *dw_spi)
+{
+ return (dw_readl(dw_spi, DW_SPI_SR) & SR_TF_NOT_FULL) != 0;
+}
+
+static bool dw_spi_tx_fifo_empty(struct dw_spi_nor *dw_spi)
+{
+ return (dw_readl(dw_spi, DW_SPI_SR) & SR_TF_EMPT) != 0;
+}
+
+static bool dw_spi_rx_fifo_not_empty(struct dw_spi_nor *dw_spi)
+{
+ return (dw_readl(dw_spi, DW_SPI_SR) & SR_RF_NOT_EMPT) != 0;
+}
+
+static int dw_spi_is_enhanced(enum spi_nor_protocol proto)
+{
+ return proto != SNOR_PROTO_1_1_1;
+}
+
+static int dw_spi_rx_tx_fifo_overflow(struct dw_spi_nor *dw_spi)
+{
+ return dw_readl(dw_spi, DW_SPI_RISR) & (SPI_INT_RXOI | SPI_INT_TXOI);
+}
+
+static int dw_spi_config_baudrate_div(struct dw_spi_nor *dws, unsigned int sclk)
+{
+ unsigned int div;
+
+ dws->sclk = sclk;
+ div = dws->master_ref_clk_hz / sclk;
+ /* divisor value must be even */
+ div += div % 2;
+
+ dev_dbg(dws->dev, "configure clock divider (%u/%u) -> %u\n",
+ dws->master_ref_clk_hz, sclk, div);
+ dw_spi_enable_chip(dws, 0);
+ dw_writel(dws, DW_SPI_BAUDR, div);
+ dw_spi_enable_chip(dws, 1);
+
+ if (dw_readl(dws, DW_SPI_BAUDR) != div) {
+ dev_err(dws->dev, "Unable to configure clock divider\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dw_spi_prep_slave_cfg(struct spi_nor *nor)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ struct dw_spi_flash_pdata *f_pdata = to_flash_pdata(nor);
+ int ret;
+
+ /* switch chip select */
+ if (dw_spi->current_cs != f_pdata->cs) {
+ ret = dw_spi_set_cs(dw_spi, f_pdata->cs);
+ if (ret)
+ return ret;
+ }
+
+ /* setup baudrate divisor */
+ if (dw_spi->sclk != f_pdata->clk_rate) {
+ ret = dw_spi_config_baudrate_div(dw_spi, f_pdata->clk_rate);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dw_spi_set_ctrl0(struct dw_spi_nor *dw_spi, u8 tmod, u8 frf)
+{
+ u32 ctrl0;
+
+ /* spi mode configuration */
+ ctrl0 = dw_readl(dw_spi, DW_SPI_CTRL0);
+ ctrl0 &= ~(SPI_TMOD_MASK | SPI_SPI_FRF_MASK);
+ ctrl0 |= tmod << SPI_TMOD_OFFSET;
+ ctrl0 |= frf << SPI_SPI_FRF_OFFSET;
+
+ dw_spi_enable_chip(dw_spi, 0);
+ dev_dbg(dw_spi->dev, "Setting ctrl0 to %x\n", ctrl0);
+ dw_writel(dw_spi, DW_SPI_CTRL0, ctrl0);
+ dw_spi_enable_chip(dw_spi, 1);
+}
+
+static int dw_spi_prep(struct spi_nor *nor, u8 tmod, u8 frf)
+{
+ int ret;
+ struct dw_spi_nor *dw_spi = nor->priv;
+
+ ret = dw_spi_prep_slave_cfg(nor);
+ if (ret)
+ return ret;
+
+ dw_spi_set_ctrl0(dw_spi, tmod, frf);
+
+ return 0;
+}
+
+static int dw_spi_set_addr_len(struct spi_nor *nor)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ u32 val, addr_l;
+
+ val = dw_readl(dw_spi, DW_SPI_SPI_CTRL0);
+ val &= ~SPI_SPI_CTRL0_ADDR_L_MASK;
+
+ if (nor->addr_width == 3) {
+ addr_l = SPI_SPI_CTRL0_ADDR_L24;
+ } else if (nor->addr_width == 4) {
+ addr_l = SPI_SPI_CTRL0_ADDR_L32;
+ } else {
+ dev_err(nor->dev, "unsupported addr_width %d\n",
+ nor->addr_width);
+ return -EINVAL;
+ }
+
+ val |= addr_l << SPI_SPI_CTRL0_ADDR_L_OFFSET;
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_writel(dw_spi, DW_SPI_SPI_CTRL0, val);
+ dw_spi_enable_chip(dw_spi, 1);
+
+ return 0;
+}
+
+static int dw_spi_prep_enhanced(struct spi_nor *nor,
+ enum spi_nor_protocol proto, u8 tmod)
+{
+ int ret;
+ u8 frf;
+
+ switch (proto) {
+ case SNOR_PROTO_1_1_2:
+ dev_dbg(nor->dev, "dual mode\n");
+ frf = SPI_DUAL_FORMAT;
+ break;
+ case SNOR_PROTO_1_1_4:
+ dev_dbg(nor->dev, "quad mode\n");
+ frf = SPI_QUAD_FORMAT;
+ break;
+ default:
+ dev_err(nor->dev, "unsupported enhanced mode %d\n",
+ nor->read_proto);
+ return -EINVAL;
+ }
+
+ ret = dw_spi_set_addr_len(nor);
+ if (ret)
+ return ret;
+
+ return dw_spi_prep(nor, tmod, frf);
+}
+
+static int dw_spi_prep_std(struct spi_nor *nor, u8 tmod)
+{
+ return dw_spi_prep(nor, tmod, SPI_STANDARD_FORMAT);
+}
+
+static int dw_spi_read_enhanced(struct spi_nor *nor, const u8 opcode,
+ int address, u8 *rxbuf, size_t n_rx)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ u32 offset = 0;
+ int ret;
+
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_writel(dw_spi, DW_SPI_CTRL1, n_rx - 1);
+ dw_spi_enable_chip(dw_spi, 1);
+
+ ret = dw_spi_wait_not_busy(dw_spi);
+ if (ret)
+ return ret;
+
+ /* send the opcode and the address */
+ dw_writel(dw_spi, DW_SPI_DR, opcode);
+ dw_writel(dw_spi, DW_SPI_DR, address);
+
+ while (n_rx) {
+ if (dw_spi_rx_fifo_not_empty(dw_spi)) {
+ rxbuf[offset++] = dw_readl(dw_spi, DW_SPI_DR);
+ n_rx--;
+ }
+
+ /* check RX/TX overflow */
+ if (dw_spi_rx_tx_fifo_overflow(dw_spi))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int dw_spi_read_std(struct spi_nor *nor, const u8 opcode, int address,
+ u8 *rxbuf, unsigned int n_rx)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ int tx_cnt = n_rx, rx_cnt = n_rx, skip_rx, cur_rx = 0;
+ int ret = 0, i, txfhr, rx_free;
+ u32 tmp_val;
+
+ ret = dw_spi_wait_not_busy(dw_spi);
+ if (ret)
+ return ret;
+
+ /* clear interrupts */
+ dw_readl(dw_spi, DW_SPI_ICR);
+
+ /* TX fifo must not became empty during the frame transfer:
+ * use TXFTHR (Transfert Start FIFO level) to avoid the frame
+ * to start during the first phase computation */
+ skip_rx = 1 /* opcode */ + nor->addr_width;
+ txfhr = min_t(unsigned int, skip_rx + n_rx, dw_spi->tx_fifo_len) - 1;
+ rx_free = dw_spi->rx_fifo_len - skip_rx;
+
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_writel(dw_spi, DW_SPI_TXFTLR, txfhr << SPI_TXFTLR_TXFTHR_OFFSET);
+ dw_writel(dw_spi, DW_SPI_RXFTLR, dw_spi->rx_fifo_len / 2);
+ dw_spi_enable_chip(dw_spi, 1);
+
+ /* opcode phase */
+ dw_writel(dw_spi, DW_SPI_DR, opcode);
+
+ /* address phase in TR mode */
+ for (i = nor->addr_width - 1; i >= 0; i--)
+ dw_writel(dw_spi, DW_SPI_DR, (address >> (8 * i)) & 0xff);
+
+ while (rx_cnt) {
+ /* push dummy bytes to receive data */
+ while (tx_cnt && dw_spi_tx_fifo_not_full(dw_spi) &&
+ rx_free > 0) {
+ dw_writel(dw_spi, DW_SPI_DR, 0xff);
+ tx_cnt--;
+ rx_free--;
+ }
+
+ if (dw_spi_rx_fifo_not_empty(dw_spi)) {
+ tmp_val = dw_readl(dw_spi, DW_SPI_DR);
+ rx_free++;
+ if (skip_rx) {
+ skip_rx--;
+ continue;
+ }
+
+ rxbuf[cur_rx++] = tmp_val;
+ rx_cnt--;
+ }
+ if (dw_spi_rx_tx_fifo_overflow(dw_spi))
+ return -EIO;
+ }
+
+ return n_rx;
+}
+
+static int dw_spi_wait_tx_end(struct dw_spi_nor *dw_spi)
+{
+ int res;
+
+ /* As specified in ssi_user_guide p63 the BUSY bit cannot be polled
+ * immediately. As indicated in ssi_databook p40 the TFE bit shall
+ * be tested before testing busy bit
+ */
+ res = wait_on_timeout(100 * MSECOND, dw_spi_tx_fifo_empty(dw_spi));
+ if (res < 0) {
+ dev_err(dw_spi->dev, "SPI write failure, TX FIFO is never empty\n");
+ return res;
+ }
+
+ return dw_spi_wait_not_busy(dw_spi);
+}
+
+static int dw_spi_write_enhanced(struct spi_nor *nor, u8 opcode, u32 address,
+ u8 *txbuf, unsigned int n_tx)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ size_t tx_cnt = 0;
+
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_writel(dw_spi, DW_SPI_CTRL1, n_tx - 1);
+ dw_spi_enable_chip(dw_spi, 1);
+
+ dw_writel(dw_spi, DW_SPI_DR, opcode);
+ dw_writel(dw_spi, DW_SPI_DR, address);
+
+ /* send data */
+ while (tx_cnt < n_tx) {
+ if (dw_spi_tx_fifo_not_full(dw_spi)) {
+ dw_writel(dw_spi, DW_SPI_DR, txbuf[tx_cnt]);
+ tx_cnt++;
+ }
+ }
+
+ return dw_spi_wait_tx_end(dw_spi);
+}
+
+static int dw_spi_write_std(struct spi_nor *nor, const u8 *opbuf,
+ unsigned int n_op, u8 *txbuf, unsigned int n_tx)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ int op_cnt = 0, tx_cnt = 0, txfhr;
+
+ txfhr = min_t(unsigned int, dw_spi->tx_fifo_len, n_op + n_tx) - 1;
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_writel(dw_spi, DW_SPI_TXFTLR, txfhr << SPI_TXFTLR_TXFTHR_OFFSET);
+ dw_spi_enable_chip(dw_spi, 1);
+
+ /* send opcodes */
+ while (op_cnt < n_op) {
+ if (dw_spi_tx_fifo_not_full(dw_spi)) {
+ dw_writel(dw_spi, DW_SPI_DR, opbuf[op_cnt]);
+ op_cnt++;
+ }
+ }
+
+ /* send data */
+ while (tx_cnt < n_tx) {
+ if (dw_spi_tx_fifo_not_full(dw_spi)) {
+ dw_writel(dw_spi, DW_SPI_DR, txbuf[tx_cnt]);
+ tx_cnt++;
+ }
+ }
+
+ return dw_spi_wait_tx_end(dw_spi);
+}
+
+static int dw_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ int i, ret;
+
+ ret = dw_spi_prep_std(nor, SPI_TMOD_TR);
+ if (ret)
+ return ret;
+
+ ret = dw_spi_read_std(nor, opcode, -1, buf, len);
+
+ dev_dbg(nor->dev, "read_reg opcode 0x%02x: ", opcode);
+ for (i = 0; i < len; i++)
+ pr_debug("%02x ", buf[i]);
+ pr_debug("\n");
+
+ return ret;
+}
+
+static int dw_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ int i, ret;
+
+ dev_dbg(nor->dev, "write_reg opcode 0x%02x: ", opcode);
+ for (i = 0; i < len; i++)
+ pr_debug("%02x ", buf[i]);
+ pr_debug("\n");
+
+ ret = dw_spi_prep_std(nor, SPI_TMOD_TO);
+ if (ret)
+ return ret;
+
+ return dw_spi_write_std(nor, &opcode, 1, buf, len);
+}
+
+static void dw_spi_write(struct spi_nor *nor, loff_t to,
+ size_t len, size_t *retlen, const u_char *buf)
+{
+ u8 opcode[8];
+ unsigned int opcode_len = 0;
+ int i, ret;
+ struct dw_spi_nor *dw_spi = nor->priv;
+
+ *retlen = 0;
+ dev_dbg(dw_spi->dev, "write %zu bytes at @0x%llx\n", len, to);
+
+ if (dw_spi_is_enhanced(nor->write_proto)) {
+ if (dw_spi_prep_enhanced(nor, nor->write_proto, SPI_TMOD_TO))
+ return;
+
+ ret = dw_spi_write_enhanced(nor, nor->program_opcode, to,
+ (u8 *)buf, len);
+ } else {
+ if (dw_spi_prep_std(nor, SPI_TMOD_TO))
+ return;
+
+ opcode[0] = nor->program_opcode;
+ opcode_len = 1 + nor->addr_width;
+ for (i = 0; i < nor->addr_width; i++)
+ opcode[1 + i] =
+ (to >> (8 * (nor->addr_width - 1 - i))) & 0xff;
+
+ ret = dw_spi_write_std(nor, opcode, opcode_len, (u8 *)buf, len);
+ }
+
+ if (ret == 0)
+ *retlen = len;
+}
+
+static int dw_spi_read(struct spi_nor *nor, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ struct dw_spi_nor *dw_spi = nor->priv;
+ size_t to_read, orig_len = len;
+ u8 *ptr = (u8 *)buf;
+ loff_t offset = from;
+ int ret = 0, enhanced = dw_spi_is_enhanced(nor->read_proto);
+ size_t chunk;
+
+ *retlen = 0;
+ dev_dbg(nor->dev, "read %zu bytes from @0x%llx\n", len, from);
+
+ if (enhanced)
+ ret = dw_spi_prep_enhanced(nor, nor->read_proto, SPI_TMOD_RO);
+ else
+ ret = dw_spi_prep_std(nor, SPI_TMOD_TR);
+ if (ret)
+ return ret;
+
+ /*
+ * If clock stretching is not supported, we have no way to prevent RX
+ * overflow except reducing the number received data to the size of the
+ * RX fifo
+ */
+ if (dw_spi->clk_strech_en && enhanced)
+ chunk = DW_SPI_CTRL1_NDF_MASK;
+ else
+ chunk = dw_spi->rx_fifo_len;
+
+ while (len) {
+ to_read = min(chunk, len);
+
+ if (enhanced)
+ ret = dw_spi_read_enhanced(nor, nor->read_opcode,
+ offset, ptr, to_read);
+ else
+ ret = dw_spi_read_std(nor, nor->read_opcode,
+ offset, ptr, to_read);
+ if (ret < 0)
+ return ret;
+
+ offset += to_read;
+ ptr += to_read;
+ len -= to_read;
+ }
+ *retlen = orig_len;
+
+ return ret;
+}
+
+static int dw_spi_erase(struct spi_nor *nor, loff_t offs)
+{
+ int ret = 0;
+ int i;
+ u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; /* addr is 3 or 4 bytes */
+
+ dev_dbg(nor->dev, "erase(%0x) @0x%llx\n", nor->erase_opcode, offs);
+
+ for (i = nor->addr_width - 1; i >= 0; i--) {
+ buf[i] = offs & 0xff;
+ offs >>= 8;
+ }
+
+ ret = dw_spi_prep_std(nor, SPI_TMOD_TO);
+ if (ret)
+ return ret;
+
+ /* Caller is responsible for enabling write,
+ * only send the erase sector command */
+ ret = nor->write_reg(nor, nor->erase_opcode, buf,
+ nor->addr_width);
+
+ /* Caller is responsible to wait for operation completion */
+ return ret;
+}
+
+static int dw_spi_setup_flash(struct device *dev,
+ struct dw_spi_flash_pdata *f_pdata,
+ struct device_node *np)
+{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_PP |
+ SNOR_HWCAPS_PP_1_1_4,
+ };
+ struct dw_spi_nor *dw_spi = dev->priv;
+ struct mtd_info *mtd = &f_pdata->mtd;
+ struct spi_nor *nor = &f_pdata->nor;
+ int ret;
+
+ ret = dw_spi_of_get_flash_pdata(dev, f_pdata, np);
+ if (ret)
+ goto probe_failed;
+
+ nor->dev = kzalloc(sizeof(*nor->dev), GFP_KERNEL);
+ if (!nor->dev)
+ return -ENOMEM;
+
+ dev_set_name(nor->dev, np->name);
+
+ nor->dev->of_node = np;
+ nor->dev->id = DEVICE_ID_SINGLE;
+ nor->dev->parent = dev;
+ ret = register_device(nor->dev);
+ if (ret)
+ return ret;
+
+ mtd->priv = nor;
+ mtd->dev.parent = nor->dev;
+ nor->mtd = mtd;
+ nor->priv = dw_spi;
+
+ nor->read_reg = dw_spi_read_reg;
+ nor->write_reg = dw_spi_write_reg;
+ nor->read = dw_spi_read;
+ nor->write = dw_spi_write;
+ nor->erase = dw_spi_erase;
+
+ ret = spi_nor_scan(nor, NULL, &hwcaps, false);
+ if (ret)
+ goto probe_failed;
+
+ ret = add_mtd_device(mtd, NULL, DEVICE_ID_DYNAMIC);
+ if (ret)
+ goto probe_failed;
+
+ return 0;
+
+probe_failed:
+ dev_err(dev, "probing for flashchip failed\n");
+ return ret;
+}
+
+static void dw_spi_detect_hw_params(struct dw_spi_nor *dw_spi)
+{
+ int fifo;
+
+ /* Detect supported slave number */
+ dw_spi_enable_chip(dw_spi, 0);
+ dw_writel(dw_spi, DW_SPI_SER, 0xffff);
+ dw_spi_enable_chip(dw_spi, 1);
+ dw_spi->supported_cs = hweight32(dw_readl(dw_spi, DW_SPI_SER));
+
+ dw_spi_set_cs(dw_spi, -1);
+ dw_spi->sclk = 0;
+
+ /* Detect the FIFO depth */
+ dw_spi_enable_chip(dw_spi, 0);
+ for (fifo = 1; fifo < TX_FIFO_MAX_SIZE; fifo++) {
+ dw_writel(dw_spi, DW_SPI_TXFTLR, fifo);
+ if (fifo != dw_readl(dw_spi, DW_SPI_TXFTLR))
+ break;
+ }
+ dw_writel(dw_spi, DW_SPI_TXFTLR, 0);
+ dw_spi->tx_fifo_len = (fifo == 1) ? 0 : fifo;
+ dev_dbg(dw_spi->dev, "Detected TX FIFO size: %u bytes\n",
+ dw_spi->tx_fifo_len);
+
+ for (fifo = 1; fifo < RX_FIFO_MAX_SIZE; fifo++) {
+ dw_writel(dw_spi, DW_SPI_RXFTLR, fifo);
+ if (fifo != dw_readl(dw_spi, DW_SPI_RXFTLR))
+ break;
+ }
+ dw_writel(dw_spi, DW_SPI_RXFTLR, 0);
+ dw_spi->rx_fifo_len = (fifo == 1) ? 0 : fifo;
+ dev_dbg(dw_spi->dev, "Detected RX FIFO size: %u bytes\n",
+ dw_spi->tx_fifo_len);
+ dw_spi_enable_chip(dw_spi, 1);
+}
+
+static int dw_spi_probe(struct device *dev)
+{
+ struct dw_spi_nor *dw_spi;
+ struct resource *iores;
+ struct device_node *np = dev->of_node;
+ struct dw_spi_flash_pdata *f_pdata = NULL;
+ int ret;
+
+ dw_spi = kzalloc(sizeof(*dw_spi), GFP_KERNEL);
+ if (!dw_spi)
+ return -ENOMEM;
+
+ dw_spi->dev = dev;
+ dev->priv = dw_spi;
+
+ dw_spi->clk = clk_get(dev, NULL);
+ if (IS_ERR(dw_spi->clk)) {
+ dev_err(dev, "unable to get spi clk\n");
+ ret = PTR_ERR(dw_spi->clk);
+ goto probe_failed;
+ }
+
+ dw_spi->master_ref_clk_hz = clk_get_rate(dw_spi->clk);
+ if (dw_spi->master_ref_clk_hz == 0) {
+ dev_err(dev, "unable to get spi clk rate\n");
+ ret = PTR_ERR(dw_spi->clk);
+ goto probe_failed;
+ }
+
+ clk_enable(dw_spi->clk);
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "dev_request_mem_region failed\n");
+ ret = PTR_ERR(iores);
+ goto probe_failed;
+ }
+ dw_spi->regs = IOMEM(iores->start);
+
+ dw_spi_hw_init(dw_spi);
+
+ dw_spi_detect_hw_params(dw_spi);
+
+ /* Get clock stretching mode support from device-tree */
+ dw_spi->clk_strech_en = of_property_read_bool(dev->of_node,
+ "clock-stretching");
+ dev_dbg(dev, "clock stretching %s supported\n",
+ dw_spi->clk_strech_en ? "is" : "is not");
+
+ /* Get flash device data */
+ for_each_available_child_of_node(dev->of_node, np) {
+ unsigned int cs;
+
+ if (of_property_read_u32(np, "reg", &cs)) {
+ dev_err(dev, "couldn't determine chip select\n");
+ ret = -ENXIO;
+ goto probe_failed;
+ }
+ if (cs > dw_spi->supported_cs) {
+ dev_err(dev, "chip select %d out of range (%d supported)\n",
+ cs, dw_spi->supported_cs);
+ ret = -ENXIO;
+ goto probe_failed;
+ }
+ f_pdata = &dw_spi->f_pdata[cs];
+ f_pdata->cs = cs;
+
+ ret = dw_spi_setup_flash(dev, f_pdata, np);
+ if (ret)
+ goto probe_failed;
+ }
+
+ dev_info(dev, "Synopsys Octal SPI NOR flash driver\n");
+ return 0;
+
+probe_failed:
+ dev_err(dev, "probe failed: %d\n", ret);
+ return ret;
+}
+
+static __maybe_unused struct of_device_id dw_spi_dt_ids[] = {
+ { .compatible = "snps,ospi-nor", },
+ { /* sentinel */ }
+};
+
+static struct driver dw_spi_driver = {
+ .name = "dw_ospi_nor",
+ .probe = dw_spi_probe,
+ .of_compatible = DRV_OF_COMPAT(dw_spi_dt_ids),
+};
+device_platform_driver(dw_spi_driver);
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 0ed7fd3d04..b9b2c7f6e5 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -19,10 +19,8 @@
#include <linux/mtd/cfi.h>
#include <linux/mtd/spi-nor.h>
#include <of.h>
-#include <spi/flash.h>
#define SPI_NOR_MAX_ID_LEN 6
-#define SPI_NOR_MAX_ADDR_WIDTH 4
/*
* For everything but full-chip erase; probably could be much smaller, but kept
@@ -84,6 +82,7 @@ struct flash_info {
#define USE_CLSR BIT(14) /* use CLSR command */
#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */
#define UNLOCK_GLOBAL_BLOCK BIT(16) /* Unlock global block protection */
+#define SPI_NOR_QUAD_WRITE BIT(17) /* Flash supports Quad Write */
};
enum spi_nor_read_command_index {
@@ -547,12 +546,12 @@ erase_err:
return ret;
}
-static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
return 0;
}
-static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
uint8_t status;
@@ -659,9 +658,6 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
- /* Fujitsu */
- { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
-
/* GigaDevice */
{ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32, SECT_4K) },
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
@@ -680,7 +676,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "is25lp016d", INFO(0x9d6015, 0, 64 * 1024, 32,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "is25lp01g", INFO(0x9d601b, 0, 64 * 1024, 2048,
- SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_QUAD_WRITE) },
{ "is25lp080d", INFO(0x9d6014, 0, 64 * 1024, 16,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "is25lp032", INFO(0x9d6016, 0, 64 * 1024, 64,
@@ -868,11 +864,15 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K) },
+ { "w25q512nwq", INFO(0xef6020, 0, 512 * 1024, 128, SECT_4K) },
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{ "w25q128", INFO(0xef7018, 0, 64 * 1024, 256, SECT_4K) },
- { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
+ { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K |
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_QUAD_WRITE | SPI_NOR_4B_OPCODES) },
+ { "w25q256jwm", INFO(0xef8019, 0, 64 * 1024, 512, SECT_4K |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
@@ -1167,6 +1167,13 @@ static int spi_nor_init_params(struct spi_nor *nor,
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+ if (info->flags & SPI_NOR_QUAD_WRITE) {
+ params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4;
+ spi_nor_set_pp_settings(
+ &params->page_programs[SNOR_CMD_PP_1_1_4],
+ SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4);
+ }
+
if (info->flags & UNLOCK_GLOBAL_BLOCK) {
int err;
@@ -1370,9 +1377,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
struct spi_nor_flash_parameter params;
const struct spi_device_id *id = NULL;
struct flash_info *info;
- struct device_d *dev = nor->dev;
+ struct device *dev = nor->dev;
struct mtd_info *mtd = nor->mtd;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret;
int i;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 59aa5a3c7f..fd64790fb8 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -319,7 +319,7 @@ struct ubi_eba_leb_desc {
* the moment or is damaged because of an unclean reboot.
*/
struct ubi_volume {
- struct device_d dev;
+ struct device dev;
struct cdev cdev;
struct ubi_device *ubi;
int vol_id;
@@ -516,7 +516,7 @@ struct ubi_debug_info {
*/
struct ubi_device {
struct cdev cdev;
- struct device_d dev;
+ struct device dev;
int ubi_num;
char ubi_name[sizeof(UBI_NAME_STR)+5];
int vol_count;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 27d0c4ec8b..13e9ff6924 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -107,6 +107,15 @@ config DRIVER_NET_DESIGNWARE_EQOS
This option enables support for the Synopsys
Designware Ethernet Quality-of-Service (GMAC4).
+config DRIVER_NET_DESIGNWARE_IMX8
+ bool "Designware EQOS i.MX Ethernet driver"
+ depends on HAS_DMA && COMMON_CLK && OFTREE && (ARCH_IMX8M || ARCH_IMX93 || COMPILE_TEST)
+ select DRIVER_NET_DESIGNWARE_EQOS
+ select MFD_SYSCON
+ help
+ This option enables support for the Designware EQOS MAC implemented on
+ the NXP i.MX SoCs.
+
config DRIVER_NET_DESIGNWARE_STM32
bool "STM32 Designware Ethernet driver"
depends on HAS_DMA && COMMON_CLK && OFTREE && (ARCH_STM32MP || COMPILE_TEST)
@@ -179,6 +188,15 @@ config DRIVER_NET_FEC_IMX
depends on HAS_DMA
select PHYLIB
+config DRIVER_NET_FSL_ENETC
+ bool "Freescale enetc ethernet driver"
+ select PHYLIB
+ depends on PCI
+ depends on HAS_DMA
+ help
+ This option enables support for the Freescale enetc core found
+ on Layerscape SoCs.
+
config DRIVER_NET_FSL_FMAN
bool "Freescale fman ethernet driver"
select PHYLIB
@@ -232,7 +250,6 @@ config DRIVER_NET_ORION
config DRIVER_NET_RTL8139
bool "RealTek RTL-8139 PCI Ethernet driver"
depends on PCI
- depends on MIPS
select PHYLIB
help
This is a driver for the Fast Ethernet PCI network cards based on
@@ -243,6 +260,7 @@ config DRIVER_NET_RTL8169
depends on PCI
depends on HAS_DMA
select PHYLIB
+ select REALTEK_PHY
help
This is a driver for the Fast Ethernet PCI network cards based on
the RTL 8169 chips.
@@ -267,7 +285,7 @@ config DRIVER_NET_TAP
config DRIVER_NET_EFI_SNP
bool "EFI SNP ethernet driver"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
config DRIVER_NET_VIRTIO
bool "virtio net driver"
@@ -283,22 +301,6 @@ config DRIVER_NET_AG71XX
help
This option enables support for Atheros AG71XX ethernet chip.
-config DRIVER_NET_TSE
- depends on NIOS2
- bool "Altera TSE ethernet driver"
- select PHYLIB
- help
- This option enables support for the Altera TSE MAC.
-
-config TSE_USE_DEDICATED_DESC_MEM
- depends on DRIVER_NET_TSE
- bool "Altera TSE uses dedicated descriptor memory"
- help
- This option tells the TSE driver to use an onchip memory
- to store SGDMA descriptors. Descriptor memory is not
- reserved with a malloc but directly mapped to the memory
- address (defined in config.h)
-
config DRIVER_NET_LITEETH
bool "LiteX ethernet driver"
select PHYLIB
@@ -316,9 +318,17 @@ menuconfig DSA
if DSA
+config DRIVER_NET_KSZ8873
+ bool "KSZ8873 switch driver"
+ help
+ This option enables support for the Microchip KSZ8873
+ switch chip.
+
config DRIVER_NET_KSZ9477
bool "KSZ9477 switch driver"
- depends on SPI
+ depends on SPI || I2C
+ select REGMAP_SPI if SPI
+ select REGMAP_I2C if I2C
help
This option enables support for the Microchip KSZ9477
switch chip.
@@ -339,6 +349,8 @@ config DRIVER_NET_SJA1105
- SJA1105R (Gen. 2, SGMII, No TT-Ethernet)
- SJA1105S (Gen. 2, SGMII, TT-Ethernet)
+source "drivers/net/realtek-dsa/Kconfig"
+
endif
endmenu
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a3eb10d1df..207345cfa3 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_DRIVER_NET_DESIGNWARE_GENERIC) += designware_generic.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STARFIVE) += designware_starfive.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) += designware_eqos.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_IMX8) += designware_imx.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_TEGRA186) += designware_tegra186.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_ROCKCHIP) += designware_rockchip.o
@@ -24,9 +25,11 @@ obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o
obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o
obj-$(CONFIG_DRIVER_NET_ETHOC) += ethoc.o
obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o
+obj-$(CONFIG_DRIVER_NET_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
obj-$(CONFIG_DRIVER_NET_FSL_FMAN) += fsl-fman.o
obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o
obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o
+obj-$(CONFIG_DRIVER_NET_KSZ8873) += ksz8873.o
obj-$(CONFIG_DRIVER_NET_KSZ9477) += ksz9477.o
obj-$(CONFIG_DRIVER_NET_MACB) += macb.o
obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o
@@ -34,13 +37,13 @@ obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o
obj-$(CONFIG_DRIVER_NET_MVNETA) += mvneta.o
obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o
obj-$(CONFIG_DRIVER_NET_RTL8139) += rtl8139.o
-obj-$(CONFIG_DRIVER_NET_RTL8169) += rtl8169.o
+obj-$(CONFIG_DRIVER_NET_RTL8169) += r8169_main.o r8169_firmware.o r8169_phy_config.o
obj-$(CONFIG_DRIVER_NET_SJA1105) += sja1105.o
obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o
obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o
obj-$(CONFIG_DRIVER_NET_TAP) += tap.o
-obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o
obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o
obj-$(CONFIG_DRIVER_NET_VIRTIO) += virtio.o
obj-$(CONFIG_DRIVER_NET_AG71XX) += ag71xx.o
obj-$(CONFIG_DRIVER_NET_LITEETH) += liteeth.o
+obj-$(CONFIG_DRIVER_NET_DSA_REALTEK) += realtek-dsa/
diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c
index 70aaa60f1a..cf00c2eb09 100644
--- a/drivers/net/ag71xx.c
+++ b/drivers/net/ag71xx.c
@@ -211,7 +211,7 @@ typedef struct {
#define MAX_WAIT 1000
struct ag71xx {
- struct device_d *dev;
+ struct device *dev;
struct eth_device netdev;
void __iomem *regs;
void __iomem *regs_gmac;
@@ -287,7 +287,7 @@ static inline void ag71xx_wr(struct ag71xx *priv, int reg, u32 val)
static int ag71xx_mii_wait(struct ag71xx *priv, int write)
{
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
uint64_t start;
start = get_time_ns();
@@ -371,7 +371,7 @@ static int ag71xx_ether_get_ethaddr(struct eth_device *edev, unsigned char *adr)
static void ag71xx_ether_halt(struct eth_device *edev)
{
struct ag71xx *priv = edev->priv;
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
uint64_t start;
ag71xx_wr(priv, AG71XX_REG_RX_CTRL, 0);
@@ -403,7 +403,7 @@ static int ag71xx_ether_rx(struct eth_device *edev)
rx_pkt = priv->rx_pkt[priv->next_rx];
/* invalidate */
- dma_sync_single_for_cpu((unsigned long)rx_pkt, pktlen,
+ dma_sync_single_for_cpu(priv->dev, (unsigned long)rx_pkt, pktlen,
DMA_FROM_DEVICE);
net_receive(edev, rx_pkt, pktlen - 4);
@@ -425,13 +425,13 @@ static int ag71xx_ether_rx(struct eth_device *edev)
static int ag71xx_ether_send(struct eth_device *edev, void *packet, int length)
{
struct ag71xx *priv = edev->priv;
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
ag7240_desc_t *f = &priv->fifo_tx[priv->next_tx];
uint64_t start;
int ret = 0;
/* flush */
- dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, (unsigned long)packet, length, DMA_TO_DEVICE);
f->pkt_start_addr = virt_to_phys(packet);
f->res1 = 0;
@@ -441,7 +441,7 @@ static int ag71xx_ether_send(struct eth_device *edev, void *packet, int length)
ag71xx_wr(priv, AG71XX_REG_TX_CTRL, TX_CTRL_TXE);
/* flush again?! */
- dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_sync_single_for_cpu(dev, (unsigned long)packet, length, DMA_TO_DEVICE);
start = get_time_ns();
while (!f->is_empty) {
@@ -491,7 +491,7 @@ static int ag71xx_ether_init(struct eth_device *edev)
fr->next_desc = virt_to_phys(&priv->fifo_rx[(i + 1) % NO_OF_RX_FIFOS]);
/* invalidate */
- dma_sync_single_for_device((unsigned long)rxbuf, MAX_RBUFF_SZ,
+ dma_sync_single_for_device(priv->dev, (unsigned long)rxbuf, MAX_RBUFF_SZ,
DMA_FROM_DEVICE);
rxbuf += MAX_RBUFF_SZ;
@@ -549,7 +549,7 @@ static struct ag71xx_cfg ag71xx_cfg_ar9344_gmac0 = {
.init_mii = ag71xx_ar9344_gmac0_mii_init,
};
-static int ag71xx_probe(struct device_d *dev)
+static int ag71xx_probe(struct device *dev)
{
void __iomem *regs, *regs_gmac;
struct mii_bus *miibus;
@@ -659,7 +659,7 @@ static int ag71xx_probe(struct device_d *dev)
return 0;
}
-static void ag71xx_remove(struct device_d *dev)
+static void ag71xx_remove(struct device *dev)
{
struct eth_device *edev = dev->priv;
@@ -671,8 +671,9 @@ static __maybe_unused struct of_device_id ag71xx_dt_ids[] = {
{ .compatible = "qca,ar9344-gmac0", .data = &ag71xx_cfg_ar9344_gmac0, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, ag71xx_dt_ids);
-static struct driver_d ag71xx_driver = {
+static struct driver ag71xx_driver = {
.name = "ag71xx-gmac",
.probe = ag71xx_probe,
.remove = ag71xx_remove,
diff --git a/drivers/net/altera_tse.c b/drivers/net/altera_tse.c
deleted file mode 100644
index f1dfe5952c..0000000000
--- a/drivers/net/altera_tse.c
+++ /dev/null
@@ -1,563 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Altera TSE Network driver
- *
- * Copyright (C) 2008 Altera Corporation.
- * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
- * Copyright (C) 2011 Franck JULLIEN, <elec4fun@gmail.com>
- */
-
-#include <common.h>
-#include <dma.h>
-#include <net.h>
-#include <init.h>
-#include <clock.h>
-#include <linux/mii.h>
-#include <linux/phy.h>
-#include <linux/err.h>
-
-#include <io.h>
-#include <asm/dma-mapping.h>
-
-#include "altera_tse.h"
-
-/* This is a generic routine that the SGDMA mode-specific routines
- * call to populate a descriptor.
- * arg1 :pointer to first SGDMA descriptor.
- * arg2 :pointer to next SGDMA descriptor.
- * arg3 :Address to where data to be written.
- * arg4 :Address from where data to be read.
- * arg5 :no of byte to transaction.
- * arg6 :variable indicating to generate start of packet or not
- * arg7 :read fixed
- * arg8 :write fixed
- * arg9 :read burst
- * arg10 :write burst
- * arg11 :atlantic_channel number
- */
-static void alt_sgdma_construct_descriptor_burst(
- struct alt_sgdma_descriptor *desc,
- struct alt_sgdma_descriptor *next,
- uint32_t *read_addr,
- uint32_t *write_addr,
- uint16_t length_or_eop,
- uint8_t generate_eop,
- uint8_t read_fixed,
- uint8_t write_fixed_or_sop,
- uint8_t read_burst,
- uint8_t write_burst,
- uint8_t atlantic_channel)
-{
- uint32_t temp;
-
- /*
- * Mark the "next" descriptor as "not" owned by hardware. This prevents
- * The SGDMA controller from continuing to process the chain. This is
- * done as a single IO write to bypass cache, without flushing
- * the entire descriptor, since only the 8-bit descriptor status must
- * be flushed.
- */
- if (!next)
- printf("Next descriptor not defined!!\n");
-
- temp = readb(&next->descriptor_control);
- writeb(temp & ~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK,
- &next->descriptor_control);
-
- writel((uint32_t)read_addr, &desc->source);
- writel((uint32_t)write_addr, &desc->destination);
- writel((uint32_t)next, &desc->next);
-
- writel(0, &desc->source_pad);
- writel(0, &desc->destination_pad);
- writel(0, &desc->next_pad);
- writew(length_or_eop, &desc->bytes_to_transfer);
- writew(0, &desc->actual_bytes_transferred);
- writeb(0, &desc->descriptor_status);
-
- /* SGDMA burst not currently supported */
- writeb(0, &desc->read_burst);
- writeb(0, &desc->write_burst);
-
- /*
- * Set the descriptor control block as follows:
- * - Set "owned by hardware" bit
- * - Optionally set "generate EOP" bit
- * - Optionally set the "read from fixed address" bit
- * - Optionally set the "write to fixed address bit (which serves
- * serves as a "generate SOP" control bit in memory-to-stream mode).
- * - Set the 4-bit atlantic channel, if specified
- *
- * Note this step is performed after all other descriptor information
- * has been filled out so that, if the controller already happens to be
- * pointing at this descriptor, it will not run (via the "owned by
- * hardware" bit) until all other descriptor has been set up.
- */
-
- writeb((ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK) |
- (generate_eop ? ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK : 0) |
- (read_fixed ? ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK : 0) |
- (write_fixed_or_sop ? ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK : 0) |
- (atlantic_channel ? ((atlantic_channel & 0x0F) << 3) : 0),
- &desc->descriptor_control);
-}
-
-static int alt_sgdma_do_sync_transfer(struct alt_sgdma_registers *dev,
- struct alt_sgdma_descriptor *desc)
-{
- uint32_t temp;
- uint64_t start;
- uint64_t tout;
-
- /* Wait for any pending transfers to complete */
- tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND;
-
- start = get_time_ns();
-
- while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) {
- if (is_timeout(start, tout)) {
- debug("Timeout waiting sgdma in do sync!\n");
- break;
- }
- }
-
- /*
- * Clear any (previous) status register information
- * that might occlude our error checking later.
- */
- writel(0xFF, &dev->status);
-
- /* Point the controller at the descriptor */
- writel((uint32_t)desc, &dev->next_descriptor_pointer);
- debug("next desc in sgdma 0x%x\n", (uint32_t)dev->next_descriptor_pointer);
-
- /*
- * Set up SGDMA controller to:
- * - Disable interrupt generation
- * - Run once a valid descriptor is written to controller
- * - Stop on an error with any particular descriptor
- */
- writel(ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK,
- &dev->control);
-
- /* Wait for the descriptor (chain) to complete */
- debug("wait for sgdma....");
- start = get_time_ns();
-
- while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) {
- if (is_timeout(start, tout)) {
- debug("Timeout waiting sgdma in do sync!\n");
- break;
- }
- }
-
- debug("done\n");
-
- /* Clear Run */
- temp = readl(&dev->control);
- writel(temp & ~ALT_SGDMA_CONTROL_RUN_MSK, &dev->control);
-
- /* Get & clear status register contents */
- debug("tx sgdma status = 0x%x", readl(&dev->status));
- writel(0xFF, &dev->status);
-
- return 0;
-}
-
-static int alt_sgdma_do_async_transfer(struct alt_sgdma_registers *dev,
- struct alt_sgdma_descriptor *desc)
-{
- uint64_t start;
- uint64_t tout;
-
- /* Wait for any pending transfers to complete */
- tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND;
-
- start = get_time_ns();
-
- while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) {
- if (is_timeout(start, tout)) {
- debug("Timeout waiting sgdma in do async!\n");
- break;
- }
- }
-
- /*
- * Clear any (previous) status register information
- * that might occlude our error checking later.
- */
- writel(0xFF, &dev->status);
-
- /* Point the controller at the descriptor */
- writel((uint32_t)desc, &dev->next_descriptor_pointer);
-
- /*
- * Set up SGDMA controller to:
- * - Disable interrupt generation
- * - Run once a valid descriptor is written to controller
- * - Stop on an error with any particular descriptor
- */
- writel(ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK,
- &dev->control);
-
- return 0;
-}
-
-static int tse_get_ethaddr(struct eth_device *edev, unsigned char *m)
-{
- struct altera_tse_priv *priv = edev->priv;
- struct alt_tse_mac *mac_dev = priv->tse_regs;
-
- m[5] = (readl(&mac_dev->mac_addr_1) >> 8) && 0xFF;
- m[4] = (readl(&mac_dev->mac_addr_1)) && 0xFF;
- m[3] = (readl(&mac_dev->mac_addr_0) >> 24) && 0xFF;
- m[2] = (readl(&mac_dev->mac_addr_0) >> 16) && 0xFF;
- m[1] = (readl(&mac_dev->mac_addr_0) >> 8) && 0xFF;
- m[0] = (readl(&mac_dev->mac_addr_0)) && 0xFF;
-
- return 0;
-}
-
-static int tse_set_ethaddr(struct eth_device *edev, const unsigned char *m)
-{
- struct altera_tse_priv *priv = edev->priv;
- struct alt_tse_mac *mac_dev = priv->tse_regs;
-
- debug("Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n",
- m[0], m[1], m[2], m[3], m[4], m[5]);
-
- writel(m[3] << 24 | m[2] << 16 | m[1] << 8 | m[0], &mac_dev->mac_addr_0);
- writel((m[5] << 8 | m[4]) & 0xFFFF, &mac_dev->mac_addr_1);
-
- return 0;
-}
-
-static int tse_phy_read(struct mii_bus *bus, int phy_addr, int reg)
-{
- struct altera_tse_priv *priv = bus->priv;
- struct alt_tse_mac *mac_dev = priv->tse_regs;
- uint32_t *mdio_regs;
-
- writel(phy_addr, &mac_dev->mdio_phy1_addr);
-
- mdio_regs = (uint32_t *)&mac_dev->mdio_phy1;
-
- return readl(&mdio_regs[reg]) & 0xFFFF;
-}
-
-static int tse_phy_write(struct mii_bus *bus, int phy_addr, int reg, u16 val)
-{
- struct altera_tse_priv *priv = bus->priv;
- struct alt_tse_mac *mac_dev = priv->tse_regs;
- uint32_t *mdio_regs;
-
- writel(phy_addr, &mac_dev->mdio_phy1_addr);
-
- mdio_regs = (uint32_t *)&mac_dev->mdio_phy1;
-
- writel((uint32_t)val, &mdio_regs[reg]);
-
- return 0;
-}
-
-static void tse_reset(struct eth_device *edev)
-{
- /* stop sgdmas, disable tse receive */
- struct altera_tse_priv *priv = edev->priv;
- struct alt_tse_mac *mac_dev = priv->tse_regs;
- struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx_regs;
- struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx_regs;
- struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
- struct alt_sgdma_descriptor *tx_desc = priv->tx_desc;
- uint64_t start;
- uint64_t tout;
-
- tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND;
-
- /* clear rx desc & wait for sgdma to complete */
- writeb(0, &rx_desc->descriptor_control);
- writel(0, &rx_sgdma->control);
-
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control);
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control);
- mdelay(100);
-
- start = get_time_ns();
-
- while (readl(&rx_sgdma->status) & ALT_SGDMA_STATUS_BUSY_MSK) {
- if (is_timeout(start, tout)) {
- printf("Timeout waiting for rx sgdma!\n");
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control);
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control);
- break;
- }
- }
-
- /* clear tx desc & wait for sgdma to complete */
- writeb(0, &tx_desc->descriptor_control);
- writel(0, &tx_sgdma->control);
-
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control);
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control);
- mdelay(100);
-
- start = get_time_ns();
-
- while (readl(&tx_sgdma->status) & ALT_SGDMA_STATUS_BUSY_MSK) {
- if (is_timeout(start, tout)) {
- printf("Timeout waiting for tx sgdma!\n");
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control);
- writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control);
- break;
- }
- }
-
- /* reset the mac */
- writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK |
- ALTERA_TSE_CMD_SW_RESET_MSK, &mac_dev->command_config);
-
- start = get_time_ns();
- tout = ALT_TSE_SW_RESET_WATCHDOG_TOUT * MSECOND;
-
- while (readl(&mac_dev->command_config) & ALTERA_TSE_CMD_SW_RESET_MSK) {
- if (is_timeout(start, tout)) {
- printf("TSEMAC SW reset bit never cleared!\n");
- break;
- }
- }
-}
-
-static int tse_eth_open(struct eth_device *edev)
-{
- struct altera_tse_priv *priv = edev->priv;
- int ret;
-
- ret = phy_device_connect(edev, priv->miibus, priv->phy_addr, NULL, 0,
- PHY_INTERFACE_MODE_NA);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int tse_eth_send(struct eth_device *edev, void *packet, int length)
-{
-
- struct altera_tse_priv *priv = edev->priv;
- struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx_regs;
- struct alt_sgdma_descriptor *tx_desc = priv->tx_desc;
- struct alt_sgdma_descriptor *tx_desc_cur = tx_desc;
-
- flush_dcache_range((uint32_t)packet, (uint32_t)packet + length);
- alt_sgdma_construct_descriptor_burst(
- (struct alt_sgdma_descriptor *)&tx_desc[0],
- (struct alt_sgdma_descriptor *)&tx_desc[1],
- (uint32_t *)packet, /* read addr */
- (uint32_t *)0, /* */
- length, /* length or EOP ,will change for each tx */
- 0x1, /* gen eop */
- 0x0, /* read fixed */
- 0x1, /* write fixed or sop */
- 0x0, /* read burst */
- 0x0, /* write burst */
- 0x0 /* channel */
- );
-
- alt_sgdma_do_sync_transfer(tx_sgdma, tx_desc_cur);
-
- return 0;
-}
-
-static void tse_eth_halt(struct eth_device *edev)
-{
- struct altera_tse_priv *priv = edev->priv;
- struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx_regs;
- struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx_regs;
-
- writel(0, &rx_sgdma->control); /* Stop the controller and reset settings */
- writel(0, &tx_sgdma->control); /* Stop the controller and reset settings */
-}
-
-static int tse_eth_rx(struct eth_device *edev)
-{
- uint16_t packet_length = 0;
-
- struct altera_tse_priv *priv = edev->priv;
- struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
- struct alt_sgdma_descriptor *rx_desc_cur = rx_desc;
- struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx_regs;
-
- if (rx_desc_cur->descriptor_status &
- ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) {
-
- packet_length = rx_desc->actual_bytes_transferred;
- net_receive(edev, NetRxPackets[0], packet_length);
-
- /* Clear Run */
- rx_sgdma->control = (rx_sgdma->control & (~ALT_SGDMA_CONTROL_RUN_MSK));
-
- /* start descriptor again */
- flush_dcache_range((uint32_t)(NetRxPackets[0]), (uint32_t)(NetRxPackets[0]) + PKTSIZE);
- alt_sgdma_construct_descriptor_burst(
- (struct alt_sgdma_descriptor *)&rx_desc[0],
- (struct alt_sgdma_descriptor *)&rx_desc[1],
- (uint32_t)0x0, /* read addr */
- (uint32_t *)NetRxPackets[0], /* */
- 0x0, /* length or EOP */
- 0x0, /* gen eop */
- 0x0, /* read fixed */
- 0x0, /* write fixed or sop */
- 0x0, /* read burst */
- 0x0, /* write burst */
- 0x0 /* channel */
- );
-
- /* setup the sgdma */
- alt_sgdma_do_async_transfer(priv->sgdma_rx_regs, rx_desc);
- }
-
- return 0;
-}
-
-static int tse_init_dev(struct eth_device *edev)
-{
- struct altera_tse_priv *priv = edev->priv;
- struct alt_tse_mac *mac_dev = priv->tse_regs;
- struct alt_sgdma_descriptor *tx_desc = priv->tx_desc;
- struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
- struct alt_sgdma_descriptor *rx_desc_cur;
-
- rx_desc_cur = rx_desc;
-
- tse_reset(edev);
-
- /* need to create sgdma */
- alt_sgdma_construct_descriptor_burst(
- (struct alt_sgdma_descriptor *)&tx_desc[0],
- (struct alt_sgdma_descriptor *)&tx_desc[1],
- (uint32_t *)NULL, /* read addr */
- (uint32_t *)0, /* */
- 0, /* length or EOP ,will change for each tx */
- 0x1, /* gen eop */
- 0x0, /* read fixed */
- 0x1, /* write fixed or sop */
- 0x0, /* read burst */
- 0x0, /* write burst */
- 0x0 /* channel */
- );
-
- flush_dcache_range((uint32_t)(NetRxPackets[0]), (uint32_t)(NetRxPackets[0]) + PKTSIZE);
- alt_sgdma_construct_descriptor_burst(
- (struct alt_sgdma_descriptor *)&rx_desc[0],
- (struct alt_sgdma_descriptor *)&rx_desc[1],
- (uint32_t)0x0, /* read addr */
- (uint32_t *)NetRxPackets[0], /* */
- 0x0, /* length or EOP */
- 0x0, /* gen eop */
- 0x0, /* read fixed */
- 0x0, /* write fixed or sop */
- 0x0, /* read burst */
- 0x0, /* write burst */
- 0x0 /* channel */
- );
-
- /* start rx async transfer */
- alt_sgdma_do_async_transfer(priv->sgdma_rx_regs, rx_desc_cur);
-
- /* Initialize MAC registers */
- writel(PKTSIZE, &mac_dev->max_frame_length);
-
- /* NO Shift */
- writel(0, &mac_dev->rx_cmd_stat);
- writel(0, &mac_dev->tx_cmd_stat);
-
- /* enable MAC */
- writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK, &mac_dev->command_config);
-
- return 0;
-}
-
-static int tse_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct altera_tse_priv *priv;
- struct mii_bus *miibus;
- struct eth_device *edev;
- struct alt_sgdma_descriptor *rx_desc;
- struct alt_sgdma_descriptor *tx_desc;
-#ifndef CONFIG_TSE_USE_DEDICATED_DESC_MEM
- uint32_t dma_handle;
-#endif
- edev = xzalloc(sizeof(struct eth_device));
- priv = xzalloc(sizeof(struct altera_tse_priv));
- miibus = xzalloc(sizeof(struct mii_bus));
-
- edev->priv = priv;
-
- edev->init = tse_init_dev;
- edev->open = tse_eth_open;
- edev->send = tse_eth_send;
- edev->recv = tse_eth_rx;
- edev->halt = tse_eth_halt;
- edev->get_ethaddr = tse_get_ethaddr;
- edev->set_ethaddr = tse_set_ethaddr;
- edev->parent = dev;
-
-#ifdef CONFIG_TSE_USE_DEDICATED_DESC_MEM
- iores = dev_request_mem_resource(dev, 3);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- tx_desc = IOMEM(iores->start);
- rx_desc = tx_desc + 2;
-#else
- tx_desc = dma_alloc_coherent(sizeof(*tx_desc) * (3 + PKTBUFSRX), (dma_addr_t *)&dma_handle);
- rx_desc = tx_desc + 2;
-
- if (!tx_desc) {
- free(edev);
- free(miibus);
- return 0;
- }
-#endif
-
- memset(rx_desc, 0, (sizeof *rx_desc) * (PKTBUFSRX + 1));
- memset(tx_desc, 0, (sizeof *tx_desc) * 2);
-
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->tse_regs = IOMEM(iores->start);
- iores = dev_request_mem_resource(dev, 1);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->sgdma_rx_regs = IOMEM(iores->start);
-
- iores = dev_request_mem_resource(dev, 2);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->sgdma_tx_regs = IOMEM(iores->start);
- priv->rx_desc = rx_desc;
- priv->tx_desc = tx_desc;
-
- priv->miibus = miibus;
-
- miibus->read = tse_phy_read;
- miibus->write = tse_phy_write;
- miibus->priv = priv;
- miibus->parent = dev;
-
- if (dev->platform_data != NULL)
- priv->phy_addr = *((int8_t *)(dev->platform_data));
- else
- priv->phy_addr = -1;
-
- mdiobus_register(miibus);
-
- return eth_register(edev);
-}
-
-static struct driver_d altera_tse_driver = {
- .name = "altera_tse",
- .probe = tse_probe,
-};
-device_platform_driver(altera_tse_driver);
diff --git a/drivers/net/altera_tse.h b/drivers/net/altera_tse.h
deleted file mode 100644
index 7bff14de81..0000000000
--- a/drivers/net/altera_tse.h
+++ /dev/null
@@ -1,296 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Altera 10/100/1000 triple speed ethernet mac
- *
- * Copyright (C) 2008 Altera Corporation.
- * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
- * Copyright (C) 2011 Franck JULLIEN <elec4fun@gmail.com>
- */
-#ifndef _ALTERA_TSE_H_
-#define _ALTERA_TSE_H_
-
-/* SGDMA Stuff */
-#define ALT_SGDMA_STATUS_ERROR_MSK (0x00000001)
-#define ALT_SGDMA_STATUS_EOP_ENCOUNTERED_MSK (0x00000002)
-#define ALT_SGDMA_STATUS_DESC_COMPLETED_MSK (0x00000004)
-#define ALT_SGDMA_STATUS_CHAIN_COMPLETED_MSK (0x00000008)
-#define ALT_SGDMA_STATUS_BUSY_MSK (0x00000010)
-
-#define ALT_SGDMA_CONTROL_IE_ERROR_MSK (0x00000001)
-#define ALT_SGDMA_CONTROL_IE_EOP_ENCOUNTERED_MSK (0x00000002)
-#define ALT_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK (0x00000004)
-#define ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK (0x00000008)
-#define ALT_SGDMA_CONTROL_IE_GLOBAL_MSK (0x00000010)
-#define ALT_SGDMA_CONTROL_RUN_MSK (0x00000020)
-#define ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK (0x00000040)
-#define ALT_SGDMA_CONTROL_IE_MAX_DESC_PROCESSED_MSK (0x00000080)
-#define ALT_SGDMA_CONTROL_MAX_DESC_PROCESSED_MSK (0x0000FF00)
-#define ALT_SGDMA_CONTROL_SOFTWARERESET_MSK (0x00010000)
-#define ALT_SGDMA_CONTROL_PARK_MSK (0x00020000)
-#define ALT_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK (0x80000000)
-
-#define ALTERA_TSE_SGDMA_INTR_MASK (ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK \
- | ALT_SGDMA_STATUS_DESC_COMPLETED_MSK \
- | ALT_SGDMA_CONTROL_IE_GLOBAL_MSK)
-
-/*
- * Descriptor control bit masks & offsets
- *
- * Note: The control byte physically occupies bits [31:24] in memory.
- * The following bit-offsets are expressed relative to the LSB of
- * the control register bitfield.
- */
-#define ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK (0x00000001)
-#define ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK (0x00000002)
-#define ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK (0x00000004)
-#define ALT_SGDMA_DESCRIPTOR_CONTROL_ATLANTIC_CHANNEL_MSK (0x00000008)
-#define ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK (0x00000080)
-
-/*
- * Descriptor status bit masks & offsets
- *
- * Note: The status byte physically occupies bits [23:16] in memory.
- * The following bit-offsets are expressed relative to the LSB of
- * the status register bitfield.
- */
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK (0x00000001)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK (0x00000002)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK (0x00000004)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK (0x00000008)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK (0x00000010)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK (0x00000020)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK (0x00000040)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK (0x00000080)
-#define ALT_SGDMA_DESCRIPTOR_STATUS_ERROR_MSK (0x0000007F)
-
-/*
- * The SGDMA controller buffer descriptor allocates
- * 64 bits for each address. To support ANSI C, the
- * struct implementing a descriptor places 32-bits
- * of padding directly above each address; each pad must
- * be cleared when initializing a descriptor.
- */
-
-/*
- * Buffer Descriptor data structure
- *
- */
-struct alt_sgdma_descriptor {
- unsigned int *source; /* the address of data to be read. */
- unsigned int source_pad;
-
- unsigned int *destination; /* the address to write data */
- unsigned int destination_pad;
-
- unsigned int *next; /* the next descriptor in the list. */
- unsigned int next_pad;
-
- unsigned short bytes_to_transfer; /* the number of bytes to transfer */
- unsigned char read_burst;
- unsigned char write_burst;
-
- unsigned short actual_bytes_transferred;/* bytes transferred by DMA */
- unsigned char descriptor_status;
- unsigned char descriptor_control;
-
-} __attribute__ ((packed, aligned(1)));
-
-/* SG-DMA Control/Status Slave registers map */
-
-struct alt_sgdma_registers {
- unsigned int status;
- unsigned int status_pad[3];
- unsigned int control;
- unsigned int control_pad[3];
- unsigned int next_descriptor_pointer;
- unsigned int descriptor_pad[3];
-};
-
-/* TSE Stuff */
-#define ALTERA_TSE_CMD_TX_ENA_MSK (0x00000001)
-#define ALTERA_TSE_CMD_RX_ENA_MSK (0x00000002)
-#define ALTERA_TSE_CMD_XON_GEN_MSK (0x00000004)
-#define ALTERA_TSE_CMD_ETH_SPEED_MSK (0x00000008)
-#define ALTERA_TSE_CMD_PROMIS_EN_MSK (0x00000010)
-#define ALTERA_TSE_CMD_PAD_EN_MSK (0x00000020)
-#define ALTERA_TSE_CMD_CRC_FWD_MSK (0x00000040)
-#define ALTERA_TSE_CMD_PAUSE_FWD_MSK (0x00000080)
-#define ALTERA_TSE_CMD_PAUSE_IGNORE_MSK (0x00000100)
-#define ALTERA_TSE_CMD_TX_ADDR_INS_MSK (0x00000200)
-#define ALTERA_TSE_CMD_HD_ENA_MSK (0x00000400)
-#define ALTERA_TSE_CMD_EXCESS_COL_MSK (0x00000800)
-#define ALTERA_TSE_CMD_LATE_COL_MSK (0x00001000)
-#define ALTERA_TSE_CMD_SW_RESET_MSK (0x00002000)
-#define ALTERA_TSE_CMD_MHASH_SEL_MSK (0x00004000)
-#define ALTERA_TSE_CMD_LOOPBACK_MSK (0x00008000)
-/* Bits (18:16) = address select */
-#define ALTERA_TSE_CMD_TX_ADDR_SEL_MSK (0x00070000)
-#define ALTERA_TSE_CMD_MAGIC_ENA_MSK (0x00080000)
-#define ALTERA_TSE_CMD_SLEEP_MSK (0x00100000)
-#define ALTERA_TSE_CMD_WAKEUP_MSK (0x00200000)
-#define ALTERA_TSE_CMD_XOFF_GEN_MSK (0x00400000)
-#define ALTERA_TSE_CMD_CNTL_FRM_ENA_MSK (0x00800000)
-#define ALTERA_TSE_CMD_NO_LENGTH_CHECK_MSK (0x01000000)
-#define ALTERA_TSE_CMD_ENA_10_MSK (0x02000000)
-#define ALTERA_TSE_CMD_RX_ERR_DISC_MSK (0x04000000)
-/* Bits (30..27) reserved */
-#define ALTERA_TSE_CMD_CNT_RESET_MSK (0x80000000)
-
-#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 (0x00040000)
-#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC (0x00020000)
-
-#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 (0x02000000)
-
-#define ALT_TSE_SW_RESET_WATCHDOG_CNTR 10000
-#define ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR 90000000
-
-#define ALT_TSE_SW_RESET_WATCHDOG_TOUT 1 /* ms */
-#define ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT 5 /* ms */
-
-struct alt_tse_mdio {
- unsigned int control; /*PHY device operation control register */
- unsigned int status; /*PHY device operation status register */
- unsigned int phy_id1; /*Bits 31:16 of PHY identifier. */
- unsigned int phy_id2; /*Bits 15:0 of PHY identifier. */
- unsigned int auto_negotiation_advertisement;
- unsigned int remote_partner_base_page_ability;
-
- unsigned int reg6;
- unsigned int reg7;
- unsigned int reg8;
- unsigned int reg9;
- unsigned int rega;
- unsigned int regb;
- unsigned int regc;
- unsigned int regd;
- unsigned int rege;
- unsigned int regf;
- unsigned int reg10;
- unsigned int reg11;
- unsigned int reg12;
- unsigned int reg13;
- unsigned int reg14;
- unsigned int reg15;
- unsigned int reg16;
- unsigned int reg17;
- unsigned int reg18;
- unsigned int reg19;
- unsigned int reg1a;
- unsigned int reg1b;
- unsigned int reg1c;
- unsigned int reg1d;
- unsigned int reg1e;
- unsigned int reg1f;
-};
-
-/* MAC register Space */
-
-struct alt_tse_mac {
- unsigned int megacore_revision;
- unsigned int scratch_pad;
- unsigned int command_config;
- unsigned int mac_addr_0;
- unsigned int mac_addr_1;
- unsigned int max_frame_length;
- unsigned int pause_quanta;
- unsigned int rx_sel_empty_threshold;
- unsigned int rx_sel_full_threshold;
- unsigned int tx_sel_empty_threshold;
- unsigned int tx_sel_full_threshold;
- unsigned int rx_almost_empty_threshold;
- unsigned int rx_almost_full_threshold;
- unsigned int tx_almost_empty_threshold;
- unsigned int tx_almost_full_threshold;
- unsigned int mdio_phy0_addr;
- unsigned int mdio_phy1_addr;
-
- /* only if 100/1000 BaseX PCS, reserved otherwise */
- unsigned int reservedx44[5];
-
- unsigned int reg_read_access_status;
- unsigned int min_tx_ipg_length;
-
- /* IEEE 802.3 oEntity Managed Object Support */
- unsigned int aMACID_1; /*The MAC addresses */
- unsigned int aMACID_2;
- unsigned int aFramesTransmittedOK;
- unsigned int aFramesReceivedOK;
- unsigned int aFramesCheckSequenceErrors;
- unsigned int aAlignmentErrors;
- unsigned int aOctetsTransmittedOK;
- unsigned int aOctetsReceivedOK;
-
- /* IEEE 802.3 oPausedEntity Managed Object Support */
- unsigned int aTxPAUSEMACCtrlFrames;
- unsigned int aRxPAUSEMACCtrlFrames;
-
- /* IETF MIB (MIB-II) Object Support */
- unsigned int ifInErrors;
- unsigned int ifOutErrors;
- unsigned int ifInUcastPkts;
- unsigned int ifInMulticastPkts;
- unsigned int ifInBroadcastPkts;
- unsigned int ifOutDiscards;
- unsigned int ifOutUcastPkts;
- unsigned int ifOutMulticastPkts;
- unsigned int ifOutBroadcastPkts;
-
- /* IETF RMON MIB Object Support */
- unsigned int etherStatsDropEvent;
- unsigned int etherStatsOctets;
- unsigned int etherStatsPkts;
- unsigned int etherStatsUndersizePkts;
- unsigned int etherStatsOversizePkts;
- unsigned int etherStatsPkts64Octets;
- unsigned int etherStatsPkts65to127Octets;
- unsigned int etherStatsPkts128to255Octets;
- unsigned int etherStatsPkts256to511Octets;
- unsigned int etherStatsPkts512to1023Octets;
- unsigned int etherStatsPkts1024to1518Octets;
-
- unsigned int etherStatsPkts1519toXOctets;
- unsigned int etherStatsJabbers;
- unsigned int etherStatsFragments;
-
- unsigned int reservedxE4;
-
- /*FIFO control register. */
- unsigned int tx_cmd_stat;
- unsigned int rx_cmd_stat;
-
- unsigned int ipaccTxConf;
- unsigned int ipaccRxConf;
- unsigned int ipaccRxStat;
- unsigned int ipaccRxStatSum;
-
- /*Multicast address resolution table */
- unsigned int hash_table[64];
-
- /*Registers 0 to 31 within PHY device 0/1 */
- struct alt_tse_mdio mdio_phy0;
- struct alt_tse_mdio mdio_phy1;
-
- /*4 Supplemental MAC Addresses */
- unsigned int supp_mac_addr_0_0;
- unsigned int supp_mac_addr_0_1;
- unsigned int supp_mac_addr_1_0;
- unsigned int supp_mac_addr_1_1;
- unsigned int supp_mac_addr_2_0;
- unsigned int supp_mac_addr_2_1;
- unsigned int supp_mac_addr_3_0;
- unsigned int supp_mac_addr_3_1;
-
- unsigned int reservedx320[56];
-};
-
-struct altera_tse_priv {
- void __iomem *tse_regs;
- void __iomem *sgdma_rx_regs;
- void __iomem *sgdma_tx_regs;
- void __iomem *rx_desc;
- void __iomem *tx_desc;
- int phy_addr;
- struct mii_bus *miibus;
-};
-
-#endif /* _ALTERA_TSE_H_ */
diff --git a/drivers/net/ar231x.c b/drivers/net/ar231x.c
index 6d1a90684c..1af34a3117 100644
--- a/drivers/net/ar231x.c
+++ b/drivers/net/ar231x.c
@@ -357,7 +357,7 @@ static int ar231x_mdiibus_reset(struct mii_bus *bus)
return 0;
}
-static int ar231x_eth_probe(struct device_d *dev)
+static int ar231x_eth_probe(struct device *dev)
{
struct resource *iores;
struct ar231x_eth_priv *priv;
@@ -419,7 +419,7 @@ static int ar231x_eth_probe(struct device_d *dev)
return 0;
}
-static struct driver_d ar231x_eth_driver = {
+static struct driver ar231x_eth_driver = {
.name = "ar231x_eth",
.probe = ar231x_eth_probe,
};
diff --git a/drivers/net/arc_emac.c b/drivers/net/arc_emac.c
index 28ec5e31fb..fa6e3955db 100644
--- a/drivers/net/arc_emac.c
+++ b/drivers/net/arc_emac.c
@@ -189,7 +189,7 @@ static int arc_emac_open(struct eth_device *edev)
rxbd->data = cpu_to_le32(rxbuf);
/* Return ownership to EMAC */
- dma_sync_single_for_device((unsigned long)rxbuf, PKTSIZE,
+ dma_sync_single_for_device(edev->parent, (unsigned long)rxbuf, PKTSIZE,
DMA_FROM_DEVICE);
rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE);
@@ -240,7 +240,7 @@ static int arc_emac_send(struct eth_device *edev, void *data, int length)
length = EMAC_ZLEN;
}
- dma_sync_single_for_device((unsigned long)data, length, DMA_TO_DEVICE);
+ dma_sync_single_for_device(edev->parent, (unsigned long)data, length, DMA_TO_DEVICE);
bd->data = cpu_to_le32(data);
bd->info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | length);
@@ -249,7 +249,7 @@ static int arc_emac_send(struct eth_device *edev, void *data, int length)
ret = wait_on_timeout(20 * MSECOND,
(arc_reg_get(priv, R_STATUS) & TXINT_MASK) != 0);
- dma_sync_single_for_cpu((unsigned long)data, length, DMA_TO_DEVICE);
+ dma_sync_single_for_cpu(edev->parent, (unsigned long)data, length, DMA_TO_DEVICE);
if (ret) {
dev_err(&edev->dev, "transmit timeout\n");
@@ -297,12 +297,12 @@ static int arc_emac_recv(struct eth_device *edev)
pktlen = info & LEN_MASK;
- dma_sync_single_for_cpu((unsigned long)rxbd->data, pktlen,
+ dma_sync_single_for_cpu(edev->parent, (unsigned long)rxbd->data, pktlen,
DMA_FROM_DEVICE);
net_receive(edev, (unsigned char *)rxbd->data, pktlen);
- dma_sync_single_for_device((unsigned long)rxbd->data, pktlen,
+ dma_sync_single_for_device(edev->parent, (unsigned long)rxbd->data, pktlen,
DMA_FROM_DEVICE);
rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE);
@@ -381,7 +381,7 @@ static int arc_emac_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
#define DEFAULT_EMAC_CLOCK_FREQUENCY 50000000UL;
-static int arc_emac_probe(struct device_d *dev)
+static int arc_emac_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
@@ -464,8 +464,9 @@ static __maybe_unused struct of_device_id arc_emac_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, arc_emac_dt_ids);
-static struct driver_d arc_emac_driver = {
+static struct driver arc_emac_driver = {
.name = "arc-emac",
.probe = arc_emac_probe,
.of_compatible = DRV_OF_COMPAT(arc_emac_dt_ids),
diff --git a/drivers/net/at91_ether.c b/drivers/net/at91_ether.c
index e69a300a6b..0959a3c503 100644
--- a/drivers/net/at91_ether.c
+++ b/drivers/net/at91_ether.c
@@ -15,10 +15,10 @@
#include <xfuncs.h>
#include <init.h>
#include <asm/io.h>
-#include <mach/hardware.h>
-#include <mach/at91rm9200_emac.h>
-#include <mach/board.h>
-#include <generated/mach-types.h>
+#include <mach/at91/hardware.h>
+#include <mach/at91/at91rm9200_emac.h>
+#include <mach/at91/board.h>
+#include <asm/mach-types.h>
#include <linux/clk.h>
#include <linux/mii.h>
#include <errno.h>
@@ -186,7 +186,8 @@ static int at91_ether_send(struct eth_device *edev, void *packet, int length)
{
while (!(at91_emac_read(AT91_EMAC_TSR) & AT91_EMAC_TSR_BNQ));
- dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_sync_single_for_device(edev->parent, (unsigned long)packet,
+ length, DMA_TO_DEVICE);
/* Set address of the data in the Transmit Address register */
at91_emac_write(AT91_EMAC_TAR, (unsigned long) packet);
@@ -198,7 +199,8 @@ static int at91_ether_send(struct eth_device *edev, void *packet, int length)
at91_emac_write(AT91_EMAC_TSR,
at91_emac_read(AT91_EMAC_TSR) | AT91_EMAC_TSR_COMP);
- dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_sync_single_for_cpu(edev->parent, (unsigned long)packet,
+ length, DMA_TO_DEVICE);
return 0;
}
@@ -214,10 +216,10 @@ static int at91_ether_rx(struct eth_device *edev)
size = rbfp->size & RBF_SIZE;
- dma_sync_single_for_cpu((unsigned long)rbfp->addr, size,
+ dma_sync_single_for_cpu(edev->parent, (unsigned long)rbfp->addr, size,
DMA_FROM_DEVICE);
net_receive(edev, (unsigned char *)(rbfp->addr & RBF_ADDR), size);
- dma_sync_single_for_device((unsigned long)rbfp->addr, size,
+ dma_sync_single_for_device(edev->parent, (unsigned long)rbfp->addr, size,
DMA_FROM_DEVICE);
rbfp->addr &= ~RBF_OWNER;
@@ -284,7 +286,7 @@ static int at91_ether_init(struct eth_device *edev)
return 0;
}
-static int at91_ether_probe(struct device_d *dev)
+static int at91_ether_probe(struct device *dev)
{
unsigned int mac_cfg;
struct ether_device *ether_dev;
@@ -307,6 +309,7 @@ static int at91_ether_probe(struct device_d *dev)
miibus = &ether_dev->miibus;
edev->priv = ether_dev;
+ edev->parent = dev;
edev->init = at91_ether_init;
edev->open = at91_ether_open;
edev->send = at91_ether_send;
@@ -353,7 +356,7 @@ static int at91_ether_probe(struct device_d *dev)
return 0;
}
-static struct driver_d at91_ether_driver = {
+static struct driver at91_ether_driver = {
.name = "at91_ether",
.probe = at91_ether_probe,
};
diff --git a/drivers/net/bcmgenet.c b/drivers/net/bcmgenet.c
index 73c1590518..acbb973a92 100644
--- a/drivers/net/bcmgenet.c
+++ b/drivers/net/bcmgenet.c
@@ -174,7 +174,7 @@ struct bcmgenet_eth_priv {
u32 interface;
struct mii_bus miibus;
struct eth_device edev;
- struct device_d *dev;
+ struct device *dev;
unsigned char addr[6];
};
@@ -215,15 +215,6 @@ static void bcmgenet_umac_reset(struct bcmgenet_eth_priv *priv)
writel(1, (priv->mac_reg + RBUF_TBUF_SIZE_CTRL));
}
-static int bcmgenet_set_hwaddr(struct eth_device *dev, const unsigned char *addr)
-{
- struct bcmgenet_eth_priv *priv = dev->priv;
-
- memcpy(priv->addr, addr, 6);
-
- return 0;
-}
-
static int __bcmgenet_set_hwaddr(struct bcmgenet_eth_priv *priv)
{
const unsigned char *addr = priv->addr;
@@ -238,6 +229,17 @@ static int __bcmgenet_set_hwaddr(struct bcmgenet_eth_priv *priv)
return 0;
}
+static int bcmgenet_set_hwaddr(struct eth_device *dev, const unsigned char *addr)
+{
+ struct bcmgenet_eth_priv *priv = dev->priv;
+
+ memcpy(priv->addr, addr, 6);
+
+ __bcmgenet_set_hwaddr(priv);
+
+ return 0;
+}
+
static int bcmgenet_get_hwaddr(struct eth_device *edev, unsigned char *mac)
{
return -1;
@@ -312,8 +314,8 @@ static int bcmgenet_gmac_eth_recv(struct eth_device *edev)
struct bcmgenet_eth_priv *priv = edev->priv;
void *desc_base = priv->mac_reg + GENET_RX_OFF + priv->rx_index * DMA_DESC_SIZE;
u32 prod_index = readl(priv->mac_reg + RDMA_PROD_INDEX);
- u32 length;
- unsigned long addr_lo, addr_hi, addr;
+ u32 length, addr_lo, addr_hi;
+ dma_addr_t addr;
if (prod_index == priv->c_index)
return -EAGAIN;
@@ -322,9 +324,9 @@ static int bcmgenet_gmac_eth_recv(struct eth_device *edev)
length = (length >> DMA_BUFLENGTH_SHIFT) & DMA_BUFLENGTH_MASK;
addr_lo = readl(desc_base + DMA_DESC_ADDRESS_LO);
addr_hi = readl(desc_base + DMA_DESC_ADDRESS_HI);
- addr = addr_hi << 32 | addr_lo;
+ addr = (u64)addr_hi << 32 | addr_lo;
- dma_sync_single_for_cpu(addr, length, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(priv->dev, addr, length, DMA_FROM_DEVICE);
/* To cater for the IP header alignment the hardware does.
* This would actually not be needed if we don't program
@@ -332,7 +334,7 @@ static int bcmgenet_gmac_eth_recv(struct eth_device *edev)
*/
net_receive(edev, (void *)addr + RX_BUF_OFFSET, length - RX_BUF_OFFSET);
- dma_sync_single_for_device(addr, length, DMA_FROM_DEVICE);
+ dma_sync_single_for_device(priv->dev, addr, length, DMA_FROM_DEVICE);
/* Tell the MAC we have consumed that last receive buffer. */
priv->c_index = (priv->c_index + 1) & 0xffff;
@@ -354,12 +356,13 @@ static void rx_descs_init(struct bcmgenet_eth_priv *priv)
len_stat = (RX_BUF_LENGTH << DMA_BUFLENGTH_SHIFT) | DMA_OWN;
for (i = 0; i < RX_DESCS; i++) {
- writel(lower_32_bits((uintptr_t)&rxbuffs[i * RX_BUF_LENGTH]),
- desc_base + i * DMA_DESC_SIZE + DMA_DESC_ADDRESS_LO);
- writel(upper_32_bits((uintptr_t)&rxbuffs[i * RX_BUF_LENGTH]),
- desc_base + i * DMA_DESC_SIZE + DMA_DESC_ADDRESS_HI);
- writel(len_stat,
- desc_base + i * DMA_DESC_SIZE + DMA_DESC_LENGTH_STATUS);
+ dma_addr_t dma_addr = dma_map_single(priv->dev, &rxbuffs[i * RX_BUF_LENGTH],
+ RX_BUF_LENGTH, DMA_FROM_DEVICE);
+ void *desc = desc_base + i * DMA_DESC_SIZE;
+
+ writel(lower_32_bits(dma_addr), desc + DMA_DESC_ADDRESS_LO);
+ writel(upper_32_bits(dma_addr), desc + DMA_DESC_ADDRESS_HI);
+ writel(len_stat, desc + DMA_DESC_LENGTH_STATUS);
}
}
@@ -516,7 +519,7 @@ static int bcmgenet_mdio_read(struct mii_bus *bus, int addr, int reg)
return val & 0xffff;
}
-static int bcmgenet_probe(struct device_d *dev)
+static int bcmgenet_probe(struct device *dev)
{
struct resource *iores;
struct bcmgenet_eth_priv *priv;
@@ -559,7 +562,7 @@ static int bcmgenet_probe(struct device_d *dev)
return -ENODEV;
}
- ret = of_get_phy_mode(dev->device_node);
+ ret = of_get_phy_mode(dev->of_node);
if (ret < 0)
priv->interface = PHY_INTERFACE_MODE_MII;
else
@@ -577,6 +580,8 @@ static int bcmgenet_probe(struct device_d *dev)
priv->miibus.priv = priv;
priv->miibus.parent = dev;
+ priv->miibus.dev.of_node
+ = of_get_compatible_child(dev->of_node, "brcm,genet-mdio-v5");
ret = mdiobus_register(&priv->miibus);
if (ret)
@@ -596,7 +601,7 @@ static void bcmgenet_gmac_eth_stop(struct bcmgenet_eth_priv *priv)
bcmgenet_disable_dma(priv);
}
-static void bcmgenet_remove(struct device_d *dev)
+static void bcmgenet_remove(struct device *dev)
{
struct bcmgenet_eth_priv *priv = dev->priv;
@@ -612,8 +617,9 @@ static struct of_device_id bcmgenet_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, bcmgenet_ids);
-static struct driver_d bcmgenet_driver = {
+static struct driver bcmgenet_driver = {
.name = "brcm-genet",
.probe = bcmgenet_probe,
.remove = bcmgenet_remove,
diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c
index 73671ae2d4..3aafebaad4 100644
--- a/drivers/net/cpsw.c
+++ b/drivers/net/cpsw.c
@@ -22,7 +22,7 @@
#include <asm/system.h>
#include <linux/err.h>
-#include <mach/cpsw.h>
+#include <mach/omap/cpsw.h>
#define CPSW_VERSION_1 0x19010a
#define CPSW_VERSION_2 0x19010c
@@ -51,10 +51,12 @@
#define CPDMA_DESC_EOP BIT(30)
#define CPDMA_DESC_OWNER BIT(29)
#define CPDMA_DESC_EOQ BIT(28)
+#define CPDMA_DESC_PASS_CRC BIT(26)
#define CPDMA_DESC_TO_PORT_EN BIT(20)
#define CPDMA_FROM_TO_PORT_SHIFT 16
#define CPDMA_RX_SOURCE_PORT(__status__) \
(((__status__) >> CPDMA_FROM_TO_PORT_SHIFT) & 0x7)
+#define CPDMA_DESC_CRC_LEN 4
#define SLIVER_SIZE 0x40
@@ -175,7 +177,7 @@ struct cpsw_slave {
phy_interface_t phy_if;
struct eth_device edev;
struct cpsw_priv *cpsw;
- struct device_d dev;
+ struct device dev;
};
struct cpdma_desc {
@@ -195,7 +197,7 @@ struct cpdma_chan {
};
struct cpsw_priv {
- struct device_d *dev;
+ struct device *dev;
u32 version;
struct cpsw_platform_data data;
@@ -215,6 +217,8 @@ struct cpsw_priv {
unsigned int slave_size;
unsigned int sliver_ofs;
+ void *rx_buffer[PKTBUFSRX - 2];
+
struct cpdma_desc *descs;
struct cpdma_desc *desc_free;
struct cpdma_chan rx_chan, tx_chan;
@@ -223,7 +227,7 @@ struct cpsw_priv {
};
struct cpsw_mdio_priv {
- struct device_d *dev;
+ struct device *dev;
struct mii_bus miibus;
struct cpsw_mdio_regs *mdio_regs;
};
@@ -581,7 +585,7 @@ static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 val
return 0;
}
-static int cpsw_mdio_probe(struct device_d *dev)
+static int cpsw_mdio_probe(struct device *dev)
{
struct resource *iores;
struct cpsw_mdio_priv *priv;
@@ -620,7 +624,7 @@ static int cpsw_mdio_probe(struct device_d *dev)
* silicon. Since the effect of (b) was found to be largely
* negligible, we keep things simple here.
*/
- udelay(1000);
+ udelay(2000);
start = get_time_ns();
while (1) {
@@ -653,8 +657,9 @@ static __maybe_unused struct of_device_id cpsw_mdio_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, cpsw_mdio_dt_ids);
-static struct driver_d cpsw_mdio_driver = {
+static struct driver cpsw_mdio_driver = {
.name = "cpsw-mdio",
.probe = cpsw_mdio_probe,
.of_compatible = DRV_OF_COMPAT(cpsw_mdio_dt_ids),
@@ -801,7 +806,8 @@ static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc)
}
static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan,
- void *buffer, int len, int port)
+ void *sw_buffer, dma_addr_t hw_buffer,
+ int len, int port)
{
struct cpdma_desc *desc, *prev;
u32 mode;
@@ -820,10 +826,10 @@ static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan,
(port << CPDMA_FROM_TO_PORT_SHIFT);
writel(0, &desc->hw_next);
- writel((u32)buffer, &desc->hw_buffer);
+ writel(hw_buffer, &desc->hw_buffer);
writel(len, &desc->hw_len);
writel(mode | len, &desc->hw_mode);
- writel((u32)buffer, &desc->sw_buffer);
+ writel((u32)sw_buffer, &desc->sw_buffer);
writel((u32)len, &desc->sw_len);
if (!chan->head) {
@@ -850,7 +856,7 @@ done:
}
static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan,
- void **buffer, int *len)
+ void **buffer, dma_addr_t *dma, int *len)
{
struct cpdma_desc *desc = chan->head;
struct cpsw_priv *priv = slave->cpsw;
@@ -861,9 +867,14 @@ static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan,
status = readl(&desc->hw_mode);
- if (len)
+ if (len) {
*len = status & 0x7ff;
+ if (status & CPDMA_DESC_PASS_CRC)
+ *len -= CPDMA_DESC_CRC_LEN;
+ }
+ if (dma)
+ *dma = readl(&desc->hw_buffer);
if (buffer)
*buffer = (void *)readl(&desc->sw_buffer);
@@ -915,7 +926,7 @@ static int cpsw_open(struct eth_device *edev)
return 0;
}
-static int cpsw_setup(struct device_d *dev)
+static int cpsw_setup(struct device *dev)
{
struct cpsw_priv *priv = dev->priv;
int i, ret;
@@ -979,8 +990,15 @@ static int cpsw_setup(struct device_d *dev)
/* submit rx descs */
for (i = 0; i < PKTBUFSRX - 2; i++) {
- ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i],
- PKTSIZE, 0);
+ void *buffer = priv->rx_buffer[i];
+ unsigned len = PKTSIZE;
+ dma_addr_t dma;
+
+ dma = dma_map_single(priv->dev, buffer, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(priv->dev, dma))
+ return -EFAULT;
+
+ ret = cpdma_submit(priv, &priv->rx_chan, buffer, dma, len, 0);
if (ret < 0) {
dev_err(dev, "error %d submitting rx desc\n", ret);
break;
@@ -1011,20 +1029,21 @@ static int cpsw_send(struct eth_device *edev, void *packet, int length)
{
struct cpsw_slave *slave = edev->priv;
struct cpsw_priv *priv = slave->cpsw;
- void *buffer;
- int ret, len;
+ dma_addr_t dma;
+ int ret;
dev_dbg(&slave->dev, "* %s slave %d\n", __func__, slave->slave_num);
/* first reap completed packets */
- while (cpdma_process(slave, &priv->tx_chan, &buffer, &len) >= 0);
+ while (cpdma_process(slave, &priv->tx_chan, NULL, NULL, NULL) >= 0)
+ ;
dev_dbg(&slave->dev, "%s: %i bytes @ 0x%p\n", __func__, length, packet);
- dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE);
- ret = cpdma_submit(priv, &priv->tx_chan, packet,
+ dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE);
+ ret = cpdma_submit(priv, &priv->tx_chan, packet, dma,
length, BIT(slave->slave_num));
- dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE);
return ret;
}
@@ -1033,16 +1052,15 @@ static int cpsw_recv(struct eth_device *edev)
{
struct cpsw_slave *slave = edev->priv;
struct cpsw_priv *priv = slave->cpsw;
+ dma_addr_t dma;
void *buffer;
int len;
- while (cpdma_process(slave, &priv->rx_chan, &buffer, &len) >= 0) {
- dma_sync_single_for_cpu((unsigned long)buffer, len,
- DMA_FROM_DEVICE);
+ while (cpdma_process(slave, &priv->rx_chan, &buffer, &dma, &len) >= 0) {
+ dma_sync_single_for_cpu(priv->dev, dma, len, DMA_FROM_DEVICE);
net_receive(edev, buffer, len);
- dma_sync_single_for_device((unsigned long)buffer, len,
- DMA_FROM_DEVICE);
- cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE, 0);
+ dma_sync_single_for_device(priv->dev, dma, len, DMA_FROM_DEVICE);
+ cpdma_submit(priv, &priv->rx_chan, buffer, dma, PKTSIZE, 0);
}
return 0;
@@ -1062,7 +1080,7 @@ static int cpsw_slave_setup(struct cpsw_slave *slave, int slave_num,
{
void *regs = priv->regs;
struct eth_device *edev = &slave->edev;
- struct device_d *dev = &slave->dev;
+ struct device *dev = &slave->dev;
int ret;
edev->parent = dev;
@@ -1229,7 +1247,7 @@ static void cpsw_add_slave(struct cpsw_slave *slave, struct device_node *child,
dev_warn(slave->cpsw->dev, "phy_id is deprecated, use phy-handle\n");
}
- slave->dev.device_node = child;
+ slave->dev.of_node = child;
slave->phy_id = phy_id[1];
slave->phy_if = of_get_phy_mode(child);
slave->slave_num = i;
@@ -1237,8 +1255,8 @@ static void cpsw_add_slave(struct cpsw_slave *slave, struct device_node *child,
static int cpsw_legacy_probe_dt(struct cpsw_priv *priv)
{
- struct device_d *dev = priv->dev;
- struct device_node *np = dev->device_node, *child;
+ struct device *dev = priv->dev;
+ struct device_node *np = dev->of_node, *child;
int ret, i = 0;
ret = of_property_read_u32(np, "slaves", &priv->num_slaves);
@@ -1265,8 +1283,8 @@ static int cpsw_legacy_probe_dt(struct cpsw_priv *priv)
static int cpsw_switch_probe_dt(struct cpsw_priv *priv)
{
- struct device_d *dev = priv->dev;
- struct device_node *np = dev->device_node, *child;
+ struct device *dev = priv->dev;
+ struct device_node *np = dev->of_node, *child;
struct device_node *ports = NULL;
int ret, i = 0;
@@ -1298,7 +1316,7 @@ static int cpsw_switch_probe_dt(struct cpsw_priv *priv)
static int cpsw_probe_dt(struct cpsw_priv *priv)
{
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
struct device_node *physel;
int (*probe_slaves_dt)(struct cpsw_priv *priv);
int ret, i = 0;
@@ -1326,7 +1344,7 @@ static int cpsw_probe_dt(struct cpsw_priv *priv)
return 0;
}
-static int cpsw_probe(struct device_d *dev)
+static int cpsw_probe(struct device *dev)
{
struct resource *iores;
struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data;
@@ -1342,14 +1360,18 @@ static int cpsw_probe(struct device_d *dev)
return PTR_ERR(iores);
regs = IOMEM(iores->start);
- ret = of_platform_populate(dev->device_node, NULL, dev);
+ ret = of_platform_populate(dev->of_node, NULL, dev);
if (ret)
return ret;
priv = xzalloc(sizeof(*priv));
priv->dev = dev;
- if (dev->device_node) {
+ ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer));
+ if (ret)
+ goto out;
+
+ if (dev->of_node) {
ret = cpsw_probe_dt(priv);
if (ret)
goto out;
@@ -1410,7 +1432,7 @@ out:
return ret;
}
-static void cpsw_remove(struct device_d *dev)
+static void cpsw_remove(struct device *dev)
{
struct cpsw_priv *priv = dev->priv;
int i;
@@ -1433,8 +1455,9 @@ static __maybe_unused struct of_device_id cpsw_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, cpsw_dt_ids);
-static struct driver_d cpsw_driver = {
+static struct driver cpsw_driver = {
.name = "cpsw",
.probe = cpsw_probe,
.remove = cpsw_remove,
diff --git a/drivers/net/cs8900.c b/drivers/net/cs8900.c
index 75bbbd79e1..0dfd3f1303 100644
--- a/drivers/net/cs8900.c
+++ b/drivers/net/cs8900.c
@@ -180,6 +180,7 @@ struct cs8900_priv {
void *regs;
struct cs89x0_product *product;
struct cs89x0_chip *chip;
+ void *rx_buf;
};
/* Read a 16-bit value from PacketPage Memory using I/O Space operation */
@@ -294,13 +295,13 @@ static int cs8900_recv(struct eth_device *dev)
status = readw(priv->regs + CS8900_RTDATA0);
len = readw(priv->regs + CS8900_RTDATA0);
- for (addr = (u16 *) NetRxPackets[0], i = len >> 1; i > 0; i--) {
+ for (addr = (u16 *)priv->rx_buf, i = len >> 1; i > 0; i--) {
*addr++ = readw(priv->regs + CS8900_RTDATA0);
}
if (len & 1) {
*addr++ = readw(priv->regs + CS8900_RTDATA0);
}
- net_receive(dev, NetRxPackets[0], len);
+ net_receive(dev, priv->rx_buf, len);
return len;
}
@@ -349,7 +350,7 @@ static const char *yesno_str(int v)
return v ? "yes" : "no";
}
-static void cs8900_info(struct device_d *dev)
+static void cs8900_info(struct device *dev)
{
struct eth_device *edev = dev_to_edev(dev);
struct cs8900_priv *priv = (struct cs8900_priv *)edev->priv;
@@ -424,7 +425,7 @@ static int cs8900_check_id(struct cs8900_priv *priv)
return result;
}
-static int cs8900_probe(struct device_d *dev)
+static int cs8900_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
@@ -442,6 +443,8 @@ static int cs8900_probe(struct device_d *dev)
return -1;
}
+ priv->rx_buf = xmalloc(PKTSIZE);
+
edev = (struct eth_device *)xmalloc(sizeof(struct eth_device));
edev->priv = priv;
@@ -460,7 +463,7 @@ static int cs8900_probe(struct device_d *dev)
return 0;
}
-static struct driver_d cs8900_driver = {
+static struct driver cs8900_driver = {
.name = "cs8900",
.probe = cs8900_probe,
};
diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index ff35b746e2..04277e6924 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -31,12 +31,12 @@
#include <init.h>
#include <asm/system.h>
#include <linux/phy.h>
-#include <mach/emac_defs.h>
+#include <mach/omap/emac_defs.h>
#include <of_net.h>
#include "davinci_emac.h"
struct davinci_emac_priv {
- struct device_d *dev;
+ struct device *dev;
struct eth_device edev;
/* EMAC Addresses */
@@ -83,7 +83,7 @@ static inline void __iomem *HW_TO_BD(uint32_t x)
#endif
struct davinci_mdio_priv {
- struct device_d *dev;
+ struct device *dev;
struct mii_bus miibus;
void __iomem *adap_mdio; /* = EMAC_MDIO_BASE_ADDR */
};
@@ -430,7 +430,7 @@ static int davinci_emac_send(struct eth_device *edev, void *packet, int length)
EMAC_CPPI_OWNERSHIP_BIT |
EMAC_CPPI_EOP_BIT),
priv->emac_tx_desc + EMAC_DESC_PKT_FLAG_LEN);
- dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_sync_single_for_device(priv->dev, (unsigned long)packet, length, DMA_TO_DEVICE);
/* Send the packet */
writel(BD_TO_HW(priv->emac_tx_desc), priv->adap_emac + EMAC_TX0HDP);
@@ -448,7 +448,7 @@ static int davinci_emac_send(struct eth_device *edev, void *packet, int length)
break;
}
}
- dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE);
+ dma_sync_single_for_cpu(priv->dev, (unsigned long)packet, length, DMA_TO_DEVICE);
dev_dbg(priv->dev, "- emac_send (ret_status %i)\n", ret_status);
return ret_status;
@@ -480,9 +480,9 @@ static int davinci_emac_recv(struct eth_device *edev)
pkt = (unsigned char *)readl(rx_curr_desc + EMAC_DESC_BUFFER);
len = readl(rx_curr_desc + EMAC_DESC_BUFF_OFF_LEN) & 0xffff;
dev_dbg(priv->dev, "| emac_recv got packet (length %i)\n", len);
- dma_sync_single_for_cpu((unsigned long)pkt, len, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(priv->dev, (unsigned long)pkt, len, DMA_FROM_DEVICE);
net_receive(edev, pkt, len);
- dma_sync_single_for_device((unsigned long)pkt, len, DMA_FROM_DEVICE);
+ dma_sync_single_for_device(priv->dev, (unsigned long)pkt, len, DMA_FROM_DEVICE);
ret = len;
}
@@ -533,13 +533,13 @@ out:
return ret;
}
-static int davinci_emac_probe(struct device_d *dev)
+static int davinci_emac_probe(struct device *dev)
{
struct resource *iores;
struct davinci_emac_priv *priv;
uint32_t ctrl_reg_offset;
uint32_t ctrl_ram_offset;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
dev_dbg(dev, "+ emac_probe\n");
@@ -588,7 +588,7 @@ static int davinci_emac_probe(struct device_d *dev)
return 0;
}
-static void davinci_emac_remove(struct device_d *dev)
+static void davinci_emac_remove(struct device *dev)
{
struct davinci_emac_priv *priv = dev->priv;
@@ -602,8 +602,9 @@ static __maybe_unused struct of_device_id davinci_emac_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, davinci_emac_dt_ids);
-static struct driver_d davinci_emac_driver = {
+static struct driver davinci_emac_driver = {
.name = "davinci_emac",
.probe = davinci_emac_probe,
.remove = davinci_emac_remove,
@@ -611,7 +612,7 @@ static struct driver_d davinci_emac_driver = {
};
device_platform_driver(davinci_emac_driver);
-static int davinci_mdio_probe(struct device_d *dev)
+static int davinci_mdio_probe(struct device *dev)
{
struct resource *iores;
struct davinci_mdio_priv *priv;
@@ -658,8 +659,9 @@ static __maybe_unused struct of_device_id davinci_mdio_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, davinci_mdio_dt_ids);
-static struct driver_d davinci_mdio_driver = {
+static struct driver davinci_mdio_driver = {
.name = "davinci_mdio",
.probe = davinci_mdio_probe,
.of_compatible = DRV_OF_COMPAT(davinci_mdio_dt_ids),
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index afc275e81e..6936c844cd 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -156,7 +156,7 @@ static void rx_descs_init(struct eth_device *dev)
else
desc_p->dmamac_cntl |= DESC_RXCTRL_RXCHAIN;
- dma_sync_single_for_cpu(desc_p->dmamac_addr,
+ dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr,
CONFIG_ETH_BUFSIZE, DMA_FROM_DEVICE);
desc_p->txrx_status = DESC_RXSTS_OWNBYDMA;
}
@@ -286,7 +286,7 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length)
}
memcpy(dmamac_addr(desc_p), packet, length);
- dma_sync_single_for_device(desc_p->dmamac_addr, length,
+ dma_sync_single_for_device(dev->parent, desc_p->dmamac_addr, length,
DMA_TO_DEVICE);
if (priv->enh_desc) {
@@ -314,7 +314,7 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length)
/* Start the transmission */
writel(POLL_DATA, &dma_p->txpolldemand);
- dma_sync_single_for_cpu(desc_p->dmamac_addr, length,
+ dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr, length,
DMA_TO_DEVICE);
return 0;
@@ -358,10 +358,10 @@ static int dwc_ether_rx(struct eth_device *dev)
length = (status & DESC_RXSTS_FRMLENMSK) >>
DESC_RXSTS_FRMLENSHFT;
- dma_sync_single_for_cpu(desc_p->dmamac_addr,
+ dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr,
length, DMA_FROM_DEVICE);
net_receive(dev, dmamac_addr(desc_p), length);
- dma_sync_single_for_device(desc_p->dmamac_addr,
+ dma_sync_single_for_device(dev->parent, desc_p->dmamac_addr,
length, DMA_FROM_DEVICE);
ret = length;
}
@@ -410,7 +410,7 @@ static int dwc_ether_set_ethaddr(struct eth_device *dev, const unsigned char *ad
return 0;
}
-static void dwc_version(struct device_d *dev, u32 hwid)
+static void dwc_version(struct device *dev, u32 hwid)
{
u32 uid = ((hwid & 0x0000ff00) >> 8);
u32 synid = (hwid & 0x000000ff);
@@ -419,7 +419,7 @@ static void dwc_version(struct device_d *dev, u32 hwid)
uid, synid);
}
-static int dwc_probe_dt(struct device_d *dev, struct dw_eth_dev *priv)
+static int dwc_probe_dt(struct device *dev, struct dw_eth_dev *priv)
{
struct device_node *child;
@@ -427,12 +427,12 @@ static int dwc_probe_dt(struct device_d *dev, struct dw_eth_dev *priv)
return -ENODEV;
priv->phy_addr = -1;
- priv->interface = of_get_phy_mode(dev->device_node);
+ priv->interface = of_get_phy_mode(dev->of_node);
/* Set MDIO bus device node, if present. */
- for_each_child_of_node(dev->device_node, child) {
+ for_each_child_of_node(dev->of_node, child) {
if (of_device_is_compatible(child, "snps,dwmac-mdio")) {
- priv->miibus.dev.device_node = child;
+ priv->miibus.dev.of_node = child;
break;
}
}
@@ -440,7 +440,7 @@ static int dwc_probe_dt(struct device_d *dev, struct dw_eth_dev *priv)
return 0;
}
-struct dw_eth_dev *dwc_drv_probe(struct device_d *dev)
+struct dw_eth_dev *dwc_drv_probe(struct device *dev)
{
struct resource *iores;
struct dw_eth_dev *priv;
@@ -526,7 +526,7 @@ struct dw_eth_dev *dwc_drv_probe(struct device_d *dev)
return priv;
}
-void dwc_drv_remove(struct device_d *dev)
+void dwc_drv_remove(struct device *dev)
{
struct eth_device *edev = dev->priv;
diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index 8f6234aec5..5b587fa59e 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -57,8 +57,8 @@ static inline dma_addr_t rx_dma_addr(struct dw_eth_dev *priv,
+ ((u8 *)desc - (u8 *)priv->rx_mac_descrtable_cpu);
}
-struct dw_eth_dev *dwc_drv_probe(struct device_d *dev);
-void dwc_drv_remove(struct device_d *dev);
+struct dw_eth_dev *dwc_drv_probe(struct device *dev);
+void dwc_drv_remove(struct device *dev);
#define CONFIG_TX_DESCR_NUM 16
#define CONFIG_RX_DESCR_NUM 16
diff --git a/drivers/net/designware_eqos.c b/drivers/net/designware_eqos.c
index 79b9979697..ccce51b6af 100644
--- a/drivers/net/designware_eqos.c
+++ b/drivers/net/designware_eqos.c
@@ -9,7 +9,7 @@
#include <common.h>
#include <init.h>
#include <gpio.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#include <dma.h>
#include <net.h>
#include <of_net.h>
@@ -26,7 +26,8 @@
struct eqos_mac_regs {
u32 config; /* 0x000 */
u32 ext_config; /* 0x004 */
- u32 unused_004[(0x070 - 0x008) / 4]; /* 0x008 */
+ u32 packet_filter; /* 0x008 */
+ u32 unused_004[(0x070 - 0x00C) / 4]; /* 0x00C */
u32 q0_tx_flow_ctrl; /* 0x070 */
u32 unused_070[(0x090 - 0x074) / 4]; /* 0x074 */
u32 rx_flow_ctrl; /* 0x090 */
@@ -62,6 +63,9 @@ struct eqos_mac_regs {
#define EQOS_MAC_CONFIGURATION_TE BIT(1)
#define EQOS_MAC_CONFIGURATION_RE BIT(0)
+#define EQOS_MAC_PACKET_FILTER_PR BIT(0) /* Promiscuous mode */
+#define EQOS_MAC_PACKET_FILTER_PCF BIT(7) /* Pass Control Frames */
+
#define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT 16
#define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_MASK 0xffff
#define EQOS_MAC_Q0_TX_FLOW_CTRL_TFE BIT(1)
@@ -168,8 +172,6 @@ struct eqos_dma_regs {
#define EQOS_DESCRIPTOR_SIZE (EQOS_DESCRIPTOR_WORDS * 4)
/* We assume ARCH_DMA_MINALIGN >= 16; 16 is the EQOS HW minimum */
#define EQOS_DESCRIPTOR_ALIGN 64
-#define EQOS_DESCRIPTORS_TX 4
-#define EQOS_DESCRIPTORS_RX 4
#define EQOS_DESCRIPTORS_NUM (EQOS_DESCRIPTORS_TX + EQOS_DESCRIPTORS_RX)
#define EQOS_DESCRIPTORS_SIZE ALIGN(EQOS_DESCRIPTORS_NUM * \
EQOS_DESCRIPTOR_SIZE, EQOS_DESCRIPTOR_ALIGN)
@@ -194,23 +196,23 @@ struct eqos_desc {
#define MII_BUSY (1 << 0)
-static int eqos_phy_reset(struct device_d *dev, struct eqos *eqos)
+static int eqos_phy_reset(struct device *dev, struct eqos *eqos)
{
- int phy_reset;
+ struct gpio_desc *phy_reset;
u32 delays[3] = { 0, 0, 0 };
- phy_reset = gpiod_get(dev, "snps,reset", GPIOF_OUT_INIT_ACTIVE);
-
- if (!gpio_is_valid(phy_reset))
- return 0;
-
- of_property_read_u32_array(dev->device_node,
- "snps,reset-delays-us",
- delays, ARRAY_SIZE(delays));
-
- udelay(delays[1]);
- gpio_set_active(phy_reset, false);
- udelay(delays[2]);
+ phy_reset = gpiod_get_optional(dev, "snps,reset", GPIOF_OUT_INIT_ACTIVE);
+ if (IS_ERR(phy_reset)) {
+ dev_warn(dev, "Failed to get 'snps,reset' GPIO (ignored)\n");
+ } else if (phy_reset) {
+ of_property_read_u32_array(dev->of_node,
+ "snps,reset-delays-us",
+ delays, ARRAY_SIZE(delays));
+
+ udelay(delays[1]);
+ gpiod_set_value(phy_reset, false);
+ udelay(delays[2]);
+ }
return 0;
}
@@ -347,6 +349,14 @@ int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac)
memcpy(eqos->macaddr, mac, ETH_ALEN);
+ if (!eqos->is_started)
+ return 0;
+
+ /* mac_hi is only partially overwritten by the following code. Part of
+ * this variable is DCS (DMA Channel Select). If this variable is not
+ * zeroed, we may get some random DMA RX channel.
+ */
+ mac_hi = 0;
/* Update the MAC address */
memcpy(&mac_hi, &mac[4], 2);
memcpy(&mac_lo, &mac[0], 4);
@@ -357,6 +367,26 @@ int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac)
return 0;
}
+static int eqos_set_promisc(struct eth_device *edev, bool enable)
+{
+ struct eqos *eqos = edev->priv;
+ u32 mask;
+
+ eqos->promisc_enabled = enable;
+
+ if (!eqos->is_started)
+ return 0;
+
+ mask = EQOS_MAC_PACKET_FILTER_PR;
+
+ if (enable)
+ setbits_le32(&eqos->mac_regs->packet_filter, mask);
+ else
+ clrbits_le32(&eqos->mac_regs->packet_filter, mask);
+
+ return 0;
+}
+
/* Get PHY out of power saving mode. If this is needed elsewhere then
* consider making it part of phy-core and adding a resume method to
* the phy device ops. */
@@ -384,7 +414,7 @@ static int eqos_start(struct eth_device *edev)
{
struct eqos *eqos = edev->priv;
u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl;
- unsigned long last_rx_desc;
+ unsigned long last_rx_rf_desc;
unsigned long rate;
u32 mode_set;
int ret;
@@ -395,6 +425,10 @@ static int eqos_start(struct eth_device *edev)
if (ret)
return ret;
+ /* In some cases where PHY or DSA switch is the clock provider for
+ * EQOS, we need to probe and configure them before issuing software
+ * reset here.
+ */
setbits_le32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR);
ret = readl_poll_timeout(&eqos->dma_regs->mode, mode_set,
@@ -405,8 +439,10 @@ static int eqos_start(struct eth_device *edev)
return ret;
}
- /* Reset above clears MAC address */
+ /* Reset above clears any previously made configuration */
+ eqos->is_started = true;
eqos_set_ethaddr(edev, eqos->macaddr);
+ eqos_set_promisc(edev, eqos->promisc_enabled);
/* Required for accurate time keeping with EEE counters */
rate = eqos->ops->get_csr_clk_rate(eqos);
@@ -588,9 +624,9 @@ static int eqos_start(struct eth_device *edev)
eqos->tx_currdescnum = eqos->rx_currdescnum = 0;
for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) {
- struct eqos_desc *rx_desc = &eqos->rx_descs[i];
+ struct eqos_desc *rx_rf_desc = &eqos->rx_descs[i];
- writel(EQOS_DESC3_BUF1V | EQOS_DESC3_OWN, &rx_desc->des3);
+ writel(EQOS_DESC3_BUF1V | EQOS_DESC3_OWN, &rx_rf_desc->des3);
}
writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress);
@@ -620,8 +656,8 @@ static int eqos_start(struct eth_device *edev)
* that's not distinguishable from none of the descriptors being
* available.
*/
- last_rx_desc = (ulong)&eqos->rx_descs[(EQOS_DESCRIPTORS_RX - 1)];
- writel(last_rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+ last_rx_rf_desc = (ulong)&eqos->rx_descs[(EQOS_DESCRIPTORS_RX - 1)];
+ writel(last_rx_rf_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
return 0;
}
@@ -668,7 +704,6 @@ static void eqos_stop(struct eth_device *edev)
static int eqos_send(struct eth_device *edev, void *packet, int length)
{
struct eqos *eqos = edev->priv;
- struct device_d *dev = &eqos->netdev.dev;
struct eqos_desc *tx_desc;
dma_addr_t dma;
u32 des3;
@@ -678,8 +713,8 @@ static int eqos_send(struct eth_device *edev, void *packet, int length)
eqos->tx_currdescnum++;
eqos->tx_currdescnum %= EQOS_DESCRIPTORS_TX;
- dma = dma_map_single(dev, packet, length, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, dma))
+ dma = dma_map_single(edev->parent, packet, length, DMA_TO_DEVICE);
+ if (dma_mapping_error(edev->parent, dma))
return -EFAULT;
tx_desc->des0 = (unsigned long)dma;
@@ -698,7 +733,7 @@ static int eqos_send(struct eth_device *edev, void *packet, int length)
!(des3 & EQOS_DESC3_OWN),
100 * USEC_PER_MSEC);
- dma_unmap_single(dev, dma, length, DMA_TO_DEVICE);
+ dma_unmap_single(edev->parent, dma, length, DMA_TO_DEVICE);
if (ret == -ETIMEDOUT)
eqos_dbg(eqos, "TX timeout\n");
@@ -709,33 +744,51 @@ static int eqos_send(struct eth_device *edev, void *packet, int length)
static int eqos_recv(struct eth_device *edev)
{
struct eqos *eqos = edev->priv;
- struct eqos_desc *rx_desc;
+ struct eqos_desc *rx_wbf_desc, *rx_rf_desc;
+ dma_addr_t dma;
void *frame;
int length;
- rx_desc = &eqos->rx_descs[eqos->rx_currdescnum];
- if (readl(&rx_desc->des3) & EQOS_DESC3_OWN)
+ /* We have two types of RX descriptors at some pointer: Read and
+ * Write-Back:
+ * All RX descriptors are prepared by the software and given to the
+ * DMA as "Normal" Descriptors with the content as shown in Receive
+ * Normal Descriptor (Read Format). The DMA reads this descriptor and
+ * after transferring a received packet (or part of) to the buffers
+ * indicated by the descriptor, the Rx DMA will close the descriptor
+ * with the corresponding packet status. The format of this status is
+ * given in the "Receive Normal Descriptor (Write-Back Format)"
+ */
+
+ /* Write-Back Format RX descriptor */
+ rx_wbf_desc = &eqos->rx_descs[eqos->rx_currdescnum];
+ if (readl(&rx_wbf_desc->des3) & EQOS_DESC3_OWN)
return 0;
- frame = phys_to_virt(rx_desc->des0);
- length = rx_desc->des3 & 0x7fff;
+ dma = eqos->dma_rx_buf[eqos->rx_currdescnum];
+ frame = phys_to_virt(dma);
+ length = rx_wbf_desc->des3 & 0x7fff;
- dma_sync_single_for_cpu((unsigned long)frame, length, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(edev->parent, (unsigned long)frame,
+ length, DMA_FROM_DEVICE);
net_receive(edev, frame, length);
- dma_sync_single_for_device((unsigned long)frame, length, DMA_FROM_DEVICE);
-
- rx_desc->des0 = (unsigned long)frame;
- rx_desc->des1 = 0;
- rx_desc->des2 = 0;
+ dma_sync_single_for_device(edev->parent, (unsigned long)frame,
+ length, DMA_FROM_DEVICE);
+
+ /* Read Format RX descriptor */
+ rx_rf_desc = &eqos->rx_descs[eqos->rx_currdescnum];
+ rx_rf_desc->des0 = dma;
+ rx_rf_desc->des1 = 0;
+ rx_rf_desc->des2 = 0;
/*
* Make sure that if HW sees the _OWN write below, it will see all the
* writes to the rest of the descriptor too.
*/
- rx_desc->des3 |= EQOS_DESC3_BUF1V;
- rx_desc->des3 |= EQOS_DESC3_OWN;
+ rx_rf_desc->des3 |= EQOS_DESC3_BUF1V;
+ rx_rf_desc->des3 |= EQOS_DESC3_OWN;
barrier();
- writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+ writel((ulong)rx_rf_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
eqos->rx_currdescnum++;
eqos->rx_currdescnum %= EQOS_DESCRIPTORS_RX;
@@ -745,7 +798,7 @@ static int eqos_recv(struct eth_device *edev)
static int eqos_init_resources(struct eqos *eqos)
{
- struct device_d *dev = eqos->netdev.parent;
+ struct eth_device *edev = &eqos->netdev;
int ret = -ENOMEM;
void *descs;
void *p;
@@ -763,16 +816,17 @@ static int eqos_init_resources(struct eqos *eqos)
goto err_free_desc;
for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) {
- struct eqos_desc *rx_desc = &eqos->rx_descs[i];
+ struct eqos_desc *rx_rf_desc = &eqos->rx_descs[i];
dma_addr_t dma;
- dma = dma_map_single(dev, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, dma)) {
+ dma = dma_map_single(edev->parent, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(edev->parent, dma)) {
ret = -EFAULT;
goto err_free_rx_bufs;
}
- rx_desc->des0 = dma;
+ rx_rf_desc->des0 = dma;
+ eqos->dma_rx_buf[i] = dma;
p += EQOS_MAX_PACKET_SIZE;
}
@@ -788,7 +842,7 @@ err:
return ret;
}
-static int eqos_init(struct device_d *dev, struct eqos *eqos)
+static int eqos_init(struct device *dev, struct eqos *eqos)
{
int ret;
@@ -804,26 +858,26 @@ static int eqos_init(struct device_d *dev, struct eqos *eqos)
return ret;
}
-static void eqos_probe_dt(struct device_d *dev, struct eqos *eqos)
+static void eqos_probe_dt(struct device *dev, struct eqos *eqos)
{
struct device_node *child;
- eqos->interface = of_get_phy_mode(dev->device_node);
+ eqos->interface = of_get_phy_mode(dev->of_node);
eqos->phy_addr = -1;
/* Set MDIO bus device node, if present. */
- for_each_child_of_node(dev->device_node, child) {
+ for_each_child_of_node(dev->of_node, child) {
if (of_device_is_compatible(child, "snps,dwmac-mdio") ||
(child->name && !of_node_cmp(child->name, "mdio"))) {
- eqos->miibus.dev.device_node = child;
+ eqos->miibus.dev.of_node = child;
break;
}
}
}
-int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv)
+int eqos_probe(struct device *dev, const struct eqos_ops *ops, void *priv)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct mii_bus *miibus;
struct resource *iores;
struct eqos *eqos;
@@ -856,6 +910,7 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv)
edev->halt = eqos_stop;
edev->get_ethaddr = ops->get_ethaddr;
edev->set_ethaddr = ops->set_ethaddr;
+ edev->set_promisc = eqos_set_promisc;
miibus = &eqos->miibus;
miibus->parent = edev->parent;
@@ -863,9 +918,9 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv)
miibus->write = eqos_mdio_write;
miibus->priv = eqos;
- miibus->dev.device_node = of_get_compatible_child(np, "snps,dwmac-mdio");
- if (!miibus->dev.device_node)
- miibus->dev.device_node = of_get_child_by_name(np, "mdio");
+ miibus->dev.of_node = of_get_compatible_child(np, "snps,dwmac-mdio");
+ if (!miibus->dev.of_node)
+ miibus->dev.of_node = of_get_child_by_name(np, "mdio");
ret = eqos_init(dev, eqos);
if (ret)
@@ -882,7 +937,7 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv)
return eth_register(edev);
}
-void eqos_remove(struct device_d *dev)
+void eqos_remove(struct device *dev)
{
struct eqos *eqos = dev->priv;
diff --git a/drivers/net/designware_eqos.h b/drivers/net/designware_eqos.h
index 1b6b0400e1..951565e8f9 100644
--- a/drivers/net/designware_eqos.h
+++ b/drivers/net/designware_eqos.h
@@ -10,7 +10,7 @@ struct eqos;
struct eth_device;
struct eqos_ops {
- int (*init)(struct device_d *dev, struct eqos *priv);
+ int (*init)(struct device *dev, struct eqos *priv);
int (*get_ethaddr)(struct eth_device *dev, unsigned char *mac);
int (*set_ethaddr)(struct eth_device *edev, const unsigned char *mac);
void (*adjust_link)(struct eth_device *edev);
@@ -40,6 +40,9 @@ struct eqos_dma_regs;
struct eqos_mac_regs;
struct eqos_mtl_regs;
+#define EQOS_DESCRIPTORS_TX 4
+#define EQOS_DESCRIPTORS_RX 64
+
struct eqos {
struct eth_device netdev;
struct mii_bus miibus;
@@ -49,6 +52,7 @@ struct eqos {
u32 tx_currdescnum, rx_currdescnum;
struct eqos_desc *tx_descs, *rx_descs;
+ dma_addr_t dma_rx_buf[EQOS_DESCRIPTORS_RX];
void __iomem *regs;
struct eqos_mac_regs __iomem *mac_regs;
@@ -60,11 +64,14 @@ struct eqos {
const struct eqos_ops *ops;
void *priv;
+
+ bool is_started;
+ bool promisc_enabled;
};
-struct device_d;
-int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv);
-void eqos_remove(struct device_d *dev);
+struct device;
+int eqos_probe(struct device *dev, const struct eqos_ops *ops, void *priv);
+void eqos_remove(struct device *dev);
int eqos_get_ethaddr(struct eth_device *edev, unsigned char *mac);
int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac);
diff --git a/drivers/net/designware_generic.c b/drivers/net/designware_generic.c
index 90fca1951d..fc9f0745f7 100644
--- a/drivers/net/designware_generic.c
+++ b/drivers/net/designware_generic.c
@@ -16,7 +16,7 @@ static struct dw_eth_drvdata dwmac_370a_drvdata = {
.enh_desc = 1,
};
-static int dwc_ether_probe(struct device_d *dev)
+static int dwc_ether_probe(struct device *dev)
{
struct dw_eth_dev *dwc;
@@ -35,8 +35,9 @@ static __maybe_unused struct of_device_id dwc_ether_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, dwc_ether_compatible);
-static struct driver_d dwc_ether_driver = {
+static struct driver dwc_ether_driver = {
.name = "designware_eth",
.probe = dwc_ether_probe,
.remove = dwc_drv_remove,
diff --git a/drivers/net/designware_imx.c b/drivers/net/designware_imx.c
new file mode 100644
index 0000000000..c281d3b64b
--- /dev/null
+++ b/drivers/net/designware_imx.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <init.h>
+#include <net.h>
+#include <linux/regmap.h>
+#include <mfd/syscon.h>
+#include <linux/clk.h>
+
+#include "designware_eqos.h"
+
+#define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16)
+#define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16)
+#define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16)
+#define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16)
+#define GPR_ENET_QOS_CLK_GEN_EN BIT(19)
+#define GPR_ENET_QOS_CLK_TX_CLK_SEL BIT(20)
+#define GPR_ENET_QOS_RGMII_EN BIT(21)
+
+#define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0)
+#define MX93_GPR_ENET_QOS_INTF_MASK GENMASK(3, 1)
+#define MX93_GPR_ENET_QOS_INTF_SEL_MII (0x0 << 1)
+#define MX93_GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 1)
+#define MX93_GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 1)
+#define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0)
+
+struct eqos_imx_soc_data {
+ int (*set_interface_mode)(struct eqos *eqos);
+ bool mac_rgmii_txclk_auto_adj;
+};
+
+struct eqos_imx_priv {
+ struct device *dev;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct regmap *intf_regmap;
+ u32 intf_reg_off;
+ bool rmii_refclk_ext;
+ struct eqos_imx_soc_data *soc_data;
+};
+
+enum { CLK_STMMACETH, CLK_PCLK, CLK_PTP_REF, CLK_TX};
+static const struct clk_bulk_data imx_clks[] = {
+ [CLK_STMMACETH] = { .id = "stmmaceth" },
+ [CLK_PCLK] = { .id = "pclk" },
+ [CLK_PTP_REF] = { .id = "ptp_ref" },
+ [CLK_TX] = { .id = "tx" },
+};
+
+static unsigned long eqos_get_csr_clk_rate_imx(struct eqos *eqos)
+{
+ struct eqos_imx_priv *priv = eqos->priv;
+
+ return clk_get_rate(priv->clks[CLK_PCLK].clk);
+}
+
+static int eqos_set_txclk(struct eqos *eqos, int speed)
+{
+ struct eqos_imx_priv *priv = eqos->priv;
+ unsigned long rate;
+ int ret;
+
+ switch (speed) {
+ case SPEED_10:
+ rate = 2500000;
+ break;
+ case SPEED_100:
+ rate = 25000000;
+ break;
+ case SPEED_1000:
+ rate = 125000000;
+ break;
+ default:
+ dev_err(priv->dev, "unknown speed value for GMAC speed=%d", speed);
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(priv->clks[CLK_TX].clk, rate);
+ if (ret)
+ dev_err(priv->dev, "set TX clk rate %ld failed %d\n", rate, ret);
+
+ return ret;
+}
+
+static void eqos_adjust_link_imx(struct eth_device *edev)
+{
+ struct eqos *eqos = edev->priv;
+ struct eqos_imx_priv *priv = eqos->priv;
+
+ if (!priv->soc_data->mac_rgmii_txclk_auto_adj)
+ eqos_set_txclk(eqos, edev->phydev->speed);
+
+ eqos_adjust_link(edev);
+}
+
+static int eqos_imx8mp_set_interface_mode(struct eqos *eqos)
+{
+ struct eqos_imx_priv *priv = eqos->priv;
+ int val;
+
+ switch (eqos->interface) {
+ case PHY_INTERFACE_MODE_MII:
+ val = GPR_ENET_QOS_INTF_SEL_MII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ val = GPR_ENET_QOS_INTF_SEL_RMII;
+ val |= (priv->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL);
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ val = GPR_ENET_QOS_INTF_SEL_RGMII |
+ GPR_ENET_QOS_RGMII_EN;
+ break;
+ default:
+ dev_err(priv->dev, "no valid interface mode found!\n");
+ return -EINVAL;
+ }
+
+ val |= GPR_ENET_QOS_CLK_GEN_EN;
+
+ return regmap_update_bits(priv->intf_regmap, priv->intf_reg_off,
+ GPR_ENET_QOS_INTF_MODE_MASK, val);
+}
+
+static int eqos_imx93_set_interface_mode(struct eqos *eqos)
+{
+ struct eqos_imx_priv *priv = eqos->priv;
+
+ int val;
+
+ switch (eqos->interface) {
+ case PHY_INTERFACE_MODE_MII:
+ val = MX93_GPR_ENET_QOS_INTF_SEL_MII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ val = MX93_GPR_ENET_QOS_INTF_SEL_RMII;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ val = MX93_GPR_ENET_QOS_INTF_SEL_RGMII;
+ break;
+ default:
+ dev_dbg(priv->dev, "imx dwmac doesn't support %d interface\n",
+ eqos->interface);
+ return -EINVAL;
+ }
+
+ val |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
+
+ return regmap_update_bits(priv->intf_regmap, priv->intf_reg_off,
+ MX93_GPR_ENET_QOS_INTF_MODE_MASK, val);
+};
+
+static int eqos_init_imx(struct device *dev, struct eqos *eqos)
+{
+ struct device_node *np = dev->device_node;
+ struct eqos_imx_priv *priv = eqos->priv;
+ int ret;
+
+ priv->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode");
+ if (IS_ERR(priv->intf_regmap))
+ return PTR_ERR(priv->intf_regmap);
+
+ ret = of_property_read_u32_index(np, "intf_mode", 1, &priv->intf_reg_off);
+ if (ret) {
+ dev_err(dev, "Can't get intf mode reg offset (%d)\n", ret);
+ return ret;
+ }
+
+ ret = priv->soc_data->set_interface_mode(eqos);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct eqos_ops imx_ops = {
+ .init = eqos_init_imx,
+ .get_ethaddr = eqos_get_ethaddr,
+ .set_ethaddr = eqos_set_ethaddr,
+ .adjust_link = eqos_adjust_link_imx,
+ .get_csr_clk_rate = eqos_get_csr_clk_rate_imx,
+
+ .clk_csr = EQOS_MDIO_ADDR_CR_250_300,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+};
+
+static int eqos_probe_imx(struct device *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct eqos_imx_soc_data *soc_data;
+ struct eqos_imx_priv *priv;
+ int ret;
+
+ ret = dev_get_drvdata(dev, (const void **)&soc_data);
+ if (ret)
+ return ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->soc_data = soc_data;
+ priv->dev = dev;
+
+ if (of_get_property(np, "snps,rmii_refclk_ext", NULL))
+ priv->rmii_refclk_ext = true;
+
+ priv->num_clks = ARRAY_SIZE(imx_clks);
+ priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks));
+ memcpy(priv->clks, imx_clks, sizeof imx_clks);
+
+ ret = clk_bulk_get(dev, priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(dev, "Failed to get clks: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = clk_bulk_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(dev, "Failed to enable clks: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ return eqos_probe(dev, &imx_ops, priv);
+}
+
+static void eqos_remove_imx(struct device *dev)
+{
+ struct eqos *eqos = dev->priv;
+ struct eqos_imx_priv *priv = eqos->priv;
+
+ eqos_remove(dev);
+
+ clk_bulk_disable(priv->num_clks, priv->clks);
+ clk_bulk_put(priv->num_clks, priv->clks);
+}
+
+static struct eqos_imx_soc_data imx93_soc_data = {
+ .set_interface_mode = eqos_imx93_set_interface_mode,
+ .mac_rgmii_txclk_auto_adj = true,
+};
+
+static struct eqos_imx_soc_data imx8mp_soc_data = {
+ .set_interface_mode = eqos_imx8mp_set_interface_mode,
+ .mac_rgmii_txclk_auto_adj = false,
+};
+
+static __maybe_unused struct of_device_id eqos_imx_ids[] = {
+ {
+ .compatible = "nxp,imx93-dwmac-eqos",
+ .data = &imx93_soc_data,
+ }, {
+ .compatible = "nxp,imx8mp-dwmac-eqos",
+ .data = &imx8mp_soc_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, eqos_imx_ids);
+
+static struct driver eqos_imx_driver = {
+ .name = "eqos-imx",
+ .probe = eqos_probe_imx,
+ .remove = eqos_remove_imx,
+ .of_compatible = DRV_OF_COMPAT(eqos_imx_ids),
+};
+device_platform_driver(eqos_imx_driver);
diff --git a/drivers/net/designware_rockchip.c b/drivers/net/designware_rockchip.c
index a3859dce0c..04e2b7f12d 100644
--- a/drivers/net/designware_rockchip.c
+++ b/drivers/net/designware_rockchip.c
@@ -4,7 +4,7 @@
#include <init.h>
#include <dma.h>
#include <net.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <of_net.h>
#include <mfd/syscon.h>
#include <linux/iopoll.h>
@@ -33,7 +33,7 @@ struct eqos_rk_gmac {
int bus_id;
u32 tx_delay;
u32 rx_delay;
- struct device_d *dev;
+ struct device *dev;
};
enum {
@@ -101,7 +101,7 @@ static void rk3568_set_to_rgmii(struct eqos *eqos,
int tx_delay, int rx_delay)
{
struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
u32 offset_con0, offset_con1;
if (IS_ERR(priv->grf)) {
@@ -127,7 +127,7 @@ static void rk3568_set_to_rgmii(struct eqos *eqos,
static void rk3568_set_to_rmii(struct eqos *eqos)
{
struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
u32 offset_con1;
if (IS_ERR(priv->grf)) {
@@ -145,7 +145,7 @@ static void rk3568_set_to_rmii(struct eqos *eqos)
static void rk3568_set_gmac_speed(struct eqos *eqos, int speed)
{
struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
unsigned long rate;
int ret;
@@ -185,7 +185,7 @@ static const struct rk_gmac_ops rk3568_ops = {
static int rk_gmac_powerup(struct eqos *eqos)
{
struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
/*rmii or rgmii*/
switch (eqos->interface) {
@@ -230,9 +230,9 @@ static void eqos_rk_adjust_link(struct eth_device *edev)
eqos_adjust_link(edev);
}
-static int eqos_init_rk_gmac(struct device_d *dev, struct eqos *eqos)
+static int eqos_init_rk_gmac(struct device *dev, struct eqos *eqos)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
int i = 0, ret;
const char *strings;
@@ -306,7 +306,7 @@ static struct eqos_ops rk_gmac_ops = {
.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
};
-static int rk_gmac_probe(struct device_d *dev)
+static int rk_gmac_probe(struct device *dev)
{
return eqos_probe(dev, &rk_gmac_ops, xzalloc(sizeof(struct eqos_rk_gmac)));
}
@@ -319,8 +319,9 @@ static __maybe_unused struct of_device_id rk_gmac_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, rk_gmac_compatible);
-static struct driver_d rk_gmac_driver = {
+static struct driver rk_gmac_driver = {
.name = "eqos-rockchip",
.probe = rk_gmac_probe,
.remove = eqos_remove,
diff --git a/drivers/net/designware_socfpga.c b/drivers/net/designware_socfpga.c
index d6c28af45e..a39c945c81 100644
--- a/drivers/net/designware_socfpga.c
+++ b/drivers/net/designware_socfpga.c
@@ -154,7 +154,8 @@ static int socfpga_gen10_set_phy_mode(struct socfpga_dwc_dev *dwc_dev)
}
-static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *priv)
+static int socfpga_dwc_probe_dt(struct device *dev,
+ struct socfpga_dwc_dev *priv)
{
u32 reg_offset, reg_shift;
int ret;
@@ -162,7 +163,7 @@ static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *pr
if (!IS_ENABLED(CONFIG_OFTREE))
return -ENODEV;
- ret = of_property_read_u32_index(dev->device_node, "altr,sysmgr-syscon",
+ ret = of_property_read_u32_index(dev->of_node, "altr,sysmgr-syscon",
1, &reg_offset);
if (ret) {
dev_err(dev, "Could not read reg_offset from sysmgr-syscon! Please update the devicetree.\n");
@@ -170,14 +171,15 @@ static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *pr
return -EINVAL;
}
- ret = of_property_read_u32_index(dev->device_node, "altr,sysmgr-syscon",
+ ret = of_property_read_u32_index(dev->of_node, "altr,sysmgr-syscon",
2, &reg_shift);
if (ret) {
dev_err(dev, "Could not read reg_shift from sysmgr-syscon! Please update the devicetree.\n");
return -EINVAL;
}
- priv->f2h_ptp_ref_clk = of_property_read_bool(dev->device_node, "altr,f2h_ptp_ref_clk");
+ priv->f2h_ptp_ref_clk = of_property_read_bool(dev->of_node,
+ "altr,f2h_ptp_ref_clk");
priv->reg_offset = reg_offset;
priv->reg_shift = reg_shift;
@@ -185,7 +187,7 @@ static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *pr
return 0;
}
-static int socfpga_dwc_ether_probe(struct device_d *dev)
+static int socfpga_dwc_ether_probe(struct device *dev)
{
struct socfpga_dwc_dev *dwc_dev;
struct dw_eth_dev *priv;
@@ -215,7 +217,7 @@ static int socfpga_dwc_ether_probe(struct device_d *dev)
dwc_dev->priv = priv;
- dwc_dev->sys_mgr_base = syscon_base_lookup_by_phandle(dev->device_node,
+ dwc_dev->sys_mgr_base = syscon_base_lookup_by_phandle(dev->of_node,
"altr,sysmgr-syscon");
if (IS_ERR(dwc_dev->sys_mgr_base)) {
dev_err(dev, "Could not get sysmgr-syscon node\n");
@@ -259,8 +261,9 @@ static __maybe_unused struct of_device_id socfpga_dwc_ether_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, socfpga_dwc_ether_compatible);
-static struct driver_d socfpga_dwc_ether_driver = {
+static struct driver socfpga_dwc_ether_driver = {
.name = "socfpga_designware_eth",
.probe = socfpga_dwc_ether_probe,
.remove = dwc_drv_remove,
diff --git a/drivers/net/designware_starfive.c b/drivers/net/designware_starfive.c
index 3dc9d14e11..aff2cc10e1 100644
--- a/drivers/net/designware_starfive.c
+++ b/drivers/net/designware_starfive.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <init.h>
#include <linux/reset.h>
+#include <linux/regmap.h>
#include <linux/clk.h>
#include <mfd/syscon.h>
#include <soc/starfive/sysmain.h>
@@ -56,7 +57,7 @@ static struct dw_eth_drvdata starfive_drvdata = {
.fix_mac_speed = dwmac_fixed_speed,
};
-static int starfive_dwc_ether_probe(struct device_d *dev)
+static int starfive_dwc_ether_probe(struct device *dev)
{
struct dw_eth_dev *dwc;
struct regmap *regmap;
@@ -67,7 +68,8 @@ static int starfive_dwc_ether_probe(struct device_d *dev)
{ .id = "tx" },
};
- regmap = syscon_regmap_lookup_by_phandle(dev->device_node, "starfive,sysmain");
+ regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "starfive,sysmain");
if (IS_ERR(regmap)) {
dev_err(dev, "Could not get starfive,sysmain node\n");
return PTR_ERR(regmap);
@@ -101,8 +103,9 @@ static struct of_device_id starfive_dwc_ether_compatible[] = {
{ .compatible = "starfive,stmmac", .data = &starfive_drvdata },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, starfive_dwc_ether_compatible);
-static struct driver_d starfive_dwc_ether_driver = {
+static struct driver starfive_dwc_ether_driver = {
.name = "starfive-designware_eth",
.probe = starfive_dwc_ether_probe,
.of_compatible = starfive_dwc_ether_compatible,
diff --git a/drivers/net/designware_stm32.c b/drivers/net/designware_stm32.c
index 43f2d0987c..54dabcc8d3 100644
--- a/drivers/net/designware_stm32.c
+++ b/drivers/net/designware_stm32.c
@@ -11,6 +11,7 @@
#include <net.h>
#include <linux/clk.h>
#include <mfd/syscon.h>
+#include <linux/regmap.h>
#include "designware_eqos.h"
@@ -112,9 +113,9 @@ static int eqos_set_mode_stm32(struct eqos_stm32 *priv, phy_interface_t interfac
return 0;
}
-static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos)
+static int eqos_init_stm32(struct device *dev, struct eqos *eqos)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct eqos_stm32 *priv = to_stm32(eqos);
struct clk_bulk_data *eth_ck;
int ret;
@@ -126,14 +127,14 @@ static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos)
priv->eth_ref_clk_sel_reg =
of_property_read_bool(np, "st,eth-ref-clk-sel");
- priv->regmap = syscon_regmap_lookup_by_phandle(dev->device_node,
+ priv->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"st,syscon");
if (IS_ERR(priv->regmap)) {
dev_err(dev, "Could not get st,syscon node\n");
return PTR_ERR(priv->regmap);
}
- ret = of_property_read_u32_index(dev->device_node, "st,syscon",
+ ret = of_property_read_u32_index(dev->of_node, "st,syscon",
1, &priv->mode_reg);
if (ret) {
dev_err(dev, "Can't get sysconfig mode offset (%s)\n",
@@ -177,12 +178,12 @@ static struct eqos_ops stm32_ops = {
.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
};
-static int eqos_probe_stm32(struct device_d *dev)
+static int eqos_probe_stm32(struct device *dev)
{
return eqos_probe(dev, &stm32_ops, xzalloc(sizeof(struct eqos_stm32)));
}
-static void eqos_remove_stm32(struct device_d *dev)
+static void eqos_remove_stm32(struct device *dev)
{
struct eqos_stm32 *priv = to_stm32(dev->priv);
@@ -196,8 +197,9 @@ static const struct of_device_id eqos_stm32_ids[] = {
{ .compatible = "st,stm32mp1-dwmac" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, eqos_stm32_ids);
-static struct driver_d eqos_stm32_driver = {
+static struct driver eqos_stm32_driver = {
.name = "eqos-stm32",
.probe = eqos_probe_stm32,
.remove = eqos_remove_stm32,
diff --git a/drivers/net/designware_tegra186.c b/drivers/net/designware_tegra186.c
index 0cbbdb46a4..86f97e853e 100644
--- a/drivers/net/designware_tegra186.c
+++ b/drivers/net/designware_tegra186.c
@@ -203,7 +203,7 @@ static int eqos_set_ethaddr_tegra186(struct eth_device *edev, const unsigned cha
return eqos_set_ethaddr(edev, mac);
}
-static int eqos_init_tegra186(struct device_d *dev, struct eqos *eqos)
+static int eqos_init_tegra186(struct device *dev, struct eqos *eqos)
{
struct eqos_tegra186 *priv = to_tegra186(eqos);
int phy_reset;
@@ -217,7 +217,7 @@ static int eqos_init_tegra186(struct device_d *dev, struct eqos *eqos)
return PTR_ERR(priv->rst);
}
- phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0);
+ phy_reset = of_get_named_gpio(dev->of_node, "phy-reset-gpios", 0);
if (gpio_is_valid(phy_reset)) {
ret = gpio_request(phy_reset, "phy-reset");
if (ret)
@@ -283,12 +283,12 @@ static const struct eqos_ops tegra186_ops = {
.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
};
-static int eqos_probe_tegra186(struct device_d *dev)
+static int eqos_probe_tegra186(struct device *dev)
{
return eqos_probe(dev, &tegra186_ops, xzalloc(sizeof(struct eqos_tegra186)));
}
-static void eqos_remove_tegra186(struct device_d *dev)
+static void eqos_remove_tegra186(struct device *dev)
{
struct eqos_tegra186 *priv = to_tegra186(dev->priv);
@@ -308,8 +308,9 @@ static const struct of_device_id eqos_tegra186_ids[] = {
{ .compatible = "nvidia,tegra186-eqos" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, eqos_tegra186_ids);
-static struct driver_d eqos_tegra186_driver = {
+static struct driver eqos_tegra186_driver = {
.name = "eqos-tegra186",
.probe = eqos_probe_tegra186,
.remove = eqos_remove_tegra186,
diff --git a/drivers/net/dm9k.c b/drivers/net/dm9k.c
index d183ab55c9..29defece4e 100644
--- a/drivers/net/dm9k.c
+++ b/drivers/net/dm9k.c
@@ -349,7 +349,7 @@ static int dm9k_phy_read(struct mii_bus *bus, int addr, int reg)
{
unsigned val;
struct dm9k *priv = bus->priv;
- struct device_d *dev = &bus->dev;
+ struct device *dev = &bus->dev;
/* only internal phy supported by now, so show only one phy on miibus */
if (addr != 0) {
@@ -373,7 +373,7 @@ static int dm9k_phy_read(struct mii_bus *bus, int addr, int reg)
static int dm9k_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
{
struct dm9k *priv = bus->priv;
- struct device_d *dev = &bus->dev;
+ struct device *dev = &bus->dev;
/* only internal phy supported by now, so show only one phy on miibus */
if (addr != 0) {
@@ -397,7 +397,7 @@ static int dm9k_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
static int dm9k_check_id(struct dm9k *priv)
{
- struct device_d *dev = priv->miibus.parent;
+ struct device *dev = priv->miibus.parent;
u32 id;
char c;
@@ -461,7 +461,7 @@ static void dm9k_enable(struct dm9k *priv)
static void dm9k_reset(struct dm9k *priv)
{
- struct device_d *dev = priv->miibus.parent;
+ struct device *dev = priv->miibus.parent;
dev_dbg(dev, "%s\n", __func__);
@@ -504,7 +504,7 @@ static void dm9k_write_length(struct dm9k *priv, unsigned length)
static int dm9k_wait_for_trans_end(struct dm9k *priv)
{
- struct device_d *dev = priv->miibus.parent;
+ struct device *dev = priv->miibus.parent;
static const uint64_t toffs = 1 * SECOND;
uint8_t status;
uint64_t start = get_time_ns();
@@ -530,7 +530,7 @@ static int dm9k_wait_for_trans_end(struct dm9k *priv)
static int dm9k_eth_send(struct eth_device *edev, void *packet, int length)
{
struct dm9k *priv = (struct dm9k *)edev->priv;
- struct device_d *dev = priv->miibus.parent;
+ struct device *dev = priv->miibus.parent;
dev_dbg(dev, "%s: %d bytes\n", __func__, length);
@@ -556,7 +556,7 @@ static int dm9k_eth_send(struct eth_device *edev, void *packet, int length)
static int dm9k_check_for_rx_packet(struct dm9k *priv)
{
uint8_t status;
- struct device_d *dev = priv->miibus.parent;
+ struct device *dev = priv->miibus.parent;
status = dm9k_ior(priv, DM9K_ISR);
if (!(status & ISR_PR))
@@ -568,7 +568,7 @@ static int dm9k_check_for_rx_packet(struct dm9k *priv)
static int dm9k_validate_entry(struct dm9k *priv)
{
- struct device_d *dev = priv->miibus.parent;
+ struct device *dev = priv->miibus.parent;
uint8_t p_stat;
/*
@@ -600,7 +600,7 @@ static int dm9k_validate_entry(struct dm9k *priv)
static int dm9k_eth_rx(struct eth_device *edev)
{
struct dm9k *priv = (struct dm9k *)edev->priv;
- struct device_d *dev = edev->parent;
+ struct device *dev = edev->parent;
unsigned rx_stat = 0, rx_len = 0;
bool p_valid;
@@ -720,9 +720,9 @@ static int dm9k_init_dev(struct eth_device *edev)
return 0;
}
-static int dm9000_parse_dt(struct device_d *dev, struct dm9k *priv)
+static int dm9000_parse_dt(struct device *dev, struct dm9k *priv)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
uint32_t prop;
if (!IS_ENABLED(CONFIG_OFDEVICE) || !np)
@@ -757,7 +757,7 @@ static int dm9000_parse_dt(struct device_d *dev, struct dm9k *priv)
return 0;
}
-static int dm9000_parse_pdata(struct device_d *dev, struct dm9k *priv)
+static int dm9000_parse_pdata(struct device *dev, struct dm9k *priv)
{
struct dm9000_platform_data *pdata = dev->platform_data;
@@ -768,7 +768,7 @@ static int dm9000_parse_pdata(struct device_d *dev, struct dm9k *priv)
return 0;
}
-static int dm9k_probe(struct device_d *dev)
+static int dm9k_probe(struct device *dev)
{
struct resource *iores;
unsigned io_mode;
@@ -870,8 +870,9 @@ static struct of_device_id dm9000_of_matches[] = {
{ .compatible = "davicom,dm9000", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dm9000_of_matches);
-static struct driver_d dm9k_driver = {
+static struct driver dm9k_driver = {
.name = "dm9000",
.probe = dm9k_probe,
.of_compatible = DRV_OF_COMPAT(dm9000_of_matches),
diff --git a/drivers/net/dsa.c b/drivers/net/dsa.c
index 7a64a79412..ccd7d87550 100644
--- a/drivers/net/dsa.c
+++ b/drivers/net/dsa.c
@@ -54,12 +54,12 @@ static int dsa_port_probe(struct eth_device *edev)
{
struct dsa_port *dp = edev->priv;
struct dsa_switch *ds = dp->ds;
- const struct dsa_ops *ops = ds->ops;
+ const struct dsa_switch_ops *ops = ds->ops;
phy_interface_t interface;
int ret;
if (ops->port_probe) {
- interface = of_get_phy_mode(dp->dev.device_node);
+ interface = of_get_phy_mode(dp->dev->of_node);
ret = ops->port_probe(dp, dp->index, interface);
if (ret)
return ret;
@@ -86,14 +86,14 @@ static int dsa_port_start(struct eth_device *edev)
{
struct dsa_port *dp = edev->priv;
struct dsa_switch *ds = dp->ds;
- const struct dsa_ops *ops = ds->ops;
+ const struct dsa_switch_ops *ops = ds->ops;
phy_interface_t interface;
int ret;
if (dp->enabled)
return -EBUSY;
- interface = of_get_phy_mode(dp->dev.device_node);
+ interface = of_get_phy_mode(dp->dev->of_node);
if (ops->port_pre_enable) {
/* In case of RMII interface we need to enable RMII clock
@@ -104,17 +104,13 @@ static int dsa_port_start(struct eth_device *edev)
return ret;
}
- ret = phy_device_connect(edev, ds->slave_mii_bus, dp->index, NULL, 0,
- interface);
+ ret = phy_device_connect(edev, ds->slave_mii_bus, dp->index,
+ ops->adjust_link, 0, interface);
if (ret)
return ret;
dsa_port_set_ethaddr(edev);
- ret = phy_wait_aneg_done(dp->edev.phydev);
- if (ret)
- return ret;
-
if (ops->port_enable) {
ret = ops->port_enable(dp, dp->index, dp->edev.phydev);
if (ret)
@@ -126,6 +122,16 @@ static int dsa_port_start(struct eth_device *edev)
if (!ds->cpu_port_users) {
struct dsa_port *dpc = ds->dp[ds->cpu_port];
+ if (ops->port_pre_enable) {
+ /* In case of RMII interface we need to enable RMII clock
+ * before talking to the PHY.
+ */
+ ret = ops->port_pre_enable(dpc, ds->cpu_port,
+ ds->cpu_port_fixed_phy->interface);
+ if (ret)
+ return ret;
+ }
+
if (ops->port_enable) {
ret = ops->port_enable(dpc, ds->cpu_port,
ds->cpu_port_fixed_phy);
@@ -133,6 +139,11 @@ static int dsa_port_start(struct eth_device *edev)
return ret;
}
+ ret = eth_set_promisc(ds->edev_master, true);
+ if (ret)
+ dev_warn(ds->dev, "Failed to set promisc mode. Using different eth addresses may not work. %pe\n",
+ ERR_PTR(ret));
+
eth_open(ds->edev_master);
}
@@ -146,7 +157,7 @@ static void dsa_port_stop(struct eth_device *edev)
{
struct dsa_port *dp = edev->priv;
struct dsa_switch *ds = dp->ds;
- const struct dsa_ops *ops = ds->ops;
+ const struct dsa_switch_ops *ops = ds->ops;
if (!dp->enabled)
return;
@@ -164,6 +175,7 @@ static void dsa_port_stop(struct eth_device *edev)
ops->port_disable(dpc, ds->cpu_port,
ds->cpu_port_fixed_phy);
+ eth_set_promisc(ds->edev_master, false);
eth_close(ds->edev_master);
}
}
@@ -172,7 +184,7 @@ static int dsa_port_send(struct eth_device *edev, void *packet, int length)
{
struct dsa_port *dp = edev->priv;
struct dsa_switch *ds = dp->ds;
- const struct dsa_ops *ops = ds->ops;
+ const struct dsa_switch_ops *ops = ds->ops;
void *tx_buf = ds->tx_buf;
size_t full_length, stuff = 0;
int ret;
@@ -233,35 +245,36 @@ static int dsa_ether_get_ethaddr(struct eth_device *edev, unsigned char *adr)
return edev_master->get_ethaddr(edev_master, adr);
}
-static int dsa_switch_register_edev(struct dsa_switch *ds,
- struct device_node *dn, int port)
+static struct dsa_port *dsa_port_alloc(struct dsa_switch *ds,
+ struct device_node *dn, int port)
{
- struct eth_device *edev;
- struct device_d *dev;
+ struct device *dev;
struct dsa_port *dp;
- int ret;
ds->dp[port] = xzalloc(sizeof(*dp));
-
dp = ds->dp[port];
- dev = &dp->dev;
- dev_set_name(dev, "dsa_port");
- dev->id = DEVICE_ID_DYNAMIC;
- dev->parent = ds->dev;
- dev->device_node = dn;
+ dev = of_platform_device_create(dn, ds->dev);
+ of_platform_device_dummy_drv(dev);
+ dp->dev = dev;
+ dp->ds = ds;
+ dp->index = port;
- ret = register_device(dev);
- if (ret)
- return ret;
+ return dp;
+}
+static int dsa_switch_register_edev(struct dsa_switch *ds,
+ struct device_node *dn, int port)
+{
+ struct eth_device *edev;
+ struct dsa_port *dp;
+
+ dp = dsa_port_alloc(ds, dn, port);
dp->rx_buf = xmalloc(DSA_PKTSIZE);
- dp->ds = ds;
- dp->index = port;
edev = &dp->edev;
edev->priv = dp;
- edev->parent = dev;
+ edev->parent = dp->dev;
edev->init = dsa_port_probe;
edev->open = dsa_port_start;
edev->send = dsa_port_send;
@@ -277,7 +290,7 @@ static int dsa_rx_preprocessor(struct eth_device *edev, unsigned char **packet,
int *length)
{
struct dsa_switch *ds = edev->rx_preprocessor_priv;
- const struct dsa_ops *ops = ds->ops;
+ const struct dsa_switch_ops *ops = ds->ops;
struct dsa_port *dp;
int ret, port;
@@ -314,7 +327,6 @@ static int dsa_switch_register_master(struct dsa_switch *ds,
{
struct device_node *phy_node;
struct phy_device *phydev;
- struct dsa_port *dp;
int ret;
if (ds->edev_master) {
@@ -345,9 +357,7 @@ static int dsa_switch_register_master(struct dsa_switch *ds,
phydev->interface = of_get_phy_mode(np);
- ds->dp[port] = xzalloc(sizeof(*dp));
- dp = ds->dp[port];
- dp->ds = ds;
+ dsa_port_alloc(ds, np, port);
ds->cpu_port = port;
ds->cpu_port_fixed_phy = phydev;
@@ -390,8 +400,11 @@ static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
}
master = of_parse_phandle(port, "ethernet", 0);
- if (master)
- dsa_switch_register_master(ds, port, master, reg);
+ if (master) {
+ ret = dsa_switch_register_master(ds, port, master, reg);
+ if (ret)
+ return ret;
+ }
}
/* Now we can register regular switch ports */
@@ -422,7 +435,7 @@ int dsa_register_switch(struct dsa_switch *ds)
return -ENODEV;
}
- if (!ds->dev->device_node)
+ if (!ds->dev->of_node)
return -ENODEV;
if (!ds->num_ports || ds->num_ports > DSA_MAX_PORTS) {
@@ -430,7 +443,7 @@ int dsa_register_switch(struct dsa_switch *ds)
return -EINVAL;
}
- ret = dsa_switch_parse_ports_of(ds, ds->dev->device_node);
+ ret = dsa_switch_parse_ports_of(ds, ds->dev->of_node);
if (ret)
return ret;
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index 3a5ee9988e..d440d7540a 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/e1000/e1000.h
@@ -2150,7 +2150,7 @@ struct e1000_hw {
struct eth_device edev;
struct pci_dev *pdev;
- struct device_d *dev;
+ struct device *dev;
void __iomem *hw_addr;
@@ -2163,7 +2163,7 @@ struct e1000_hw {
struct {
struct cdev cdev;
- struct device_d dev;
+ struct device dev;
int line;
} invm;
@@ -2183,7 +2183,9 @@ struct e1000_hw {
struct mii_bus miibus;
struct e1000_tx_desc *tx_base;
+ dma_addr_t tx_base_phys;
struct e1000_rx_desc *rx_base;
+ dma_addr_t rx_base_phys;
unsigned char *packet;
dma_addr_t packet_dma;
diff --git a/drivers/net/e1000/eeprom.c b/drivers/net/e1000/eeprom.c
index 1072bc54ef..effe0c6cff 100644
--- a/drivers/net/e1000/eeprom.c
+++ b/drivers/net/e1000/eeprom.c
@@ -410,8 +410,7 @@ static void e1000_eeprom_uses_microwire(struct e1000_eeprom_info *eeprom,
static size_t e1000_igb_get_flash_size(struct e1000_hw *hw)
{
- struct device_node *node =
- hw->pdev->dev.device_node;
+ struct device_node *node = hw->pdev->dev.of_node;
u32 flash_size;
uint32_t fla;
int ret = 0;
@@ -495,6 +494,7 @@ int32_t e1000_init_eeprom_params(struct e1000_hw *hw)
case e1000_82571:
case e1000_82572:
e1000_eeprom_uses_spi(eeprom, eecd);
+ eeprom->read = e1000_read_eeprom_eerd;
break;
case e1000_82573:
@@ -502,7 +502,6 @@ int32_t e1000_init_eeprom_params(struct e1000_hw *hw)
if (e1000_is_onboard_nvm_eeprom(hw)) {
e1000_eeprom_uses_spi(eeprom, eecd);
} else {
- eeprom->read = e1000_read_eeprom_eerd;
eeprom->type = e1000_eeprom_flash;
eeprom->word_size = 2048;
@@ -513,6 +512,7 @@ int32_t e1000_init_eeprom_params(struct e1000_hw *hw)
eecd &= ~E1000_EECD_AUPDEN;
e1000_write_reg(hw, E1000_EECD, eecd);
}
+ eeprom->read = e1000_read_eeprom_eerd;
break;
case e1000_80003es2lan:
@@ -800,7 +800,7 @@ int e1000_validate_eeprom_checksum(struct e1000_hw *hw)
/* Read the EEPROM */
if (e1000_read_eeprom(hw, 0, EEPROM_CHECKSUM_REG + 1, buf) < 0) {
- dev_err(&hw->edev.dev, "Unable to read EEPROM!\n");
+ dev_err(hw->dev, "Unable to read EEPROM!\n");
return -E1000_ERR_EEPROM;
}
@@ -816,8 +816,8 @@ int e1000_validate_eeprom_checksum(struct e1000_hw *hw)
return 0;
/* Hrm, verification failed, print an error */
- dev_err(&hw->edev.dev, "EEPROM checksum is incorrect!\n");
- dev_err(&hw->edev.dev, " ...register was 0x%04hx, calculated 0x%04hx\n",
+ dev_err(hw->dev, "EEPROM checksum is incorrect!\n");
+ dev_err(hw->dev, " ...register was 0x%04hx, calculated 0x%04hx\n",
checksum_reg, checksum);
return -E1000_ERR_EEPROM;
diff --git a/drivers/net/e1000/main.c b/drivers/net/e1000/main.c
index 363730de0a..76acea563e 100644
--- a/drivers/net/e1000/main.c
+++ b/drivers/net/e1000/main.c
@@ -3226,7 +3226,7 @@ static int e1000_sw_init(struct eth_device *edev)
/* identify the MAC */
result = e1000_set_mac_type(hw);
if (result) {
- dev_err(&hw->edev.dev, "Unknown MAC Type\n");
+ dev_err(hw->dev, "Unknown MAC Type\n");
return result;
}
@@ -3267,7 +3267,7 @@ static void e1000_configure_tx(struct e1000_hw *hw)
unsigned long tctl;
unsigned long tipg, tarc;
uint32_t ipgr1, ipgr2;
- const unsigned long tx_base = (unsigned long)hw->tx_base;
+ const unsigned long tx_base = (unsigned long)hw->tx_base_phys;
e1000_write_reg(hw, E1000_TDBAL, lower_32_bits(tx_base));
e1000_write_reg(hw, E1000_TDBAH, upper_32_bits(tx_base));
@@ -3386,7 +3386,7 @@ static void e1000_setup_rctl(struct e1000_hw *hw)
static void e1000_configure_rx(struct e1000_hw *hw)
{
unsigned long rctl, ctrl_ext;
- const unsigned long rx_base = (unsigned long)hw->rx_base;
+ const unsigned long rx_base = (unsigned long)hw->rx_base_phys;
hw->rx_tail = 0;
/* make sure receives are disabled while setting up the descriptors */
@@ -3438,12 +3438,12 @@ static int e1000_poll(struct eth_device *edev)
if (readb(&rd->status) & E1000_RXD_STAT_DD) {
const uint16_t len = readw(&rd->length);
- dma_sync_single_for_cpu(hw->packet_dma, len,
+ dma_sync_single_for_cpu(hw->dev, hw->packet_dma, len,
DMA_FROM_DEVICE);
net_receive(edev, hw->packet, len);
- dma_sync_single_for_device(hw->packet_dma, len,
+ dma_sync_single_for_device(hw->dev, hw->packet_dma, len,
DMA_FROM_DEVICE);
e1000_fill_rx(hw);
return 1;
@@ -3476,7 +3476,7 @@ static int e1000_transmit(struct eth_device *edev, void *txpacket, int length)
ret = readl_poll_timeout(&txp->upper.data,
stat, stat & E1000_TXD_STAT_DD,
- MSECOND / USECOND);
+ USEC_PER_MSEC);
if (ret)
dev_dbg(hw->dev, "e1000: tx timeout\n");
@@ -3595,8 +3595,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hw = xzalloc(sizeof(*hw));
- hw->tx_base = dma_alloc_coherent(16 * sizeof(*hw->tx_base), DMA_ADDRESS_BROKEN);
- hw->rx_base = dma_alloc_coherent(16 * sizeof(*hw->rx_base), DMA_ADDRESS_BROKEN);
+ hw->tx_base = dma_alloc_coherent(16 * sizeof(*hw->tx_base), &hw->tx_base_phys);
+ hw->rx_base = dma_alloc_coherent(16 * sizeof(*hw->rx_base), &hw->rx_base_phys);
edev = &hw->edev;
diff --git a/drivers/net/e1000/mtd.c b/drivers/net/e1000/mtd.c
index d472bd10a9..50883fc2a6 100644
--- a/drivers/net/e1000/mtd.c
+++ b/drivers/net/e1000/mtd.c
@@ -688,12 +688,12 @@ static int e1000_mtd_sr_rmw(struct mtd_info *mtd, u8 mask, u8 val)
*/
#define SR_BPALL (SR_BP0 | SR_BP1 | SR_BP2)
-static int e1000_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int e1000_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
return e1000_mtd_sr_rmw(mtd, SR_BPALL, SR_BPALL);
}
-static int e1000_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int e1000_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
return e1000_mtd_sr_rmw(mtd, SR_BPALL, 0x0);
}
diff --git a/drivers/net/efi-snp.c b/drivers/net/efi-snp.c
index 9cb7144187..476015f1c2 100644
--- a/drivers/net/efi-snp.c
+++ b/drivers/net/efi-snp.c
@@ -69,10 +69,10 @@ struct efi_simple_network_mode {
uint32_t ReceiveFilterSetting;
uint32_t MaxMCastFilterCount;
uint32_t MCastFilterCount;
- efi_mac_address MCastFilter[MAX_MCAST_FILTER_CNT];
- efi_mac_address CurrentAddress;
- efi_mac_address BroadcastAddress;
- efi_mac_address PermanentAddress;
+ struct efi_mac_address MCastFilter[MAX_MCAST_FILTER_CNT];
+ struct efi_mac_address CurrentAddress;
+ struct efi_mac_address BroadcastAddress;
+ struct efi_mac_address PermanentAddress;
uint8_t IfType;
bool MacAddressChangeable;
bool MultipleTxSupported;
@@ -92,14 +92,14 @@ struct efi_simple_network {
efi_status_t (EFIAPI *shutdown) (struct efi_simple_network *This);
efi_status_t (EFIAPI *receive_filters) (struct efi_simple_network *This,
uint32_t Enable, uint32_t Disable, bool ResetMCastFilter,
- unsigned long MCastFilterCnt, efi_mac_address *MCastFilter);
+ unsigned long MCastFilterCnt, struct efi_mac_address *MCastFilter);
efi_status_t (EFIAPI *station_address) (struct efi_simple_network *This,
- bool Reset, efi_mac_address *New);
+ bool Reset, struct efi_mac_address *New);
efi_status_t (EFIAPI *statistics) (struct efi_simple_network *This,
bool Reset, unsigned long *StatisticsSize,
struct efi_network_statistics *StatisticsTable);
efi_status_t (EFIAPI *mcast_ip_to_mac) (struct efi_simple_network *This,
- bool IPv6, efi_ip_address *IP, efi_mac_address *MAC);
+ bool IPv6, union efi_ip_address *IP, struct efi_mac_address *MAC);
efi_status_t (EFIAPI *nvdata) (struct efi_simple_network *This,
bool ReadWrite, unsigned long Offset, unsigned long BufferSize,
void *Buffer);
@@ -107,19 +107,20 @@ struct efi_simple_network {
uint32_t *InterruptStatus, void **TxBuf);
efi_status_t (EFIAPI *transmit) (struct efi_simple_network *This,
unsigned long HeaderSize, unsigned long BufferSize, void *Buffer,
- efi_mac_address *SrcAddr, efi_mac_address *DestAddr,
+ struct efi_mac_address *SrcAddr, struct efi_mac_address *DestAddr,
uint16_t *Protocol);
efi_status_t (EFIAPI *receive) (struct efi_simple_network *This,
unsigned long *HeaderSize, unsigned long *BufferSize, void *Buffer,
- efi_mac_address *SrcAddr, efi_mac_address *DestAddr, uint16_t *Protocol);
+ struct efi_mac_address *SrcAddr, struct efi_mac_address *DestAddr, uint16_t *Protocol);
void *WaitForPacket;
struct efi_simple_network_mode *Mode;
};
struct efi_snp_priv {
- struct device_d *dev;
+ struct device *dev;
struct eth_device edev;
struct efi_simple_network *snp;
+ void *rx_buf;
};
static inline struct efi_snp_priv *to_priv(struct eth_device *edev)
@@ -163,7 +164,7 @@ static int efi_snp_eth_rx(struct eth_device *edev)
long bufsize = PKTSIZE;
efi_status_t efiret;
- efiret = priv->snp->receive(priv->snp, NULL, &bufsize, NetRxPackets[0], NULL, NULL, NULL);
+ efiret = priv->snp->receive(priv->snp, NULL, &bufsize, priv->rx_buf, NULL, NULL, NULL);
if (efiret == EFI_NOT_READY)
return 0;
@@ -172,7 +173,7 @@ static int efi_snp_eth_rx(struct eth_device *edev)
return -efi_errno(efiret);
}
- net_receive(edev, NetRxPackets[0], bufsize);
+ net_receive(edev, priv->rx_buf, bufsize);
return 0;
}
@@ -219,7 +220,7 @@ static int efi_snp_eth_open(struct eth_device *edev)
}
efiret = priv->snp->station_address(priv->snp, false,
- (efi_mac_address *)priv->snp->Mode->PermanentAddress.Addr );
+ (struct efi_mac_address *)priv->snp->Mode->PermanentAddress.Addr );
if (EFI_ERROR(efiret)) {
dev_err(priv->dev, "failed to set MAC address: %s\n",
efi_strerror(efiret));
@@ -285,6 +286,7 @@ static int efi_snp_probe(struct efi_device *efidev)
priv = xzalloc(sizeof(struct efi_snp_priv));
priv->snp = efidev->protocol;
priv->dev = &efidev->dev;
+ priv->rx_buf = xmalloc(PKTSIZE);
dev_dbg(&efidev->dev, "perm: %02x:%02x:%02x:%02x:%02x:%02x\n",
priv->snp->Mode->PermanentAddress.Addr[0],
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index b3cfb65c1c..9455c6f5ea 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -46,6 +46,7 @@ struct enc28j60_net {
/* store MAC address here while hardware is in the reset state */
u8 hwaddr[ETH_ALEN];
struct mii_bus miibus;
+ void *rx_buffer;
};
/*
@@ -670,7 +671,7 @@ static void enc28j60_hw_disable(struct enc28j60_net *priv)
static inline void enc28j60_dump_rsv(struct enc28j60_net *priv, const char *msg,
u16 pk_ptr, int len, u16 sts)
{
- struct device_d *dev = &priv->edev.dev;
+ struct device *dev = &priv->edev.dev;
dev_dbg(dev, "%s - NextPk: 0x%04x - RSV:\n",
msg, pk_ptr);
@@ -793,9 +794,9 @@ static void enc28j60_hw_rx(struct eth_device *edev)
/* copy the packet from the receive buffer */
enc28j60_mem_read(priv,
rx_packet_start(priv->next_pk_ptr),
- len, NetRxPackets[0]);
+ len, priv->rx_buffer);
- net_receive(edev, NetRxPackets[0], len);
+ net_receive(edev, priv->rx_buffer, len);
}
/*
@@ -922,7 +923,7 @@ static void enc28j60_eth_halt(struct eth_device *edev)
enc28j60_lowpower(priv, true);
}
-static int enc28j60_probe(struct device_d *dev)
+static int enc28j60_probe(struct device *dev)
{
struct eth_device *edev;
struct enc28j60_net *priv;
@@ -931,6 +932,7 @@ static int enc28j60_probe(struct device_d *dev)
priv = xzalloc(sizeof(*priv));
priv->spi = (struct spi_device *)dev->type_data;
+ priv->rx_buffer = net_alloc_packet();
edev = &priv->edev;
edev->priv = priv;
@@ -998,8 +1000,9 @@ static __maybe_unused struct of_device_id enc28j60_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, enc28j60_dt_ids);
-static struct driver_d enc28j60_driver = {
+static struct driver enc28j60_driver = {
.name = DRV_NAME,
.probe = enc28j60_probe,
.of_compatible = DRV_OF_COMPAT(enc28j60_dt_ids),
diff --git a/drivers/net/ep93xx.c b/drivers/net/ep93xx.c
index f8b281093d..bd954e7a17 100644
--- a/drivers/net/ep93xx.c
+++ b/drivers/net/ep93xx.c
@@ -21,7 +21,7 @@
#include <malloc.h>
#include <io.h>
#include <linux/types.h>
-#include <mach/ep93xx-regs.h>
+#include <mach/ep93xx/ep93xx-regs.h>
#include <linux/phy.h>
#include <platform_data/eth-ep93xx.h>
#include "ep93xx.h"
@@ -62,7 +62,7 @@ static void dump_dev(struct eth_device *edev)
printf(" rx_sq.end %p\n", priv->rx_sq.end);
for (i = 0; i < NUMRXDESC; i++)
- printf(" rx_buffer[%2.d] %p\n", i, NetRxPackets[i]);
+ printf(" rx_buffer[%2.d] %p\n", i, priv->rx_buffer[i]);
printf(" tx_dq.base %p\n", priv->tx_dq.base);
printf(" tx_dq.current %p\n", priv->tx_dq.current);
@@ -258,7 +258,7 @@ static int ep93xx_eth_open(struct eth_device *edev)
*/
for (i = 0; i < NUMRXDESC; i++) {
/* set buffer address */
- (priv->rx_dq.base + i)->word1 = (uint32_t)NetRxPackets[i];
+ (priv->rx_dq.base + i)->word1 = (uint32_t)priv->rx_buffer[i];
/* set buffer length, clear buffer index and NSOF */
(priv->rx_dq.base + i)->word2 = EP93XX_MAX_PKT_SIZE;
@@ -324,7 +324,7 @@ static int ep93xx_eth_rcv_packet(struct eth_device *edev)
/*
* We have a good frame. Extract the frame's length
* from the current rx_status_queue entry, and copy
- * the frame's data into NetRxPackets[] of the
+ * the frame's data into priv->rx_buffer of the
* protocol stack. We track the total number of
* bytes in the frame (nbytes_frame) which will be
* used when we pass the data off to the protocol
@@ -466,7 +466,7 @@ static int ep93xx_eth_set_ethaddr(struct eth_device *edev,
return 0;
}
-static int ep93xx_eth_probe(struct device_d *dev)
+static int ep93xx_eth_probe(struct device *dev)
{
struct ep93xx_eth_platform_data *pdata = (struct ep93xx_eth_platform_data *)dev->platform_data;
struct eth_device *edev;
@@ -532,6 +532,12 @@ static int ep93xx_eth_probe(struct device_d *dev)
goto eth_probe_failed_3;
}
+ ret = net_alloc_packets(priv->rx_buffer, NUMRXDESC);
+ if (ret) {
+ pr_err("net_alloc_packet() failed: rx_buffer");
+ goto eth_probe_failed_4;
+ }
+
mdiobus_register(&priv->miibus);
eth_register(edev);
@@ -539,6 +545,10 @@ static int ep93xx_eth_probe(struct device_d *dev)
goto eth_probe_done;
+eth_probe_failed_4:
+ free(priv->rx_sq.base);
+ /* Fall through */
+
eth_probe_failed_3:
free(priv->rx_dq.base);
/* Fall through */
@@ -650,7 +660,7 @@ static int ep93xx_phy_write(struct mii_bus *bus, int phy_addr,
return 0;
}
-static struct driver_d ep93xx_eth_driver = {
+static struct driver ep93xx_eth_driver = {
.name = "ep93xx_eth",
.probe = ep93xx_eth_probe,
};
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index f24120ce72..a31d3bb521 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -178,6 +178,8 @@ struct ethoc {
u32 cur_rx;
struct mii_bus miibus;
+
+ void *rx_buffer[PKTBUFSRX];
};
/**
@@ -266,7 +268,7 @@ static int ethoc_init_ring(struct ethoc *dev)
if (i == dev->num_rx - 1)
bd.stat |= RX_BD_WRAP;
- bd.addr = (u32)NetRxPackets[i];
+ bd.addr = (u32)dev->rx_buffer[i];
ethoc_write_bd(dev, dev->num_tx + i, &bd);
flush_dcache_range(bd.addr, bd.addr + PKTSIZE);
@@ -529,17 +531,23 @@ static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
return 0;
}
-static int ethoc_probe(struct device_d *dev)
+static int ethoc_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
struct ethoc *priv;
+ int ret;
edev = xzalloc(sizeof(struct eth_device) +
sizeof(struct ethoc));
edev->priv = (struct ethoc *)(edev + 1);
priv = edev->priv;
+
+ ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer));
+ if (ret)
+ return ret;
+
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -571,8 +579,9 @@ static struct of_device_id ethoc_dt_ids[] = {
{ .compatible = "opencores,ethoc", },
{ }
};
+MODULE_DEVICE_TABLE(of, ethoc_dt_ids);
-static struct driver_d ethoc_driver = {
+static struct driver ethoc_driver = {
.name = "ethoc",
.probe = ethoc_probe,
.of_compatible = DRV_OF_COMPAT(ethoc_dt_ids),
diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c
index 673555a48a..75a6596282 100644
--- a/drivers/net/fec_imx.c
+++ b/drivers/net/fec_imx.c
@@ -25,6 +25,22 @@
#include "fec_imx.h"
+static int fec_set_promisc(struct eth_device *edev, bool enable)
+{
+ struct fec_priv *fec = (struct fec_priv *)edev->priv;
+ u32 rcntl;
+
+ rcntl = readl(fec->regs + FEC_R_CNTRL);
+
+ if (enable)
+ rcntl |= FEC_R_CNTRL_PROMISC;
+ else
+ rcntl &= ~FEC_R_CNTRL_PROMISC;
+
+ writel(rcntl, fec->regs + FEC_R_CNTRL);
+
+ return 0;
+}
/*
* MII-interface related functions
@@ -257,10 +273,15 @@ static int fec_init(struct eth_device *dev)
*/
writel(0x00000000, fec->regs + FEC_IMASK);
+ rcntl = readl(fec->regs + FEC_R_CNTRL);
+
+ /* Keep promisc setting */
+ rcntl &= FEC_R_CNTRL_PROMISC;
+
/*
* Set FEC-Lite receive control register(R_CNTRL):
*/
- rcntl = FEC_R_CNTRL_MAX_FL(1518);
+ rcntl |= FEC_R_CNTRL_MAX_FL(1518);
rcntl |= FEC_R_CNTRL_MII_MODE;
/*
@@ -568,7 +589,7 @@ static int fec_recv(struct eth_device *dev)
* fixup and net_receive below would get
* proper data
*/
- dma_sync_single_for_cpu((unsigned long)frame,
+ dma_sync_single_for_cpu(fec->dev, (unsigned long)frame,
data_length,
DMA_FROM_DEVICE);
if (fec_is_imx28(fec))
@@ -580,7 +601,7 @@ static int fec_recv(struct eth_device *dev)
*/
len = data_length - 4;
net_receive(dev, frame, len);
- dma_sync_single_for_device((unsigned long)frame,
+ dma_sync_single_for_device(fec->dev, (unsigned long)frame,
data_length,
DMA_FROM_DEVICE);
}
@@ -631,25 +652,25 @@ static void fec_free_receive_packets(struct fec_priv *fec, int count, int size)
}
#ifdef CONFIG_OFDEVICE
-static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec)
+static int fec_probe_dt(struct device *dev, struct fec_priv *fec)
{
struct device_node *mdiobus;
int ret;
- ret = of_get_phy_mode(dev->device_node);
+ ret = of_get_phy_mode(dev->of_node);
if (ret < 0)
fec->interface = PHY_INTERFACE_MODE_MII;
else
fec->interface = ret;
- mdiobus = of_get_child_by_name(dev->device_node, "mdio");
+ mdiobus = of_get_child_by_name(dev->of_node, "mdio");
if (mdiobus)
- fec->miibus.dev.device_node = mdiobus;
+ fec->miibus.dev.of_node = mdiobus;
return 0;
}
#else
-static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec)
+static int fec_probe_dt(struct device *dev, struct fec_priv *fec)
{
return -ENODEV;
}
@@ -736,7 +757,7 @@ static int fec_clk_get(struct fec_priv *fec)
return err;
}
-static int fec_probe(struct device_d *dev)
+static int fec_probe(struct device *dev)
{
struct resource *iores;
struct fec_platform_data *pdata = (struct fec_platform_data *)dev->platform_data;
@@ -768,6 +789,7 @@ static int fec_probe(struct device_d *dev)
edev->halt = fec_halt;
edev->get_ethaddr = fec_get_hwaddr;
edev->set_ethaddr = fec_set_hwaddr;
+ edev->set_promisc = fec_set_promisc;
edev->parent = dev;
dma_set_mask(dev, DMA_BIT_MASK(32));
@@ -803,10 +825,11 @@ static int fec_probe(struct device_d *dev)
goto release_res;
}
- phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0);
+ phy_reset = of_get_named_gpio(dev->of_node, "phy-reset-gpios", 0);
if (gpio_is_valid(phy_reset)) {
- of_property_read_u32(dev->device_node, "phy-reset-duration", &msec);
- of_property_read_u32(dev->device_node, "phy-reset-post-delay",
+ of_property_read_u32(dev->of_node, "phy-reset-duration",
+ &msec);
+ of_property_read_u32(dev->of_node, "phy-reset-post-delay",
&phy_post_delay);
/* valid reset duration should be less than 1s */
if (phy_post_delay > 1000)
@@ -834,6 +857,8 @@ static int fec_probe(struct device_d *dev)
if (ret)
goto free_gpio;
+ fec_set_promisc(edev, false);
+
/*
* reserve memory for both buffer descriptor chains at once
* Datasheet forces the startaddress of each chain is 16 byte aligned
@@ -849,7 +874,7 @@ static int fec_probe(struct device_d *dev)
if (ret < 0)
goto free_xbd;
- if (dev->device_node) {
+ if (dev->of_node) {
ret = fec_probe_dt(dev, fec);
fec->phy_addr = -1;
} else if (pdata) {
@@ -870,18 +895,18 @@ static int fec_probe(struct device_d *dev)
fec->miibus.priv = fec;
fec->miibus.parent = dev;
- ret = mdiobus_register(&fec->miibus);
+ ret = eth_register(edev);
if (ret)
goto free_receive_packets;
- ret = eth_register(edev);
+ ret = mdiobus_register(&fec->miibus);
if (ret)
- goto unregister_mdio;
+ goto unregister_eth;
return 0;
-unregister_mdio:
- mdiobus_unregister(&fec->miibus);
+unregister_eth:
+ eth_unregister(edev);
free_receive_packets:
fec_free_receive_packets(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE);
free_xbd:
@@ -903,7 +928,7 @@ err_free:
return ret;
}
-static void fec_remove(struct device_d *dev)
+static void fec_remove(struct device *dev)
{
struct fec_priv *fec = dev->priv;
@@ -936,6 +961,7 @@ static __maybe_unused struct of_device_id imx_fec_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_fec_dt_ids);
static struct platform_device_id imx_fec_ids[] = {
{
@@ -955,7 +981,7 @@ static struct platform_device_id imx_fec_ids[] = {
/**
* Driver description for registering
*/
-static struct driver_d fec_driver = {
+static struct driver fec_driver = {
.name = "fec_imx",
.probe = fec_probe,
.remove = fec_remove,
diff --git a/drivers/net/fec_imx.h b/drivers/net/fec_imx.h
index 316eefe48f..1aaff87fdd 100644
--- a/drivers/net/fec_imx.h
+++ b/drivers/net/fec_imx.h
@@ -58,6 +58,7 @@
#define FEC_R_CNTRL_RMII_10T (1 << 9) /* i.MX28 specific */
#define FEC_R_CNTRL_RMII_MODE (1 << 8) /* i.MX28 specific */
#define FEC_R_CNTRL_FCE (1 << 5)
+#define FEC_R_CNTRL_PROMISC (1 << 3)
#define FEC_R_CNTRL_MII_MODE (1 << 2)
#define FEC_IEVENT_HBERR 0x80000000 /* Note: Not on i.MX28 */
@@ -138,7 +139,7 @@ enum fec_opt_clock {
*/
struct fec_priv {
struct eth_device edev;
- struct device_d *dev;
+ struct device *dev;
void __iomem *regs;
struct buffer_descriptor __iomem *rbd_base; /* RBD ring */
int rbd_index; /* next receive BD to read */
diff --git a/drivers/net/fec_mpc5200.c b/drivers/net/fec_mpc5200.c
index 4025664d0d..9c9b795f14 100644
--- a/drivers/net/fec_mpc5200.c
+++ b/drivers/net/fec_mpc5200.c
@@ -420,7 +420,8 @@ static void mpc5xxx_fec_halt(struct eth_device *dev)
/*
* wait for graceful stop to register
*/
- while ((counter--) && (!(fec->eth->ievent & FEC_IEVENT_GRA))) ;
+ while ((counter--) && (!(fec->eth->ievent & FEC_IEVENT_GRA)))
+ ;
/*
* Disable SmartDMA tasks
@@ -637,7 +638,7 @@ static int mpc5xxx_fec_recv(struct eth_device *dev)
return len;
}
-static int mpc5xxx_fec_probe(struct device_d *dev)
+static int mpc5xxx_fec_probe(struct device *dev)
{
struct resource *iores;
struct fec_platform_data *pdata = dev->platform_data;
@@ -680,14 +681,14 @@ static int mpc5xxx_fec_probe(struct device_d *dev)
return 0;
}
-static void mpc5xxx_fec_remove(struct device_d *dev)
+static void mpc5xxx_fec_remove(struct device *dev)
{
struct eth_device *edev = dev->priv;
mpc5xxx_fec_halt(edev);
}
-static struct driver_d mpc5xxx_driver = {
+static struct driver mpc5xxx_driver = {
.name = "fec_mpc5xxx",
.probe = mpc5xxx_fec_probe,
.remove = mpc5xxx_fec_remove,
diff --git a/drivers/net/fsl-fman.c b/drivers/net/fsl-fman.c
index 4d72933105..bdb2359815 100644
--- a/drivers/net/fsl-fman.c
+++ b/drivers/net/fsl-fman.c
@@ -101,7 +101,7 @@ struct fm_eth {
struct fm_bmi_rx_port *rx_port;
phy_interface_t enet_if;
struct eth_device edev;
- struct device_d *dev;
+ struct device *dev;
struct fm_port_global_pram *rx_pram; /* Rx parameter table */
struct fm_port_global_pram *tx_pram; /* Tx parameter table */
struct fm_port_bd *rx_bd_ring; /* Rx BD ring base */
@@ -207,7 +207,7 @@ static int fm_upload_ucode(struct fm_imem *imem,
return 0;
}
-static int fman_upload_firmware(struct device_d *dev, struct fm_imem *fm_imem)
+static int fman_upload_firmware(struct device *dev, struct fm_imem *fm_imem)
{
int i, size, ret;
const struct qe_firmware *firmware;
@@ -398,7 +398,7 @@ static void fm_init_qmi(struct fm_qmi_common *qmi)
out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL);
}
-static int fm_init_common(struct device_d *dev, struct ccsr_fman *reg)
+static int fm_init_common(struct device *dev, struct ccsr_fman *reg)
{
int ret;
@@ -583,7 +583,6 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
void *rx_bd_ring_base;
void *rx_buf_pool;
u32 bd_ring_base_lo, bd_ring_base_hi;
- u32 buf_lo, buf_hi;
struct fm_port_bd *rxbd;
struct fm_port_qd *rxqd;
struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port;
@@ -620,11 +619,7 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
* RX_BD_RING_SIZE);
/* alloc Rx buffer from main memory */
- rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
- if (!rx_buf_pool)
- return -ENOMEM;
-
- memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+ rx_buf_pool = dma_zalloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
/* save them to fm_eth */
fm_eth->rx_bd_ring = rx_bd_ring_base;
@@ -633,18 +628,21 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
/* init Rx BDs ring */
for (i = 0; i < RX_BD_RING_SIZE; i++) {
+ dma_addr_t dma;
+
rxbd = &fm_eth->rx_bd_ring[i];
muram_writew(&rxbd->status, RxBD_EMPTY);
muram_writew(&rxbd->len, 0);
- buf_hi = upper_32_bits(virt_to_phys(rx_buf_pool +
- i * MAX_RXBUF_LEN));
- buf_lo = lower_32_bits(virt_to_phys(rx_buf_pool +
- i * MAX_RXBUF_LEN));
- dma_sync_single_for_device((unsigned long)rx_buf_pool + i * MAX_RXBUF_LEN,
- MAX_RXBUF_LEN, DMA_FROM_DEVICE);
- muram_writew(&rxbd->buf_ptr_hi, (u16)buf_hi);
- out_be32(&rxbd->buf_ptr_lo, buf_lo);
+
+ dma = dma_map_single(fm_eth->dev,
+ rx_buf_pool + i * MAX_RXBUF_LEN,
+ MAX_RXBUF_LEN, DMA_FROM_DEVICE);
+ if (dma_mapping_error(fm_eth->dev, dma))
+ return -EFAULT;
+
+ muram_writew(&rxbd->buf_ptr_hi, (u16)upper_32_bits(dma));
+ out_be32(&rxbd->buf_ptr_lo, lower_32_bits(dma));
}
/* set the Rx queue descriptor */
@@ -911,13 +909,13 @@ static int fm_eth_recv(struct eth_device *edev)
data = (u8 *)((unsigned long)(buf_hi << 16) << 16 | buf_lo);
len = muram_readw(&rxbd->len);
- dma_sync_single_for_cpu((unsigned long)data,
+ dma_sync_single_for_cpu(fm_eth->dev, (unsigned long)data,
len,
DMA_FROM_DEVICE);
net_receive(edev, data, len);
- dma_sync_single_for_device((unsigned long)data,
+ dma_sync_single_for_device(fm_eth->dev, (unsigned long)data,
len,
DMA_FROM_DEVICE);
} else {
@@ -1058,7 +1056,7 @@ static int fm_eth_startup(struct fm_eth *fm_eth)
return 0;
}
-static int fsl_fman_mdio_probe(struct device_d *dev)
+static int fsl_fman_mdio_probe(struct device *dev)
{
struct resource *iores;
int ret;
@@ -1087,7 +1085,7 @@ static int fsl_fman_mdio_probe(struct device_d *dev)
return 0;
}
-static int fsl_fman_port_probe(struct device_d *dev)
+static int fsl_fman_port_probe(struct device *dev)
{
struct resource *iores;
int ret;
@@ -1121,15 +1119,15 @@ static int fsl_fman_port_probe(struct device_d *dev)
static int fsl_fman_memac_port_bind(struct fm_eth *fm_eth, enum fman_port_type type)
{
- struct device_node *macnp = fm_eth->dev->device_node;
+ struct device_node *macnp = fm_eth->dev->of_node;
struct device_node *portnp;
- struct device_d *portdev;
+ struct device *portdev;
struct fsl_fman_port *port;
portnp = of_parse_phandle(macnp, "fsl,fman-ports", type);
if (!portnp) {
- dev_err(fm_eth->dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n",
- macnp->full_name);
+ dev_err(fm_eth->dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n",
+ macnp);
return -EINVAL;
}
@@ -1149,7 +1147,7 @@ static int fsl_fman_memac_port_bind(struct fm_eth *fm_eth, enum fman_port_type t
return 0;
}
-static int fsl_fman_memac_probe(struct device_d *dev)
+static int fsl_fman_memac_probe(struct device *dev)
{
struct resource *iores;
struct fm_eth *fm_eth;
@@ -1178,7 +1176,7 @@ static int fsl_fman_memac_probe(struct device_d *dev)
if (ret)
return ret;
- phy_mode = of_get_phy_mode(dev->device_node);
+ phy_mode = of_get_phy_mode(dev->of_node);
if (phy_mode < 0)
return phy_mode;
@@ -1209,14 +1207,14 @@ static int fsl_fman_memac_probe(struct device_d *dev)
return 0;
}
-static void fsl_fman_memac_remove(struct device_d *dev)
+static void fsl_fman_memac_remove(struct device *dev)
{
struct fm_eth *fm_eth = dev->priv;
fm_eth_halt(&fm_eth->edev);
}
-static int fsl_fman_muram_probe(struct device_d *dev)
+static int fsl_fman_muram_probe(struct device *dev)
{
struct resource *iores;
@@ -1240,8 +1238,9 @@ static struct of_device_id fsl_fman_mdio_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, fsl_fman_mdio_dt_ids);
-static struct driver_d fman_mdio_driver = {
+static struct driver fman_mdio_driver = {
.name = "fsl-fman-mdio",
.probe = fsl_fman_mdio_probe,
.of_compatible = DRV_OF_COMPAT(fsl_fman_mdio_dt_ids),
@@ -1257,8 +1256,9 @@ static struct of_device_id fsl_fman_port_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, fsl_fman_port_dt_ids);
-static struct driver_d fman_port_driver = {
+static struct driver fman_port_driver = {
.name = "fsl-fman-port",
.probe = fsl_fman_port_probe,
.of_compatible = DRV_OF_COMPAT(fsl_fman_port_dt_ids),
@@ -1270,8 +1270,9 @@ static struct of_device_id fsl_fman_memac_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, fsl_fman_memac_dt_ids);
-static struct driver_d fman_memac_driver = {
+static struct driver fman_memac_driver = {
.name = "fsl-fman-memac",
.probe = fsl_fman_memac_probe,
.remove = fsl_fman_memac_remove,
@@ -1284,14 +1285,15 @@ static struct of_device_id fsl_fman_muram_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, fsl_fman_muram_dt_ids);
-static struct driver_d fman_muram_driver = {
+static struct driver fman_muram_driver = {
.name = "fsl-fman-muram",
.probe = fsl_fman_muram_probe,
.of_compatible = DRV_OF_COMPAT(fsl_fman_muram_dt_ids),
};
-static int fsl_fman_probe(struct device_d *dev)
+static int fsl_fman_probe(struct device *dev)
{
struct resource *iores;
struct ccsr_fman *reg;
@@ -1306,7 +1308,7 @@ static int fsl_fman_probe(struct device_d *dev)
reg = IOMEM(iores->start);
dev->priv = reg;
- ret = of_platform_populate(dev->device_node, NULL, dev);
+ ret = of_platform_populate(dev->of_node, NULL, dev);
if (ret)
return ret;
@@ -1328,8 +1330,9 @@ static struct of_device_id fsl_fman_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, fsl_fman_dt_ids);
-static struct driver_d fman_driver = {
+static struct driver fman_driver = {
.name = "fsl-fman",
.probe = fsl_fman_probe,
.of_compatible = DRV_OF_COMPAT(fsl_fman_dt_ids),
@@ -1342,7 +1345,12 @@ static int fman_of_fixup(struct device_node *root, void *context)
struct device_node *child, *child_bb;
fman_bb = of_find_compatible_node(NULL, NULL, "fsl,fman");
+ if (!fman_bb)
+ return 0;
+
fman = of_find_compatible_node(root, NULL, "fsl,fman");
+ if (!fman)
+ return 0;
/*
* The dts files in the Linux tree have all network interfaces
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c
new file mode 100644
index 0000000000..4812ed4363
--- /dev/null
+++ b/drivers/net/fsl_enetc.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ENETC ethernet controller driver
+ * Copyright 2017-2021 NXP
+ */
+#include <common.h>
+#include <dma.h>
+#include <net.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+#include <io.h>
+#include <linux/mdio.h>
+#include <asm/system.h>
+#include <of_net.h>
+#include <asm/unaligned.h>
+
+#include "fsl_enetc.h"
+
+/* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */
+static __maybe_unused int enetc_mdio_read(struct enetc_priv *priv, int addr, int devad, int reg)
+{
+ struct enetc_mdio_priv mdio_priv;
+
+ mdio_priv.regs_base = priv->port_regs + ENETC_PM_IMDIO_BASE;
+
+ return enetc_mdio_read_priv(&mdio_priv, addr, devad, reg);
+}
+
+static int enetc_mdio_write(struct enetc_priv *priv, int addr, int devad, int reg,
+ u16 val)
+{
+ struct enetc_mdio_priv mdio_priv;
+ int ret;
+
+ mdio_priv.regs_base = priv->port_regs + ENETC_PM_IMDIO_BASE;
+
+ ret = enetc_mdio_write_priv(&mdio_priv, addr, devad, reg, val);
+
+ return ret;
+}
+
+/* only interfaces that can pin out through serdes have internal MDIO */
+static bool enetc_has_imdio(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+
+ return !!(enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO);
+}
+
+/* set up serdes for SGMII */
+static int enetc_init_sgmii(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+ bool is2500 = false;
+ u16 reg;
+
+ if (!enetc_has_imdio(edev))
+ return 0;
+
+ if (priv->uclass_id == PHY_INTERFACE_MODE_2500BASEX)
+ is2500 = true;
+
+ /*
+ * Set to SGMII mode, for 1Gbps enable AN, for 2.5Gbps set fixed speed.
+ * Although fixed speed is 1Gbps, we could be running at 2.5Gbps based
+ * on PLL configuration. Setting 1G for 2.5G here is counter intuitive
+ * but intentional.
+ */
+ reg = ENETC_PCS_IF_MODE_SGMII;
+ reg |= is2500 ? ENETC_PCS_IF_MODE_SPEED_1G : ENETC_PCS_IF_MODE_SGMII_AN;
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+ ENETC_PCS_IF_MODE, reg);
+
+ /* Dev ability - SGMII */
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+ ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII);
+
+ /* Adjust link timer for SGMII */
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+ ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL);
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+ ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
+
+ reg = ENETC_PCS_CR_DEF_VAL;
+ reg |= is2500 ? ENETC_PCS_CR_RST : ENETC_PCS_CR_RESET_AN;
+ /* restart PCS AN */
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
+ ENETC_PCS_CR, reg);
+
+ return 0;
+}
+
+/* set up MAC for RGMII */
+static void enetc_init_rgmii(struct eth_device *edev, struct phy_device *phydev)
+{
+ struct enetc_priv *priv = edev->priv;
+ u32 old_val, val;
+
+ old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE);
+
+ /* disable unreliable RGMII in-band signaling and force the MAC into
+ * the speed negotiated by the PHY.
+ */
+ val &= ~ENETC_PM_IF_MODE_AN_ENA;
+
+ if (phydev->speed == SPEED_1000) {
+ val &= ~ENETC_PM_IFM_SSP_MASK;
+ val |= ENETC_PM_IFM_SSP_1000;
+ } else if (phydev->speed == SPEED_100) {
+ val &= ~ENETC_PM_IFM_SSP_MASK;
+ val |= ENETC_PM_IFM_SSP_100;
+ } else if (phydev->speed == SPEED_10) {
+ val &= ~ENETC_PM_IFM_SSP_MASK;
+ val |= ENETC_PM_IFM_SSP_10;
+ }
+
+ if (phydev->duplex == DUPLEX_FULL)
+ val |= ENETC_PM_IFM_FULL_DPX;
+ else
+ val &= ~ENETC_PM_IFM_FULL_DPX;
+
+ if (val == old_val)
+ return;
+
+ enetc_write_port(priv, ENETC_PM_IF_MODE, val);
+}
+
+/* set up MAC configuration for the given interface type */
+static void enetc_setup_mac_iface(struct eth_device *edev,
+ struct phy_device *phydev)
+{
+ struct enetc_priv *priv = edev->priv;
+ u32 if_mode;
+
+ switch (priv->uclass_id) {
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ enetc_init_rgmii(edev, phydev);
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
+ /* set ifmode to (US)XGMII */
+ if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
+ if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
+ enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+ break;
+ };
+}
+
+/* set up serdes for SXGMII */
+static int enetc_init_sxgmii(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+
+ if (!enetc_has_imdio(edev))
+ return 0;
+
+ /* Dev ability - SXGMII */
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
+ ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII);
+
+ /* Restart PCS AN */
+ enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
+ ENETC_PCS_CR,
+ ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN);
+
+ return 0;
+}
+
+/* Apply protocol specific configuration to MAC, serdes as needed */
+static void enetc_start_pcs(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+
+ priv->uclass_id = of_get_phy_mode(priv->dev->of_node);
+ if (priv->uclass_id == PHY_INTERFACE_MODE_NA) {
+ dev_dbg(&edev->dev,
+ "phy-mode property not found, defaulting to SGMII\n");
+ priv->uclass_id = PHY_INTERFACE_MODE_SGMII;
+ }
+
+ switch (priv->uclass_id) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ enetc_init_sgmii(edev);
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
+ enetc_init_sxgmii(edev);
+ break;
+ };
+}
+
+/*
+ * LS1028A is the only part with IERB at this time and there are plans to
+ * change its structure, keep this LS1028A specific for now.
+ */
+#define LS1028A_IERB_BASE 0x1f0800000ULL
+#define LS1028A_IERB_PSIPMAR0(pf, vf) (LS1028A_IERB_BASE + 0x8000 \
+ + (pf) * 0x100 + (vf) * 8)
+#define LS1028A_IERB_PSIPMAR1(pf, vf) (LS1028A_IERB_PSIPMAR0(pf, vf) + 4)
+
+static int enetc_get_hwaddr(struct eth_device *edev, unsigned char *mac)
+{
+ return -EOPNOTSUPP;
+}
+
+static int enetc_ls1028a_write_hwaddr(struct eth_device *edev, const unsigned char *mac)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct pci_dev *pdev = to_pci_dev(priv->dev);
+ const int devfn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
+ int devfn = PCI_FUNC(pdev->devfn);
+ u32 lower, upper;
+ int pf;
+
+ if (devfn >= ARRAY_SIZE(devfn_to_pf))
+ return 0;
+
+ pf = devfn_to_pf[devfn];
+ if (pf < 0)
+ return 0;
+
+ lower = get_unaligned_le16(mac + 4);
+ upper = get_unaligned_le32(mac);
+
+ out_le32(LS1028A_IERB_PSIPMAR0(pf, 0), upper);
+ out_le32(LS1028A_IERB_PSIPMAR1(pf, 0), lower);
+
+ return 0;
+}
+
+static int enetc_write_hwaddr(struct eth_device *edev, const unsigned char *mac)
+{
+ struct enetc_priv *priv = edev->priv;
+
+ u16 lower = get_unaligned_le16(mac + 4);
+ u32 upper = get_unaligned_le32(mac);
+
+ enetc_write_port(priv, ENETC_PSIPMAR0, upper);
+ enetc_write_port(priv, ENETC_PSIPMAR1, lower);
+
+ return 0;
+}
+
+/* Configure port parameters (# of rings, frame size, enable port) */
+static void enetc_enable_si_port(struct enetc_priv *priv)
+{
+ u32 val;
+
+ /* set Rx/Tx BDR count */
+ val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT);
+ val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT);
+ enetc_write_port(priv, ENETC_PSICFGR(0), val);
+ /* set Rx max frame size */
+ enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
+ /* enable MAC port */
+ enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN);
+ /* enable port */
+ enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN);
+ /* set SI cache policy */
+ enetc_write(priv, ENETC_SICAR0,
+ ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG);
+ /* enable SI */
+ enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN);
+}
+
+/* returns DMA address for a given buffer index */
+static inline dma_addr_t enetc_rxb_address(struct enetc_priv *priv, int i)
+{
+ return priv->rx_pkg_phys[i];
+}
+
+/*
+ * Setup a single Tx BD Ring (ID = 0):
+ * - set Tx buffer descriptor address
+ * - set the BD count
+ * - initialize the producer and consumer index
+ */
+static void enetc_setup_tx_bdr(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct bd_ring *tx_bdr = &priv->tx_bdr;
+ u64 tx_bd_add = (u64)priv->enetc_txbd_phys;
+
+ /* used later to advance to the next Tx BD */
+ tx_bdr->bd_count = ENETC_BD_CNT;
+ tx_bdr->next_prod_idx = 0;
+ tx_bdr->next_cons_idx = 0;
+ tx_bdr->cons_idx = priv->regs_base +
+ ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBCIR);
+ tx_bdr->prod_idx = priv->regs_base +
+ ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBPIR);
+
+ /* set Tx BD address */
+ enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBBAR0,
+ lower_32_bits(tx_bd_add));
+ enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBBAR1,
+ upper_32_bits(tx_bd_add));
+ /* set Tx 8 BD count */
+ enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBLENR,
+ tx_bdr->bd_count);
+
+ /* reset both producer/consumer indexes */
+ enetc_write_reg(tx_bdr->cons_idx, tx_bdr->next_cons_idx);
+ enetc_write_reg(tx_bdr->prod_idx, tx_bdr->next_prod_idx);
+
+ /* enable TX ring */
+ enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBMR, ENETC_TBMR_EN);
+}
+
+/*
+ * Setup a single Rx BD Ring (ID = 0):
+ * - set Rx buffer descriptors address (one descriptor per buffer)
+ * - set buffer size as max frame size
+ * - enable Rx ring
+ * - reset consumer and producer indexes
+ * - set buffer for each descriptor
+ */
+static void enetc_setup_rx_bdr(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct bd_ring *rx_bdr = &priv->rx_bdr;
+ u64 rx_bd_add = (u64)priv->enetc_rxbd_phys;
+ int i;
+
+ /* used later to advance to the next BD produced by ENETC HW */
+ rx_bdr->bd_count = ENETC_BD_CNT;
+ rx_bdr->next_prod_idx = 0;
+ rx_bdr->next_cons_idx = 0;
+ rx_bdr->cons_idx = priv->regs_base +
+ ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBCIR);
+ rx_bdr->prod_idx = priv->regs_base +
+ ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBPIR);
+
+ /* set Rx BD address */
+ enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBAR0,
+ lower_32_bits(rx_bd_add));
+ enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBAR1,
+ upper_32_bits(rx_bd_add));
+ /* set Rx BD count (multiple of 8) */
+ enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBLENR,
+ rx_bdr->bd_count);
+ /* set Rx buffer size */
+ enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBSR, PKTSIZE);
+
+ /* fill Rx BD */
+ memset_io(priv->enetc_rxbd, 0,
+ rx_bdr->bd_count * sizeof(union enetc_rx_bd));
+
+ for (i = 0; i < rx_bdr->bd_count; i++) {
+ priv->rx_pkg[i] = dma_alloc(PKTSIZE);
+ priv->rx_pkg_phys[i] = dma_map_single(priv->dev, priv->rx_pkg[i],
+ PKTSIZE, DMA_FROM_DEVICE);
+ priv->enetc_rxbd[i].w.addr = priv->rx_pkg_phys[i];
+ }
+
+ /* reset producer (ENETC owned) and consumer (SW owned) index */
+ enetc_write_reg(rx_bdr->cons_idx, rx_bdr->next_cons_idx);
+ enetc_write_reg(rx_bdr->prod_idx, rx_bdr->next_prod_idx);
+
+ /* enable Rx ring */
+ enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBMR, ENETC_RBMR_EN);
+}
+
+/*
+ * Start ENETC interface:
+ * - perform FLR
+ * - enable access to port and SI registers
+ * - set mac address
+ * - setup TX/RX buffer descriptors
+ * - enable Tx/Rx rings
+ */
+static int enetc_start(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct pci_dev *pdev = to_pci_dev(priv->dev);
+ u32 t;
+ int ret, interface;
+
+ /* reset and enable the PCI device */
+ pci_flr(pdev);
+
+ pci_read_config_dword(pdev, PCI_COMMAND, &t);
+ pci_write_config_dword(pdev, PCI_COMMAND, t | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+
+ interface = of_get_phy_mode(priv->dev->of_node);
+
+ ret = phy_device_connect(edev, NULL, 0, NULL, 0, interface);
+ if (ret)
+ return ret;
+
+ enetc_enable_si_port(priv);
+
+ /* setup Tx/Rx buffer descriptors */
+ enetc_setup_tx_bdr(edev);
+ enetc_setup_rx_bdr(edev);
+
+ enetc_setup_mac_iface(edev, priv->phy);
+
+ return 0;
+}
+
+/*
+ * Stop the network interface:
+ * - just quiesce it, we can wipe all configuration as _start starts from
+ * scratch each time
+ */
+static void enetc_stop(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct pci_dev *pdev = to_pci_dev(priv->dev);
+ u32 t;
+
+ /* FLR is sufficient to quiesce the device */
+ pci_flr(pdev);
+
+ /* leave the BARs accessible after we stop, this is needed to use
+ * internal MDIO in command line.
+ */
+ pci_read_config_dword(pdev, PCI_COMMAND, &t);
+ pci_write_config_dword(pdev, PCI_COMMAND, t | PCI_COMMAND_MEMORY);
+}
+
+/*
+ * ENETC transmit packet:
+ * - check if Tx BD ring is full
+ * - set buffer/packet address (dma address)
+ * - set final fragment flag
+ * - try while producer index equals consumer index or timeout
+ */
+static int enetc_send(struct eth_device *edev, void *packet, int length)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct bd_ring *txr = &priv->tx_bdr;
+ int ret;
+ u32 pi, ci;
+ dma_addr_t dma;
+ u64 start;
+
+ pi = txr->next_prod_idx;
+ ci = enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK;
+ /* Tx ring is full when */
+ if (((pi + 1) % txr->bd_count) == ci) {
+ dev_err(&edev->dev, "Tx BDR full\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_vdbg(&edev->dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length,
+ upper_32_bits((u64)packet), lower_32_bits((u64)packet));
+
+ dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE);
+
+ /* prepare Tx BD */
+ writeq(dma, &priv->enetc_txbd[pi].addr);
+ writew(length, &priv->enetc_txbd[pi].buf_len);
+ writew(length, &priv->enetc_txbd[pi].frm_len);
+ writew(ENETC_TXBD_FLAGS_F, &priv->enetc_txbd[pi].flags);
+
+ /* send frame: increment producer index */
+ pi = (pi + 1) % txr->bd_count;
+ txr->next_prod_idx = pi;
+ enetc_write_reg(txr->prod_idx, pi);
+
+ start = get_time_ns();
+
+ while (1) {
+ if (is_timeout(start, 100 * USECOND)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ if (pi == (enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE);
+
+ return ret;
+}
+
+/*
+ * Receive frame:
+ * - wait for the next BD to get ready bit set
+ * - clean up the descriptor
+ * - move on and indicate to HW that the cleaned BD is available for Rx
+ */
+static int enetc_recv(struct eth_device *edev)
+{
+ struct enetc_priv *priv = edev->priv;
+ struct bd_ring *rxr = &priv->rx_bdr;
+ int pi = rxr->next_prod_idx;
+ int ci = rxr->next_cons_idx;
+ u32 status;
+ void *pkg;
+ int len;
+
+ status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus);
+
+ /* check if current BD is ready to be consumed */
+ if (!ENETC_RXBD_STATUS_R(status))
+ return 0;
+
+ len = readw(&priv->enetc_rxbd[pi].r.buf_len);
+
+ dev_dbg(&edev->dev, "RxBD[%d]: len=%d err=%d pkt=0x%p\n", pi, len,
+ ENETC_RXBD_STATUS_ERRORS(status), pkg);
+
+ dma_sync_single_for_cpu(priv->dev, priv->rx_pkg_phys[pi], PKTSIZE, DMA_FROM_DEVICE);
+ net_receive(edev, priv->rx_pkg[pi], len);
+ dma_sync_single_for_device(priv->dev, priv->rx_pkg_phys[pi], PKTSIZE, DMA_FROM_DEVICE);
+
+ /* BD clean up and advance to next in ring */
+ memset_io(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd));
+ writeq(priv->rx_pkg_phys[pi], &priv->enetc_rxbd[pi].w.addr);
+ rxr->next_prod_idx = (pi + 1) % rxr->bd_count;
+ ci = (ci + 1) % rxr->bd_count;
+ rxr->next_cons_idx = ci;
+ dmb();
+ /* free up the slot in the ring for HW */
+ enetc_write_reg(rxr->cons_idx, ci);
+
+ return 0;
+}
+
+static int enetc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct enetc_priv *priv;
+ struct eth_device *edev;
+
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+
+ priv = xzalloc(sizeof(*priv));
+ priv->dev = dev;
+
+ priv->enetc_txbd = dma_alloc_coherent(sizeof(struct enetc_tx_bd) * ENETC_BD_CNT,
+ &priv->enetc_txbd_phys);
+ priv->enetc_rxbd = dma_alloc_coherent(sizeof(union enetc_rx_bd) * ENETC_BD_CNT,
+ &priv->enetc_rxbd_phys);
+
+ if (!priv->enetc_txbd || !priv->enetc_rxbd)
+ return -ENOMEM;
+
+ /* initialize register */
+ priv->regs_base = pci_iomap(pdev, 0);
+ if (!priv->regs_base) {
+ dev_err(dev, "failed to map BAR0\n");
+ return -EINVAL;
+ }
+
+ edev = &priv->edev;
+ dev->priv = priv;
+ edev->priv = priv;
+ edev->open = enetc_start;
+ edev->send = enetc_send;
+ edev->recv = enetc_recv;
+ edev->halt = enetc_stop;
+ edev->get_ethaddr = enetc_get_hwaddr;
+
+ if (of_machine_is_compatible("fsl,ls1028a"))
+ edev->set_ethaddr = enetc_ls1028a_write_hwaddr;
+ else
+ edev->set_ethaddr = enetc_write_hwaddr;
+
+ edev->parent = dev;
+
+ priv->port_regs = priv->regs_base + ENETC_PORT_REGS_OFF;
+
+ enetc_start_pcs(&priv->edev);
+
+ return eth_register(edev);
+}
+
+static void enetc_remove(struct pci_dev *pdev)
+{
+ struct enetc_priv *priv = pdev->dev.priv;
+
+ enetc_stop(&priv->edev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(enetc_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) },
+ { },
+};
+
+static struct pci_driver enetc_eth_driver = {
+ .name = "fsl_enetc",
+ .id_table = enetc_pci_tbl,
+ .probe = enetc_probe,
+ .remove = enetc_remove,
+};
+device_pci_driver(enetc_eth_driver);
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h
new file mode 100644
index 0000000000..dc59325ae7
--- /dev/null
+++ b/drivers/net/fsl_enetc.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * ENETC ethernet controller driver
+ * Copyright 2017-2021 NXP
+ */
+
+#ifndef _ENETC_H
+#define _ENETC_H
+
+#include <linux/bitops.h>
+
+/* PCI function IDs */
+#define PCI_DEVICE_ID_ENETC_ETH 0xE100
+#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01
+
+/* ENETC Ethernet controller registers */
+/* Station interface register offsets */
+#define ENETC_SIMR 0x000
+#define ENETC_SIMR_EN BIT(31)
+#define ENETC_SICAR0 0x040
+/* write cache cfg: snoop, no allocate, data & BD coherent */
+#define ENETC_SICAR_WR_CFG 0x6767
+/* read cache cfg: coherent copy, look up, don't alloc in cache */
+#define ENETC_SICAR_RD_CFG 0x27270000
+#define ENETC_SIROCT 0x300
+#define ENETC_SIRFRM 0x308
+#define ENETC_SITOCT 0x320
+#define ENETC_SITFRM 0x328
+
+/* Rx/Tx Buffer Descriptor Ring registers */
+enum enetc_bdr_type {TX, RX};
+#define ENETC_BDR(type, n, off) (0x8000 + (type) * 0x100 + (n) * 0x200 + (off))
+#define ENETC_BDR_IDX_MASK 0xffff
+
+/* Rx BDR reg offsets */
+#define ENETC_RBMR 0x00
+#define ENETC_RBMR_EN BIT(31)
+#define ENETC_RBBSR 0x08
+/* initial consumer index for Rx BDR */
+#define ENETC_RBCIR 0x0c
+#define ENETC_RBBAR0 0x10
+#define ENETC_RBBAR1 0x14
+#define ENETC_RBPIR 0x18
+#define ENETC_RBLENR 0x20
+
+/* Tx BDR reg offsets */
+#define ENETC_TBMR 0x00
+#define ENETC_TBMR_EN BIT(31)
+#define ENETC_TBBAR0 0x10
+#define ENETC_TBBAR1 0x14
+#define ENETC_TBPIR 0x18
+#define ENETC_TBCIR 0x1c
+#define ENETC_TBLENR 0x20
+
+/* Port registers offset */
+#define ENETC_PORT_REGS_OFF 0x10000
+
+/* Port registers */
+#define ENETC_PMR 0x0000
+#define ENETC_PMR_SI0_EN BIT(16)
+#define ENETC_PSIPMMR 0x0018
+#define ENETC_PSIPMAR0 0x0100
+#define ENETC_PSIPMAR1 0x0104
+#define ENETC_PCAPR0 0x0900
+#define ENETC_PCAPRO_MDIO BIT(11)
+#define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10)
+#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff)
+#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16)
+/* MAC configuration */
+#define ENETC_PM_CC 0x8008
+#define ENETC_PM_CC_DEFAULT 0x0810
+#define ENETC_PM_CC_RX_TX_EN 0x8813
+#define ENETC_PM_MAXFRM 0x8014
+#define ENETC_RX_MAXFRM_SIZE PKTSIZE
+#define ENETC_PM_IMDIO_BASE 0x8030
+#define ENETC_PM_IF_MODE 0x8300
+#define ENETC_PM_IF_MODE_RG BIT(2)
+#define ENETC_PM_IF_MODE_AN_ENA BIT(15)
+#define ENETC_PM_IFM_SSP_MASK GENMASK(14, 13)
+#define ENETC_PM_IFM_SSP_1000 (2 << 13)
+#define ENETC_PM_IFM_SSP_100 (0 << 13)
+#define ENETC_PM_IFM_SSP_10 (1 << 13)
+#define ENETC_PM_IFM_FULL_DPX BIT(12)
+#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0)
+
+/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */
+#define ENETC_BD_CNT 128
+#define ENETC_BD_ALIGN 128
+
+/* single pair of Rx/Tx rings */
+#define ENETC_RX_BDR_CNT 1
+#define ENETC_TX_BDR_CNT 1
+#define ENETC_RX_BDR_ID 0
+#define ENETC_TX_BDR_ID 0
+
+/* Tx buffer descriptor */
+struct enetc_tx_bd {
+ __le64 addr;
+ __le16 buf_len;
+ __le16 frm_len;
+ __le16 err_csum;
+ __le16 flags;
+} __packed;
+
+#define ENETC_TXBD_FLAGS_F BIT(15)
+#define ENETC_POLL_TRIES 32000
+
+/* Rx buffer descriptor */
+union enetc_rx_bd {
+ /* SW provided BD format */
+ struct {
+ __le64 addr;
+ u8 reserved[8];
+ } w;
+
+ /* ENETC returned BD format */
+ struct {
+ __le16 inet_csum;
+ __le16 parse_summary;
+ __le32 rss_hash;
+ __le16 buf_len;
+ __le16 vlan_opt;
+ union {
+ struct {
+ __le16 flags;
+ __le16 error;
+ } __packed;
+ __le32 lstatus;
+ };
+ } r;
+} __packed;
+
+#define ENETC_RXBD_STATUS_R(status) (((status) >> 30) & 0x1)
+#define ENETC_RXBD_STATUS_F(status) (((status) >> 31) & 0x1)
+#define ENETC_RXBD_STATUS_ERRORS(status) (((status) >> 16) & 0xff)
+#define ENETC_RXBD_STATUS(flags) ((flags) << 16)
+
+/* Tx/Rx ring info */
+struct bd_ring {
+ void *cons_idx;
+ void *prod_idx;
+ /* next BD index to use */
+ int next_prod_idx;
+ int next_cons_idx;
+ int bd_count;
+};
+
+/* ENETC private structure */
+struct enetc_priv {
+ struct eth_device edev;
+ struct device *dev;
+
+ struct enetc_tx_bd *enetc_txbd;
+ union enetc_rx_bd *enetc_rxbd;
+
+ dma_addr_t rx_pkg_phys[ENETC_BD_CNT];
+ dma_addr_t enetc_txbd_phys;
+ dma_addr_t enetc_rxbd_phys;
+
+ void *rx_pkg[ENETC_BD_CNT];
+
+ void __iomem *regs_base; /* base ENETC registers */
+ void __iomem *port_regs; /* base ENETC port registers */
+
+ /* Rx/Tx buffer descriptor rings info */
+ struct bd_ring tx_bdr;
+ struct bd_ring rx_bdr;
+
+ int uclass_id;
+ struct phy_device *phy;
+};
+
+/* register accessors */
+#define enetc_read_reg(x) readl((x))
+#define enetc_write_reg(x, val) writel((val), (x))
+#define enetc_read(priv, off) enetc_read_reg((priv)->regs_base + (off))
+#define enetc_write(priv, off, v) \
+ enetc_write_reg((priv)->regs_base + (off), v)
+
+/* port register accessors */
+#define enetc_port_regs(priv, off) ((priv)->port_regs + (off))
+#define enetc_read_port(priv, off) \
+ enetc_read_reg(enetc_port_regs((priv), (off)))
+#define enetc_write_port(priv, off, v) \
+ enetc_write_reg(enetc_port_regs((priv), (off)), v)
+
+/* BDR register accessors, see ENETC_BDR() */
+#define enetc_bdr_read(priv, t, n, off) \
+ enetc_read(priv, ENETC_BDR(t, n, off))
+#define enetc_bdr_write(priv, t, n, off, val) \
+ enetc_write(priv, ENETC_BDR(t, n, off), val)
+
+/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */
+#define ENETC_PCS_PHY_ADDR 0
+
+/* PCS registers */
+#define ENETC_PCS_CR 0x00
+#define ENETC_PCS_CR_RESET_AN 0x1200
+#define ENETC_PCS_CR_DEF_VAL 0x0140
+#define ENETC_PCS_CR_RST BIT(15)
+#define ENETC_PCS_DEV_ABILITY 0x04
+#define ENETC_PCS_DEV_ABILITY_SGMII 0x4001
+#define ENETC_PCS_DEV_ABILITY_SXGMII 0x5001
+#define ENETC_PCS_LINK_TIMER1 0x12
+#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0
+#define ENETC_PCS_LINK_TIMER2 0x13
+#define ENETC_PCS_LINK_TIMER2_VAL 0x0003
+#define ENETC_PCS_IF_MODE 0x14
+#define ENETC_PCS_IF_MODE_SGMII BIT(0)
+#define ENETC_PCS_IF_MODE_SGMII_AN BIT(1)
+#define ENETC_PCS_IF_MODE_SPEED_1G BIT(3)
+
+/* PCS replicator block for USXGMII */
+#define ENETC_PCS_DEVAD_REPL 0x1f
+
+#define ENETC_PCS_REPL_LINK_TIMER_1 0x12
+#define ENETC_PCS_REPL_LINK_TIMER_1_DEF 0x0003
+#define ENETC_PCS_REPL_LINK_TIMER_2 0x13
+#define ENETC_PCS_REPL_LINK_TIMER_2_DEF 0x06a0
+
+/* ENETC external MDIO registers */
+#define ENETC_MDIO_BASE 0x1c00
+#define ENETC_MDIO_CFG 0x00
+#define ENETC_EMDIO_CFG_C22 0x00809508
+#define ENETC_EMDIO_CFG_C45 0x00809548
+#define ENETC_EMDIO_CFG_RD_ER BIT(1)
+#define ENETC_EMDIO_CFG_BSY BIT(0)
+#define ENETC_MDIO_CTL 0x04
+#define ENETC_MDIO_CTL_READ BIT(15)
+#define ENETC_MDIO_DATA 0x08
+#define ENETC_MDIO_STAT 0x0c
+
+#define ENETC_MDIO_READ_ERR 0xffff
+
+struct enetc_mdio_priv {
+ void __iomem *regs_base;
+ struct mii_bus bus;
+};
+
+/*
+ * these functions are implemented by ENETC_MDIO and are re-used by ENETC driver
+ * to drive serdes / internal SoC PHYs
+ */
+int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+ int reg);
+int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+ int reg, u16 val);
+
+/* sets up primary MAC addresses in DT/IERB */
+void fdt_fixup_enetc_mac(void *blob);
+
+#endif /* _ENETC_H */
diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c
new file mode 100644
index 0000000000..773d4afd52
--- /dev/null
+++ b/drivers/net/fsl_enetc_mdio.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ENETC ethernet controller driver
+ * Copyright 2019 NXP
+ */
+
+#include <common.h>
+#include <net.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+#include <io.h>
+#include <linux/mdio.h>
+
+#include "fsl_enetc.h"
+
+static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
+{
+ int to = 10000;
+
+ while ((enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) &&
+ --to)
+ cpu_relax();
+}
+
+int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+ int reg)
+{
+ if (devad == MDIO_DEVAD_NONE)
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
+ else
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
+ enetc_mdio_wait_bsy(priv);
+
+ if (devad == MDIO_DEVAD_NONE) {
+ enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
+ (addr << 5) | reg);
+ } else {
+ enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
+ enetc_mdio_wait_bsy(priv);
+
+ enetc_write(priv, ENETC_MDIO_STAT, reg);
+ enetc_mdio_wait_bsy(priv);
+
+ enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
+ (addr << 5) | devad);
+ }
+
+ enetc_mdio_wait_bsy(priv);
+ if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER)
+ return ENETC_MDIO_READ_ERR;
+
+ return enetc_read(priv, ENETC_MDIO_DATA);
+}
+
+int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+ int reg, u16 val)
+{
+ if (devad == MDIO_DEVAD_NONE)
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
+ else
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
+ enetc_mdio_wait_bsy(priv);
+
+ if (devad != MDIO_DEVAD_NONE) {
+ enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
+ enetc_write(priv, ENETC_MDIO_STAT, reg);
+ } else {
+ enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg);
+ }
+ enetc_mdio_wait_bsy(priv);
+
+ enetc_write(priv, ENETC_MDIO_DATA, val);
+ enetc_mdio_wait_bsy(priv);
+
+ return 0;
+}
+
+static int enetc_mdio_read(struct mii_bus *bus, int addr, int reg)
+{
+ struct enetc_mdio_priv *priv = bus->priv;
+
+ return enetc_mdio_read_priv(priv, addr, MDIO_DEVAD_NONE, reg);
+}
+
+static int enetc_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+ struct enetc_mdio_priv *priv = bus->priv;
+
+ return enetc_mdio_write_priv(priv, addr, MDIO_DEVAD_NONE, reg, val);
+}
+
+static int enetc_mdio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct enetc_mdio_priv *priv;
+
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->regs_base = pci_iomap(pdev, 0);
+ if (!priv->regs_base) {
+ dev_err(&pdev->dev, "failed to map BAR0\n");
+ return -EINVAL;
+ }
+
+ priv->regs_base += ENETC_MDIO_BASE;
+
+ priv->bus.read = enetc_mdio_read;
+ priv->bus.write = enetc_mdio_write;
+ priv->bus.parent = &pdev->dev;
+ priv->bus.priv = priv;
+
+ return mdiobus_register(&priv->bus);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(enetc_mdio_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
+ { },
+};
+
+static struct pci_driver enetc_mdio_driver = {
+ .name = "fsl_enetc_mdio",
+ .id_table = enetc_mdio_pci_tbl,
+ .probe = enetc_mdio_probe,
+};
+device_pci_driver(enetc_mdio_driver);
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 8571d4f92c..21ffe822e1 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -21,11 +21,6 @@
#include <linux/err.h>
#include "gianfar.h"
-/* 2 seems to be the minimum number of TX descriptors to make it work. */
-#define TX_BUF_CNT 2
-#define RX_BUF_CNT PKTBUFSRX
-#define BUF_ALIGN 8
-
/*
* Initialize required registers to appropriate values, zeroing
* those we don't care about (unless zero is bad, in which case,
@@ -199,7 +194,7 @@ static int gfar_open(struct eth_device *edev)
for (ix = 0; ix < RX_BUF_CNT; ix++) {
out_be16(&priv->rxbd[ix].status, RXBD_EMPTY);
out_be16(&priv->rxbd[ix].length, 0);
- out_be32(&priv->rxbd[ix].bufPtr, (uint) NetRxPackets[ix]);
+ out_be32(&priv->rxbd[ix].bufPtr, (uint) priv->rx_buffer[ix]);
}
out_be16(&priv->rxbd[RX_BUF_CNT - 1].status, RXBD_EMPTY | RXBD_WRAP);
@@ -234,19 +229,13 @@ static int gfar_set_ethaddr(struct eth_device *edev, const unsigned char *mac)
{
struct gfar_private *priv = edev->priv;
void __iomem *regs = priv->regs;
- char tmpbuf[MAC_ADDR_LEN];
uint tempval;
- int ix;
- for (ix = 0; ix < MAC_ADDR_LEN; ix++)
- tmpbuf[MAC_ADDR_LEN - 1 - ix] = mac[ix];
-
- tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) |
- tmpbuf[3];
+ tempval = (mac[5] << 24) | (mac[4] << 16) | (mac[3] << 8) | mac[2];
out_be32(regs + GFAR_MACSTRADDR1_OFFSET, tempval);
- tempval = *((uint *)(tmpbuf + 4));
+ tempval = (mac[1] << 24) | (mac[0] << 16);
out_be32(regs + GFAR_MACSTRADDR2_OFFSET, tempval);
@@ -361,7 +350,7 @@ static int gfar_send(struct eth_device *edev, void *packet, int length)
{
struct gfar_private *priv = edev->priv;
void __iomem *regs = priv->regs;
- struct device_d *dev = edev->parent;
+ struct device *dev = edev->parent;
uint64_t start;
uint tidx;
uint16_t status;
@@ -401,7 +390,7 @@ static int gfar_send(struct eth_device *edev, void *packet, int length)
static int gfar_recv(struct eth_device *edev)
{
struct gfar_private *priv = edev->priv;
- struct device_d *dev = edev->parent;
+ struct device *dev = edev->parent;
void __iomem *regs = priv->regs;
uint16_t status, length;
@@ -413,7 +402,7 @@ static int gfar_recv(struct eth_device *edev)
/* Send the packet up if there were no errors */
status = in_be16(&priv->rxbd[priv->rxidx].status);
if (!(status & RXBD_STATS))
- net_receive(edev, NetRxPackets[priv->rxidx], length - 4);
+ net_receive(edev, priv->rx_buffer[priv->rxidx], length - 4);
else
dev_err(dev, "Got error %x\n", status & RXBD_STATS);
@@ -468,18 +457,23 @@ static int gfar_miiphy_write(struct mii_bus *bus, int addr, int reg,
* Initialize device structure. Returns success if
* initialization succeeded.
*/
-static int gfar_probe(struct device_d *dev)
+static int gfar_probe(struct device *dev)
{
struct gfar_info_struct *gfar_info = dev->platform_data;
struct eth_device *edev;
struct gfar_private *priv;
- struct device_d *mdev;
+ struct device *mdev;
size_t size;
char devname[16];
char *p;
+ int ret;
priv = xzalloc(sizeof(struct gfar_private));
+ ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer));
+ if (ret)
+ return ret;
+
edev = &priv->edev;
priv->mdiobus_tbi = gfar_info->mdiobus_tbi;
@@ -536,13 +530,13 @@ static int gfar_probe(struct device_d *dev)
return eth_register(edev);
}
-static struct driver_d gfar_eth_driver = {
+static struct driver gfar_eth_driver = {
.name = "gfar",
.probe = gfar_probe,
};
device_platform_driver(gfar_eth_driver);
-static int gfar_phy_probe(struct device_d *dev)
+static int gfar_phy_probe(struct device *dev)
{
struct gfar_phy *phy;
int ret;
@@ -567,13 +561,13 @@ static int gfar_phy_probe(struct device_d *dev)
return 0;
}
-static struct driver_d gfar_phy_driver = {
+static struct driver gfar_phy_driver = {
.name = "gfar-mdio",
.probe = gfar_phy_probe,
};
register_driver_macro(coredevice, platform, gfar_phy_driver);
-static int gfar_tbiphy_probe(struct device_d *dev)
+static int gfar_tbiphy_probe(struct device *dev)
{
struct gfar_phy *phy;
int ret;
@@ -597,7 +591,7 @@ static int gfar_tbiphy_probe(struct device_d *dev)
return 0;
}
-static struct driver_d gfar_tbiphy_driver = {
+static struct driver gfar_tbiphy_driver = {
.name = "gfar-tbiphy",
.probe = gfar_tbiphy_probe,
};
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 081230189a..8a60c7f38e 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -261,10 +261,15 @@ struct rxbd8 {
struct gfar_phy {
void __iomem *regs;
- struct device_d *dev;
+ struct device *dev;
struct mii_bus miibus;
};
+/* 2 seems to be the minimum number of TX descriptors to make it work. */
+#define TX_BUF_CNT 2
+#define RX_BUF_CNT PKTBUFSRX
+#define BUF_ALIGN 8
+
struct gfar_private {
struct eth_device edev;
void __iomem *regs;
@@ -282,5 +287,6 @@ struct gfar_private {
uint link;
uint duplexity;
uint speed;
+ void *rx_buffer[PKTBUFSRX];
};
#endif /* __GIANFAR_H */
diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c
index b037e19633..2120657bd9 100644
--- a/drivers/net/ks8851_mll.c
+++ b/drivers/net/ks8851_mll.c
@@ -353,7 +353,6 @@
* struct ks_net - KS8851 driver private data
* @hw_addr : start address of data register.
* @hw_addr_cmd : start address of command register.
- * @pdev : Pointer to platform device.
* @bus_width : i/o bus width.
* @extra_byte : number of extra byte prepended rx pkt.
*
@@ -364,8 +363,8 @@ struct ks_net {
struct mii_bus miibus;
void __iomem *hw_addr;
void __iomem *hw_addr_cmd;
- struct platform_device *pdev;
int bus_width;
+ void *rx_buf;
};
#define BE3 0x8000 /* Byte Enable 3 */
@@ -592,7 +591,7 @@ static void ks_soft_reset(struct ks_net *ks, unsigned op)
*/
static int ks_read_selftest(struct ks_net *ks)
{
- struct device_d *dev = &ks->edev.dev;
+ struct device *dev = &ks->edev.dev;
unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
int ret = 0;
unsigned rd;
@@ -658,8 +657,7 @@ static void ks_setup(struct ks_net *ks)
static int ks8851_rx_frame(struct ks_net *ks)
{
- struct device_d *dev = &ks->edev.dev;
- u16 *rdptr = (u16 *) NetRxPackets[0];
+ struct device *dev = &ks->edev.dev;
u16 RxStatus, RxLen = 0;
u16 tmp_rxqcr;
@@ -679,14 +677,14 @@ static int ks8851_rx_frame(struct ks_net *ks)
tmp_rxqcr = ks_rdreg16(ks, KS_RXQCR);
ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr | RXQCR_SDA);
/* read 2 bytes for dummy, 2 for status, 2 for len*/
- ks_inblk(ks, rdptr, 2 + 2 + 2);
- ks_inblk(ks, rdptr, ALIGN(RxLen, 4));
+ ks_inblk(ks, ks->rx_buf, 2 + 2 + 2);
+ ks_inblk(ks, ks->rx_buf, ALIGN(RxLen, 4));
ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr);
if (RxStatus & RXFSHR_RXFV) {
/* Pass to upper layer */
dev_dbg(dev, "passing packet to upper layer\n\n");
- net_receive(&ks->edev, NetRxPackets[0], RxLen);
+ net_receive(&ks->edev, ks->rx_buf, RxLen);
return RxLen;
} else if (RxStatus & RXFSHR_ERR) {
dev_err(dev, "RxStatus error 0x%04x\n", RxStatus & RXFSHR_ERR);
@@ -712,7 +710,7 @@ static int ks8851_rx_frame(struct ks_net *ks)
static int ks8851_eth_rx(struct eth_device *edev)
{
struct ks_net *ks = (struct ks_net *)edev->priv;
- struct device_d *dev = &edev->dev;
+ struct device *dev = &edev->dev;
u16 frame_cnt;
if (!(ks_rdreg16(ks, KS_ISR) & IRQ_RXI))
@@ -733,7 +731,7 @@ static int ks8851_eth_send(struct eth_device *edev,
void *packet, int length)
{
struct ks_net *ks = (struct ks_net *)edev->priv;
- struct device_d *dev = &edev->dev;
+ struct device *dev = &edev->dev;
uint64_t tmo;
u16 tmp_rxqcr;
@@ -769,7 +767,7 @@ static int ks8851_eth_send(struct eth_device *edev,
static int ks8851_eth_open(struct eth_device *edev)
{
struct ks_net *priv = (struct ks_net *)edev->priv;
- struct device_d *dev = &edev->dev;
+ struct device *dev = &edev->dev;
int ret;
ks_enable_qmu(priv);
@@ -792,14 +790,14 @@ static int ks8851_init_dev(struct eth_device *edev)
static void ks8851_eth_halt(struct eth_device *edev)
{
struct ks_net *priv = (struct ks_net *)edev->priv;
- struct device_d *dev = &edev->dev;
+ struct device *dev = &edev->dev;
ks_disable_qmu(priv);
dev_dbg(dev, "eth_halt\n");
}
-static int ks8851_probe(struct device_d *dev)
+static int ks8851_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
@@ -827,6 +825,7 @@ static int ks8851_probe(struct device_d *dev)
ks->hw_addr_cmd = IOMEM(iores->start);
ks->bus_width = dev->resource[0].flags & IORESOURCE_MEM_TYPE_MASK;
+ ks->rx_buf = xmalloc(PKTSIZE);
edev->init = ks8851_init_dev;
edev->open = ks8851_eth_open;
@@ -871,7 +870,7 @@ static int ks8851_probe(struct device_d *dev)
return 0;
}
-static struct driver_d ks8851_driver = {
+static struct driver ks8851_driver = {
.name = "ks8851_mll",
.probe = ks8851_probe,
};
diff --git a/drivers/net/ksz8864rmn.c b/drivers/net/ksz8864rmn.c
index 72ab86579b..c4c30377af 100644
--- a/drivers/net/ksz8864rmn.c
+++ b/drivers/net/ksz8864rmn.c
@@ -122,7 +122,7 @@ static struct cdev_operations micrel_switch_ops = {
.write = micel_switch_write,
};
-static int micrel_switch_probe(struct device_d *dev)
+static int micrel_switch_probe(struct device *dev)
{
struct micrel_switch_priv *priv;
int ret = 0;
@@ -187,7 +187,7 @@ static const struct platform_device_id ksz_ids[] = {
{ }
};
-static struct driver_d micrel_switch_driver = {
+static struct driver micrel_switch_driver = {
.name = "ksz8864rmn",
.probe = micrel_switch_probe,
.id_table = ksz_ids,
diff --git a/drivers/net/ksz8873.c b/drivers/net/ksz8873.c
new file mode 100644
index 0000000000..03d3530754
--- /dev/null
+++ b/drivers/net/ksz8873.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <common.h>
+#include <complete.h>
+#include <dsa.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mii.h>
+#include <linux/mdio.h>
+#include <net.h>
+#include <of_device.h>
+#include <linux/regmap.h>
+
+#define KSZ8873_CHIP_ID0 0x00
+#define KSZ8873_CHIP_ID1 0x01
+#define KSZ88_CHIP_ID_M GENMASK(7, 4)
+#define KSZ88_REV_ID_M GENMASK(3, 1)
+
+#define KSZ8873_GLOBAL_CTRL_1 0x03
+#define KSZ8873_PASS_ALL_FRAMES BIT(7)
+#define KSZ8873_P3_TAIL_TAG_EN BIT(6)
+
+/*
+ * port specific registers. Should be used with ksz_pwrite/ksz_pread functions
+ */
+#define KSZ8873_PORTx_CTRL_1 0x01
+#define KSZ8873_PORTx_CTRL_12 0x0c
+
+#define PORT_AUTO_NEG_ENABLE BIT(7)
+#define PORT_FORCE_100_MBIT BIT(6)
+#define PORT_FORCE_FULL_DUPLEX BIT(5)
+#define PORT_AUTO_NEG_100BTX_FD BIT(3)
+#define PORT_AUTO_NEG_100BTX BIT(2)
+#define PORT_AUTO_NEG_10BT_FD BIT(1)
+#define PORT_AUTO_NEG_10BT BIT(0)
+
+#define KSZ8873_PORTx_CTRL_13 0x0d
+
+#define PORT_AUTO_NEG_RESTART BIT(5)
+#define PORT_POWER_DOWN BIT(3)
+
+#define KSZ8873_PORTx_STATUS_0 0x0e
+
+#define PORT_AUTO_NEG_COMPLETE BIT(6)
+#define PORT_STAT_LINK_GOOD BIT(5)
+#define PORT_REMOTE_100BTX_FD BIT(3)
+#define PORT_REMOTE_100BTX BIT(2)
+#define PORT_REMOTE_10BT_FD BIT(1)
+#define PORT_REMOTE_10BT BIT(0)
+
+#define KSZ8873_PORTx_STATUS_1 0x0f
+
+#define KSZ8795_ID_HI 0x0022
+#define KSZ8863_ID_LO 0x1430
+
+#define PORT_CTRL_ADDR(port, addr) ((addr) + 0x10 + (port) * 0x10)
+
+struct ksz8873_dcfg {
+ unsigned int num_ports;
+ unsigned int phy_port_cnt;
+ u8 id0;
+ u8 id1;
+};
+
+struct ksz8873_switch {
+ struct phy_device *mdiodev;
+ struct dsa_switch ds;
+ struct device *dev;
+ const struct ksz8873_dcfg *dcfg;
+ struct regmap *regmap;
+};
+
+/* Serial Management Interface (SMI) uses the following frame format:
+ *
+ * preamble|start|Read/Write| PHY | REG |TA| Data bits | Idle
+ * |frame| OP code |address |address| | |
+ * read | 32x1´s | 01 | 00 | 1xRRR | RRRRR |Z0| 00000000DDDDDDDD | Z
+ * write| 32x1´s | 01 | 00 | 0xRRR | RRRRR |10| xxxxxxxxDDDDDDDD | Z
+ *
+ */
+
+#define SMI_KSZ88XX_READ_PHY BIT(4)
+
+static int ksz8873_mdio_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ struct ksz8873_switch *priv = ctx;
+ struct phy_device *mdiodev = priv->mdiodev;
+ int ret;
+
+ ret = mdiobus_read(mdiodev->bus, ((reg & 0xE0) >> 5) |
+ SMI_KSZ88XX_READ_PHY, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int ksz8873_mdio_write(void *ctx, unsigned int reg, unsigned int val)
+{
+ struct ksz8873_switch *priv = ctx;
+ struct phy_device *mdiodev = priv->mdiodev;
+
+ return mdiobus_write(mdiodev->bus, ((reg & 0xE0) >> 5), reg, val);
+}
+
+static const struct regmap_bus ksz8873_regmap_smi = {
+ .reg_read = ksz8873_mdio_read,
+ .reg_write = ksz8873_mdio_write,
+};
+
+static const struct regmap_config ksz8873_regmap_config = {
+ .name = "#8",
+ .reg_bits = 8,
+ .pad_bits = 24,
+ .val_bits = 8,
+};
+
+static int ksz_read8(struct ksz8873_switch *priv, u32 reg, u8 *val)
+{
+ unsigned int value;
+ int ret = regmap_read(priv->regmap, reg, &value);
+
+ *val = value & 0xff;
+
+ return ret;
+}
+
+static int ksz_write8(struct ksz8873_switch *priv, u32 reg, u8 value)
+{
+ return regmap_write(priv->regmap, reg, value);
+}
+
+static int ksz_pread8(struct ksz8873_switch *priv, int port, int reg, u8 *val)
+{
+ return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static int ksz_pwrite8(struct ksz8873_switch *priv, int port, int reg, u8 val)
+{
+ return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static void ksz8_r_phy(struct ksz8873_switch *priv, u16 phy, u16 reg, u16 *val)
+{
+ u8 restart, ctrl, link;
+ int processed = true;
+ u16 data = 0;
+ u8 p = phy;
+
+ switch (reg) {
+ case MII_BMCR:
+ ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_13, &restart);
+ ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl);
+ if (ctrl & PORT_FORCE_100_MBIT)
+ data |= BMCR_SPEED100;
+ if ((ctrl & PORT_AUTO_NEG_ENABLE))
+ data |= BMCR_ANENABLE;
+ if (restart & PORT_POWER_DOWN)
+ data |= BMCR_PDOWN;
+ if (restart & PORT_AUTO_NEG_RESTART)
+ data |= BMCR_ANRESTART;
+ if (ctrl & PORT_FORCE_FULL_DUPLEX)
+ data |= BMCR_FULLDPLX;
+ break;
+ case MII_BMSR:
+ ksz_pread8(priv, p, KSZ8873_PORTx_STATUS_0, &link);
+ data = BMSR_100FULL |
+ BMSR_100HALF |
+ BMSR_10FULL |
+ BMSR_10HALF |
+ BMSR_ANEGCAPABLE;
+ if (link & PORT_AUTO_NEG_COMPLETE)
+ data |= BMSR_ANEGCOMPLETE;
+ if (link & PORT_STAT_LINK_GOOD)
+ data |= BMSR_LSTATUS;
+ break;
+ case MII_PHYSID1:
+ data = KSZ8795_ID_HI;
+ break;
+ case MII_PHYSID2:
+ data = KSZ8863_ID_LO;
+ break;
+ case MII_ADVERTISE:
+ ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl);
+ data = ADVERTISE_CSMA;
+ if (ctrl & PORT_AUTO_NEG_100BTX_FD)
+ data |= ADVERTISE_100FULL;
+ if (ctrl & PORT_AUTO_NEG_100BTX)
+ data |= ADVERTISE_100HALF;
+ if (ctrl & PORT_AUTO_NEG_10BT_FD)
+ data |= ADVERTISE_10FULL;
+ if (ctrl & PORT_AUTO_NEG_10BT)
+ data |= ADVERTISE_10HALF;
+ break;
+ case MII_LPA:
+ ksz_pread8(priv, p, KSZ8873_PORTx_STATUS_0, &link);
+ data = LPA_SLCT;
+ if (link & PORT_REMOTE_100BTX_FD)
+ data |= LPA_100FULL;
+ if (link & PORT_REMOTE_100BTX)
+ data |= LPA_100HALF;
+ if (link & PORT_REMOTE_10BT_FD)
+ data |= LPA_10FULL;
+ if (link & PORT_REMOTE_10BT)
+ data |= LPA_10HALF;
+ if (data & ~LPA_SLCT)
+ data |= LPA_LPACK;
+ break;
+ default:
+ processed = false;
+ break;
+ }
+ if (processed)
+ *val = data;
+}
+
+static void ksz8_w_phy(struct ksz8873_switch *priv, u16 phy, u16 reg, u16 val)
+{
+ u8 restart, ctrl, data;
+ u8 p = phy;
+
+ switch (reg) {
+ case MII_BMCR:
+ ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl);
+ data = ctrl;
+ if ((val & BMCR_ANENABLE))
+ data |= PORT_AUTO_NEG_ENABLE;
+ else
+ data &= ~PORT_AUTO_NEG_ENABLE;
+
+ if (val & BMCR_SPEED100)
+ data |= PORT_FORCE_100_MBIT;
+ else
+ data &= ~PORT_FORCE_100_MBIT;
+ if (val & BMCR_FULLDPLX)
+ data |= PORT_FORCE_FULL_DUPLEX;
+ else
+ data &= ~PORT_FORCE_FULL_DUPLEX;
+ if (data != ctrl)
+ ksz_pwrite8(priv, p, KSZ8873_PORTx_CTRL_12, data);
+ ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_13, &restart);
+ data = restart;
+ if (val & BMCR_ANRESTART)
+ data |= PORT_AUTO_NEG_RESTART;
+ else
+ data &= ~(PORT_AUTO_NEG_RESTART);
+ if (val & BMCR_PDOWN)
+ data |= PORT_POWER_DOWN;
+ else
+ data &= ~PORT_POWER_DOWN;
+ if (data != restart)
+ ksz_pwrite8(priv, p, KSZ8873_PORTx_CTRL_13, data);
+ break;
+ case MII_ADVERTISE:
+ ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl);
+ data = ctrl;
+ data &= ~(PORT_AUTO_NEG_100BTX_FD |
+ PORT_AUTO_NEG_100BTX |
+ PORT_AUTO_NEG_10BT_FD |
+ PORT_AUTO_NEG_10BT);
+ if (val & ADVERTISE_100FULL)
+ data |= PORT_AUTO_NEG_100BTX_FD;
+ if (val & ADVERTISE_100HALF)
+ data |= PORT_AUTO_NEG_100BTX;
+ if (val & ADVERTISE_10FULL)
+ data |= PORT_AUTO_NEG_10BT_FD;
+ if (val & ADVERTISE_10HALF)
+ data |= PORT_AUTO_NEG_10BT;
+ if (data != ctrl)
+ ksz_pwrite8(priv, p, KSZ8873_PORTx_CTRL_12, data);
+ break;
+ default:
+ break;
+ }
+}
+
+static int ksz8873_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+ struct device *dev = ds->dev;
+ struct ksz8873_switch *priv = dev_get_priv(dev);
+ u16 val = 0xffff;
+
+ if (addr >= priv->dcfg->phy_port_cnt)
+ return val;
+
+ ksz8_r_phy(priv, addr, reg, &val);
+
+ return val;
+}
+
+static int ksz8873_phy_write16(struct dsa_switch *ds, int addr, int reg,
+ u16 val)
+{
+ struct device *dev = ds->dev;
+ struct ksz8873_switch *priv = dev_get_priv(dev);
+
+ /* No real PHY after this. */
+ if (addr >= priv->dcfg->phy_port_cnt)
+ return 0;
+
+ ksz8_w_phy(priv, addr, reg, val);
+
+ return 0;
+}
+
+static void ksz8873_cfg_port_member(struct ksz8873_switch *priv, int port,
+ u8 member)
+{
+ ksz_pwrite8(priv, port, KSZ8873_PORTx_CTRL_1, member);
+}
+
+static int ksz8873_port_enable(struct dsa_port *dp, int port,
+ struct phy_device *phy)
+{
+ return 0;
+}
+
+static int ksz8873_xmit(struct dsa_port *dp, int port, void *packet, int length)
+{
+ u8 *tag = packet + length - dp->ds->needed_tx_tailroom;
+
+ *tag = BIT(dp->index);
+
+ return 0;
+}
+
+static int ksz8873_recv(struct dsa_switch *ds, int *port, void *packet,
+ int length)
+{
+ u8 *tag = packet + length - ds->needed_rx_tailroom;
+
+ *port = *tag & 7;
+
+ return 0;
+};
+
+static const struct dsa_switch_ops ksz8873_dsa_ops = {
+ .port_enable = ksz8873_port_enable,
+ .xmit = ksz8873_xmit,
+ .rcv = ksz8873_recv,
+ .phy_read = ksz8873_phy_read16,
+ .phy_write = ksz8873_phy_write16,
+};
+
+static int ksz8873_default_setup(struct ksz8873_switch *priv)
+{
+ int i;
+
+ ksz_write8(priv, KSZ8873_GLOBAL_CTRL_1, KSZ8873_PASS_ALL_FRAMES |
+ KSZ8873_P3_TAIL_TAG_EN);
+
+ for (i = 0; i < priv->ds.num_ports; i++) {
+ u8 member;
+ /* isolate all ports by default */
+ member = BIT(priv->ds.cpu_port);
+ ksz8873_cfg_port_member(priv, i, member);
+
+ member = dsa_user_ports(&priv->ds);
+ ksz8873_cfg_port_member(priv, priv->ds.cpu_port, member);
+ }
+
+ return 0;
+}
+
+static int ksz8873_probe_mdio(struct phy_device *mdiodev)
+{
+ struct device *dev = &mdiodev->dev;
+ const struct ksz8873_dcfg *dcfg;
+ struct ksz8873_switch *priv;
+ struct dsa_switch *ds;
+ struct gpio_desc *gpio;
+ int ret;
+ u8 id0, id1;
+
+ priv = xzalloc(sizeof(*priv));
+
+ dcfg = of_device_get_match_data(dev);
+ if (!dcfg)
+ return -EINVAL;
+
+ dev->priv = priv;
+ priv->dev = dev;
+ priv->dcfg = dcfg;
+ priv->mdiodev = mdiodev;
+
+ priv->regmap = regmap_init(dev, &ksz8873_regmap_smi, priv,
+ &ksz8873_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "Failed to initialize regmap.\n");
+
+ gpio = gpiod_get_optional(dev, "reset", GPIOF_OUT_INIT_ACTIVE);
+ if (IS_ERR(gpio)) {
+ dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n");
+ } else if (gpio) {
+ mdelay(1);
+ gpiod_set_value(gpio, false);
+ }
+
+ ret = ksz_read8(priv, KSZ8873_CHIP_ID0, &id0);
+ if (ret)
+ return ret;
+
+ ret = ksz_read8(priv, KSZ8873_CHIP_ID1, &id1);
+ if (ret)
+ return ret;
+
+ if (id0 != dcfg->id0 ||
+ (id1 & (KSZ88_CHIP_ID_M | KSZ88_REV_ID_M)) != dcfg->id1)
+ return -ENODEV;
+
+ ds = &priv->ds;
+ ds->dev = dev;
+ ds->num_ports = dcfg->num_ports;
+ ds->ops = &ksz8873_dsa_ops;
+ ds->needed_rx_tailroom = 1;
+ ds->needed_tx_tailroom = 1;
+ ds->phys_mii_mask = 0x3;
+
+ ret = dsa_register_switch(ds);
+ if (ret)
+ return ret;
+
+ ksz8873_default_setup(priv);
+
+ return 0;
+}
+
+static const struct ksz8873_dcfg ksz8873_dcfg = {
+ .num_ports = 3,
+ .phy_port_cnt = 2,
+ .id0 = 0x88,
+ .id1 = 0x30,
+};
+
+static const struct of_device_id ksz8873_dt_ids[] = {
+ { .compatible = "microchip,ksz8873", .data = &ksz8873_dcfg },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ksz8873_dt_ids);
+
+static struct phy_driver ksz8873_driver_mdio = {
+ .drv = {
+ .name = "KSZ8873 MDIO",
+ .of_compatible = DRV_OF_COMPAT(ksz8873_dt_ids),
+ },
+ .probe = ksz8873_probe_mdio,
+};
+device_mdio_driver(ksz8873_driver_mdio);
diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c
index 779762fad5..1abea9d040 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -3,17 +3,17 @@
#include <common.h>
#include <complete.h>
#include <dsa.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#include <net.h>
#include <platform_data/ksz9477_reg.h>
#include <spi/spi.h>
+#include <i2c/i2c.h>
+#include "ksz_common.h"
/* SPI frame opcodes */
-#define KS_SPIOP_RD 3
-#define KS_SPIOP_WR 2
#define SPI_ADDR_SHIFT 24
-#define SPI_ADDR_MASK (BIT(SPI_ADDR_SHIFT) - 1)
+#define SPI_ADDR_ALIGN 3
#define SPI_TURNAROUND_SHIFT 5
#define GBIT_SUPPORT BIT(0)
@@ -21,131 +21,13 @@
#define IS_9893 BIT(2)
#define KSZ9477_PHY_ERRATA BIT(3)
-struct ksz_switch {
- struct spi_device *spi;
- struct dsa_switch ds;
- struct device_d *dev;
- int phy_port_cnt;
- u32 chip_id;
- u8 features;
-};
-
-static int ksz9477_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
- unsigned int len)
-{
- u32 txbuf;
- int ret;
-
- txbuf = reg & SPI_ADDR_MASK;
- txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
- txbuf <<= SPI_TURNAROUND_SHIFT;
- txbuf = cpu_to_be32(txbuf);
-
- ret = spi_write_then_read(spi, &txbuf, 4, val, len);
-
- return ret;
-}
-
-static int ksz9477_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
- unsigned int len)
-{
- u32 txbuf[2];
-
- txbuf[0] = reg & SPI_ADDR_MASK;
- txbuf[0] |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
- txbuf[0] <<= SPI_TURNAROUND_SHIFT;
- txbuf[0] = cpu_to_be32(*txbuf);
- memcpy(&txbuf[1], val, len);
-
- return spi_write(spi, txbuf, 4 + len);
-}
-
-static int ksz_read8(struct ksz_switch *priv, u32 reg, u8 *val)
-{
- return ksz9477_spi_read_reg(priv->spi, reg, val, 1);
-}
-
-static int ksz_write8(struct ksz_switch *priv, u32 reg, u8 value)
-{
- return ksz9477_spi_write_reg(priv->spi, reg, &value, 1);
-}
-
-static int ksz_read16(struct ksz_switch *priv, u32 reg, u16 *val)
-{
- int ret = ksz9477_spi_read_reg(priv->spi, reg, (u8 *)val, 2);
-
- if (!ret)
- *val = be16_to_cpu(*val);
-
- return ret;
-}
-
-static int ksz_write16(struct ksz_switch *priv, u32 reg, u16 value)
-{
- struct spi_device *spi = priv->spi;
-
- value = cpu_to_be16(value);
- return ksz9477_spi_write_reg(spi, reg, (u8 *)&value, 2);
-}
-
-static int ksz_read32(struct ksz_switch *priv, u32 reg, u32 *val)
-{
- int ret = ksz9477_spi_read_reg(priv->spi, reg, (u8 *)val, 4);
-
- if (!ret)
- *val = be32_to_cpu(*val);
-
- return ret;
-}
-
-static int ksz_write32(struct ksz_switch *priv, u32 reg, u32 value)
-{
- struct spi_device *spi = priv->spi;
-
- value = cpu_to_be32(value);
- return ksz9477_spi_write_reg(spi, reg, (u8 *)&value, 4);
-}
-
-static void ksz_cfg(struct ksz_switch *priv, u32 addr, u8 bits, bool set)
-{
- u8 data;
-
- ksz_read8(priv, addr, &data);
- if (set)
- data |= bits;
- else
- data &= ~bits;
- ksz_write8(priv, addr, data);
-}
-
-static int ksz_pread8(struct ksz_switch *priv, int port, int reg, u8 *val)
-{
- return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val);
-}
-
-static int ksz_pwrite8(struct ksz_switch *priv, int port, int reg, u8 val)
-{
- return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val);
-}
-
-static int ksz_pread16(struct ksz_switch *priv, int port, int reg, u16 *val)
-{
- return ksz_read16(priv, PORT_CTRL_ADDR(port, reg), val);
-}
-
-static int ksz_pwrite16(struct ksz_switch *priv, int port, int reg, u16 val)
-{
- return ksz_write16(priv, PORT_CTRL_ADDR(port, reg), val);
-}
-
-static int ksz_pwrite32(struct ksz_switch *priv, int port, int reg, u32 val)
-{
- return ksz_write32(priv, PORT_CTRL_ADDR(port, reg), val);
-}
+KSZ_REGMAP_TABLE(ksz9477_spi, 32, SPI_ADDR_SHIFT,
+ SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN);
+KSZ_REGMAP_TABLE(ksz9477_i2c, not_used, 16, 0, 0);
static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
{
- struct device_d *dev = ds->dev;
+ struct device *dev = ds->dev;
struct ksz_switch *priv = dev_get_priv(dev);
u16 val = 0xffff;
@@ -160,7 +42,7 @@ static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg,
u16 val)
{
- struct device_d *dev = ds->dev;
+ struct device *dev = ds->dev;
struct ksz_switch *priv = dev_get_priv(dev);
/* No real PHY after this. */
@@ -200,7 +82,7 @@ static int ksz9477_switch_detect(struct ksz_switch *priv)
id_lo = (u8)(id32 >> 8);
if ((id_lo & 0xf) == 3) {
/* Chip is from KSZ9893 design. */
- dev_info(priv->dev, "Found KSZ9893\n");
+ dev_info(priv->dev, "Found KSZ9893 or compatible\n");
priv->features |= IS_9893;
priv->features &= ~KSZ9477_PHY_ERRATA;
@@ -293,12 +175,6 @@ static void ksz9477_phy_errata_setup(struct ksz_switch *priv, int port)
*/
ksz9477_port_mmd_write(priv, port, 0x1c, 0x04, 0x00d0);
- /* Energy Efficient Ethernet (EEE) feature select must
- * be manually disabled (except on KSZ8565 which is 100Mbit)
- */
- if (priv->features & GBIT_SUPPORT)
- ksz9477_port_mmd_write(priv, port, 0x07, 0x3c, 0x0000);
-
/* Register settings are required to meet data sheet
* supply current specifications
*/
@@ -387,6 +263,8 @@ static int ksz_port_setup(struct ksz_switch *priv, int port,
if (priv->features & KSZ9477_PHY_ERRATA)
ksz9477_phy_errata_setup(priv, port);
+ ksz9477_port_mmd_write(priv, port, 0x07, 0x3c, 0x0000);
+
ksz_pwrite16(priv, port, 0x100 + (MII_BMCR << 1),
BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET);
} else {
@@ -431,7 +309,7 @@ static int ksz_port_setup(struct ksz_switch *priv, int port,
static int ksz_port_enable(struct dsa_port *dp, int port,
struct phy_device *phy)
{
- struct device_d *dev = dp->ds->dev;
+ struct device *dev = dp->ds->dev;
struct ksz_switch *priv = dev_get_priv(dev);
u8 data8;
int ret;
@@ -479,7 +357,7 @@ static int ksz_recv(struct dsa_switch *ds, int *port, void *packet, int length)
return 0;
};
-static const struct dsa_ops ksz_dsa_ops = {
+static const struct dsa_switch_ops ksz_dsa_ops = {
.port_enable = ksz_port_enable,
.xmit = ksz_xmit,
.rcv = ksz_recv,
@@ -503,10 +381,33 @@ static int ksz_default_setup(struct ksz_switch *priv)
return 0;
}
-static int microchip_switch_probe(struct device_d *dev)
+static int microchip_switch_regmap_init(struct ksz_switch *priv)
+{
+ const struct regmap_config *cfg;
+ int i;
+
+ cfg = priv->spi ? ksz9477_spi_regmap_config : ksz9477_i2c_regmap_config;
+
+ for (i = 0; i < KSZ_REGMAP_ENTRY_COUNT; i++) {
+ if (priv->spi)
+ priv->regmap[i] = regmap_init_spi(priv->spi, &cfg[i]);
+ else
+ priv->regmap[i] = regmap_init_i2c(priv->i2c, &cfg[i]);
+ if (IS_ERR(priv->regmap[i]))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->regmap[i]),
+ "Failed to initialize regmap%i\n",
+ cfg[i].val_bits);
+ }
+
+ return 0;
+}
+
+static int microchip_switch_probe(struct device *dev)
{
+ struct device *hw_dev;
struct ksz_switch *priv;
- int ret = 0, gpio;
+ struct gpio_desc *gpio;
+ int ret = 0;
struct dsa_switch *ds;
priv = xzalloc(sizeof(*priv));
@@ -514,22 +415,33 @@ static int microchip_switch_probe(struct device_d *dev)
dev->priv = priv;
priv->dev = dev;
- priv->spi = (struct spi_device *)dev->type_data;
- priv->spi->mode = SPI_MODE_0;
- priv->spi->bits_per_word = 8;
+ if (dev_bus_is_spi(dev)) {
+ priv->spi = (struct spi_device *)dev->type_data;
+ priv->spi->mode = SPI_MODE_0;
+ priv->spi->bits_per_word = 8;
+ hw_dev = &priv->spi->dev;
+ } else if (dev_bus_is_i2c(dev)) {
+ priv->i2c = dev->type_data;
+ hw_dev = &priv->i2c->dev;
+ }
- gpio = gpiod_get(dev, "reset", GPIOF_OUT_INIT_ACTIVE);
- if (gpio_is_valid(gpio)) {
+ ret = microchip_switch_regmap_init(priv);
+ if (ret)
+ return ret;
+
+ gpio = gpiod_get_optional(dev, "reset", GPIOF_OUT_INIT_ACTIVE);
+ if (IS_ERR(gpio)) {
+ dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n");
+ } else if (gpio) {
mdelay(1);
- gpio_set_active(gpio, false);
+ gpiod_set_value(gpio, false);
}
ksz_reset_switch(dev->priv);
ret = ksz9477_switch_detect(dev->priv);
if (ret) {
- dev_err(&priv->spi->dev, "error detecting KSZ9477: %s\n",
- strerror(-ret));
+ dev_err(hw_dev, "error detecting KSZ9477: %pe\n", ERR_PTR(ret));
return -ENODEV;
}
@@ -548,19 +460,40 @@ static int microchip_switch_probe(struct device_d *dev)
ksz_default_setup(priv);
- return dsa_register_switch(ds);
+ ret = dsa_register_switch(ds);
+ if (ret)
+ return ret;
+
+ if (priv->i2c)
+ slice_depends_on(mdiobus_slice(ds->slave_mii_bus),
+ i2c_client_slice(priv->i2c));
+ else
+ slice_depends_on(mdiobus_slice(ds->slave_mii_bus),
+ spi_device_slice(priv->spi));
+
+ return regmap_multi_register_cdev(priv->regmap[0], priv->regmap[1],
+ priv->regmap[2], NULL);
}
static const struct of_device_id microchip_switch_dt_ids[] = {
+ { .compatible = "microchip,ksz8563" },
{ .compatible = "microchip,ksz9477" },
{ .compatible = "microchip,ksz9563" },
+ { .compatible = "microchip,ksz9893" },
{ }
};
+MODULE_DEVICE_TABLE(of, microchip_switch_dt_ids);
-static struct driver_d microchip_switch_driver = {
- .name = "ksz9477",
+static struct driver microchip_switch_spi_driver = {
+ .name = "ksz9477-spi",
.probe = microchip_switch_probe,
.of_compatible = DRV_OF_COMPAT(microchip_switch_dt_ids),
};
+device_spi_driver(microchip_switch_spi_driver);
-device_spi_driver(microchip_switch_driver);
+static struct driver microchip_switch_i2c_driver = {
+ .name = "ksz9477-i2c",
+ .probe = microchip_switch_probe,
+ .of_compatible = DRV_OF_COMPAT(microchip_switch_dt_ids),
+};
+device_i2c_driver(microchip_switch_i2c_driver);
diff --git a/drivers/net/ksz_common.h b/drivers/net/ksz_common.h
new file mode 100644
index 0000000000..291488fe34
--- /dev/null
+++ b/drivers/net/ksz_common.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef NET_KSZ_COMMON_H_
+#define NET_KSZ_COMMON_H_
+
+#include <linux/swab.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <platform_data/ksz9477_reg.h>
+
+struct ksz_switch {
+ struct spi_device *spi;
+ struct i2c_client *i2c;
+ struct dsa_switch ds;
+ struct device *dev;
+ int phy_port_cnt;
+ u32 chip_id;
+ u8 features;
+ struct regmap *regmap[3];
+};
+
+static inline int ksz_read8(struct ksz_switch *priv, u32 reg, u8 *val)
+{
+ unsigned int value;
+ int ret = regmap_read(priv->regmap[0], reg, &value);
+
+ *val = value;
+ return ret;
+}
+
+static inline int ksz_read16(struct ksz_switch *priv, u32 reg, u16 *val)
+{
+ unsigned int value;
+ int ret = regmap_read(priv->regmap[1], reg, &value);
+
+ *val = value;
+ return ret;
+}
+
+static inline int ksz_read32(struct ksz_switch *priv, u32 reg, u32 *val)
+{
+ unsigned int value;
+ int ret = regmap_read(priv->regmap[2], reg, &value);
+
+ *val = value;
+ return ret;
+}
+
+static inline int ksz_read64(struct ksz_switch *priv, u32 reg, u64 *val)
+{
+ u32 value[2];
+ int ret;
+
+ ret = regmap_bulk_read(priv->regmap[2], reg, value, 2);
+ if (!ret)
+ *val = (u64)value[0] << 32 | value[1];
+
+ return ret;
+}
+
+static inline int ksz_write8(struct ksz_switch *priv, u32 reg, u8 value)
+{
+ return regmap_write(priv->regmap[0], reg, value);
+}
+
+static inline int ksz_write16(struct ksz_switch *priv, u32 reg, u16 value)
+{
+ return regmap_write(priv->regmap[1], reg, value);
+}
+
+static inline int ksz_write32(struct ksz_switch *priv, u32 reg, u32 value)
+{
+ return regmap_write(priv->regmap[2], reg, value);
+}
+
+static inline int ksz_write64(struct ksz_switch *priv, u32 reg, u64 value)
+{
+ u32 val[2];
+
+ /* Ick! ToDo: Add 64bit R/W to regmap on 32bit systems */
+ value = swab64(value);
+ val[0] = swab32(value & 0xffffffffULL);
+ val[1] = swab32(value >> 32ULL);
+
+ return regmap_bulk_write(priv->regmap[2], reg, val, 2);
+}
+
+static inline int ksz_pread8(struct ksz_switch *priv, int port, int reg, u8 *val)
+{
+ return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static inline int ksz_pwrite8(struct ksz_switch *priv, int port, int reg, u8 val)
+{
+ return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static inline int ksz_pread16(struct ksz_switch *priv, int port, int reg, u16 *val)
+{
+ return ksz_read16(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static inline int ksz_pwrite16(struct ksz_switch *priv, int port, int reg, u16 val)
+{
+ return ksz_write16(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static inline int ksz_pwrite32(struct ksz_switch *priv, int port, int reg, u32 val)
+{
+ return ksz_write32(priv, PORT_CTRL_ADDR(port, reg), val);
+}
+
+static void ksz_cfg(struct ksz_switch *priv, u32 addr, u8 bits, bool set)
+{
+ regmap_update_bits(priv->regmap[0], addr, bits, set ? bits : 0);
+}
+
+/* Regmap tables generation */
+#define KSZ_SPI_OP_RD 3
+#define KSZ_SPI_OP_WR 2
+
+#define swabnot_used(x) 0
+
+#define KSZ_SPI_OP_FLAG_MASK(opcode, swp, regbits, regpad) \
+ swab##swp((opcode) << ((regbits) + (regpad)))
+
+#define KSZ_REGMAP_ENTRY_COUNT 3
+
+#define KSZ_REGMAP_ENTRY(width, swp, regbits, regpad, regalign) \
+ { \
+ .name = #width, \
+ .val_bits = (width), \
+ .reg_stride = 1, \
+ .reg_bits = (regbits) + (regalign), \
+ .pad_bits = (regpad), \
+ .max_register = BIT(regbits) - 1, \
+ .read_flag_mask = \
+ KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_RD, swp, \
+ regbits, regpad), \
+ .write_flag_mask = \
+ KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_WR, swp, \
+ regbits, regpad), \
+ .reg_format_endian = REGMAP_ENDIAN_BIG, \
+ .val_format_endian = REGMAP_ENDIAN_BIG \
+ }
+
+#define KSZ_REGMAP_TABLE(ksz, swp, regbits, regpad, regalign) \
+ static const struct regmap_config ksz##_regmap_config[KSZ_REGMAP_ENTRY_COUNT] = { \
+ KSZ_REGMAP_ENTRY(8, swp, (regbits), (regpad), (regalign)), \
+ KSZ_REGMAP_ENTRY(16, swp, (regbits), (regpad), (regalign)), \
+ KSZ_REGMAP_ENTRY(32, swp, (regbits), (regpad), (regalign)), \
+ }
+
+
+#endif
diff --git a/drivers/net/liteeth.c b/drivers/net/liteeth.c
index 771f57eead..1781e26348 100644
--- a/drivers/net/liteeth.c
+++ b/drivers/net/liteeth.c
@@ -53,7 +53,7 @@
#define MAX_PKT_SIZE LITEETH_BUFFER_SIZE
struct liteeth {
- struct device_d *dev;
+ struct device *dev;
struct eth_device edev;
void __iomem *base;
void __iomem *mdio_base;
@@ -73,6 +73,8 @@ struct liteeth {
int rx_slot;
int num_rx_slots;
void __iomem *rx_base;
+
+ void *rx_buf;
};
static inline void litex_write8(void __iomem *addr, u8 val)
@@ -230,9 +232,9 @@ static int liteeth_eth_rx(struct eth_device *edev)
rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT);
- memcpy(NetRxPackets[0], priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len);
+ memcpy(priv->rx_buf, priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len);
- net_receive(edev, NetRxPackets[0], len);
+ net_receive(edev, priv->rx_buf, len);
litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg);
@@ -270,9 +272,9 @@ static int liteeth_set_ethaddr(struct eth_device *edev,
return 0;
}
-static int liteeth_probe(struct device_d *dev)
+static int liteeth_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct eth_device *edev;
void __iomem *buf_base;
struct liteeth *priv;
@@ -323,6 +325,8 @@ static int liteeth_probe(struct device_d *dev)
priv->tx_base = buf_base + priv->num_rx_slots * LITEETH_BUFFER_SIZE;
priv->tx_slot = 0;
+ priv->rx_buf = xmalloc(PKTSIZE);
+
edev->init = liteeth_init_dev;
edev->open = liteeth_eth_open;
edev->send = liteeth_eth_send;
@@ -365,8 +369,9 @@ static const struct of_device_id liteeth_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, liteeth_dt_ids);
-static struct driver_d liteeth_driver = {
+static struct driver liteeth_driver = {
.name = DRV_NAME,
.probe = liteeth_probe,
.of_compatible = DRV_OF_COMPAT(liteeth_dt_ids),
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 511846122a..bcad88f60e 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -53,7 +53,7 @@
#define GEM_Q1_DESC_BYTES (sizeof(struct macb_dma_desc) * GEM_Q1_DESCS)
struct macb_config {
- int (*txclk_init)(struct device_d *dev, struct clk **tx_clk);
+ int (*txclk_init)(struct device *dev, struct clk **tx_clk);
};
struct macb_device {
@@ -63,9 +63,13 @@ struct macb_device {
unsigned int tx_head;
void *rx_buffer;
+ dma_addr_t rx_buffer_phys;
void *tx_buffer;
+ void *rx_packet_buf;
struct macb_dma_desc *rx_ring;
+ dma_addr_t rx_ring_phys;
struct macb_dma_desc *tx_ring;
+ dma_addr_t tx_ring_phys;
struct macb_dma_desc *gem_q1_descs;
int rx_buffer_size;
@@ -74,7 +78,7 @@ struct macb_device {
int phy_addr;
struct clk *pclk, *hclk, *txclk, *rxclk;
- const struct device_d *dev;
+ struct device *dev;
struct eth_device netdev;
phy_interface_t interface;
@@ -104,6 +108,7 @@ static int macb_send(struct eth_device *edev, void *packet,
int ret = 0;
uint64_t start;
unsigned int tx_head = macb->tx_head;
+ dma_addr_t packet_dma;
ctrl = MACB_BF(TX_FRMLEN, length);
ctrl |= MACB_BIT(TX_LAST);
@@ -115,23 +120,25 @@ static int macb_send(struct eth_device *edev, void *packet,
macb->tx_head++;
}
- macb->tx_ring[tx_head].ctrl = ctrl;
- macb->tx_ring[tx_head].addr = (ulong)packet;
- barrier();
- dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE);
+ packet_dma = dma_map_single(macb->dev, packet, length, DMA_TO_DEVICE);
+ if (dma_mapping_error(macb->dev, packet_dma))
+ return -EFAULT;
+
+ writel(ctrl, &macb->tx_ring[tx_head].ctrl);
+ writel(packet_dma, &macb->tx_ring[tx_head].addr);
macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
start = get_time_ns();
ret = -ETIMEDOUT;
do {
- barrier();
- ctrl = macb->tx_ring[0].ctrl;
+ ctrl = readl(&macb->tx_ring[0].ctrl);
if (ctrl & MACB_BIT(TX_USED)) {
ret = 0;
break;
}
} while (!is_timeout(start, 100 * MSECOND));
- dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE);
+
+ dma_unmap_single(macb->dev, packet_dma, length, DMA_TO_DEVICE);
if (ctrl & MACB_BIT(TX_UNDERRUN))
dev_err(macb->dev, "TX underrun\n");
@@ -150,44 +157,40 @@ static void reclaim_rx_buffers(struct macb_device *macb,
i = macb->rx_tail;
while (i > new_tail) {
- macb->rx_ring[i].addr &= ~MACB_BIT(RX_USED);
+ clrbits_le32(&macb->rx_ring[i].addr, MACB_BIT(RX_USED));
i++;
if (i > macb->rx_ring_size)
i = 0;
}
while (i < new_tail) {
- macb->rx_ring[i].addr &= ~MACB_BIT(RX_USED);
+ clrbits_le32(&macb->rx_ring[i].addr, MACB_BIT(RX_USED));
i++;
}
- barrier();
macb->rx_tail = new_tail;
}
static int gem_recv(struct eth_device *edev)
{
struct macb_device *macb = edev->priv;
- void *buffer;
+ dma_addr_t buffer;
int length;
u32 status;
for (;;) {
- barrier();
- if (!(macb->rx_ring[macb->rx_tail].addr & MACB_BIT(RX_USED)))
+ if (!(readl(&macb->rx_ring[macb->rx_tail].addr) & MACB_BIT(RX_USED)))
return -1;
- barrier();
- status = macb->rx_ring[macb->rx_tail].ctrl;
+ status = readl(&macb->rx_ring[macb->rx_tail].ctrl);
length = MACB_BFEXT(RX_FRMLEN, status);
- buffer = macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail;
- dma_sync_single_for_cpu((unsigned long)buffer, length,
- DMA_FROM_DEVICE);
- net_receive(edev, buffer, length);
- dma_sync_single_for_device((unsigned long)buffer, length,
- DMA_FROM_DEVICE);
- macb->rx_ring[macb->rx_tail].addr &= ~MACB_BIT(RX_USED);
- barrier();
+ buffer = macb->rx_buffer_phys + macb->rx_buffer_size * macb->rx_tail;
+ dma_sync_single_for_cpu(macb->dev, buffer, length, DMA_FROM_DEVICE);
+ net_receive(edev,
+ macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail,
+ length);
+ dma_sync_single_for_device(macb->dev, buffer, length, DMA_FROM_DEVICE);
+ clrbits_le32(&macb->rx_ring[macb->rx_tail].addr, MACB_BIT(RX_USED));
macb->rx_tail++;
if (macb->rx_tail >= macb->rx_ring_size)
@@ -201,18 +204,16 @@ static int macb_recv(struct eth_device *edev)
{
struct macb_device *macb = edev->priv;
unsigned int rx_tail = macb->rx_tail;
- void *buffer;
+ dma_addr_t buffer;
int length;
int wrapped = 0;
u32 status;
for (;;) {
- barrier();
- if (!(macb->rx_ring[rx_tail].addr & MACB_BIT(RX_USED)))
+ if (!(readl(&macb->rx_ring[rx_tail].addr) & MACB_BIT(RX_USED)))
return -1;
- barrier();
- status = macb->rx_ring[rx_tail].ctrl;
+ status = readl(&macb->rx_ring[rx_tail].ctrl);
if (status & MACB_BIT(RX_SOF)) {
if (rx_tail != macb->rx_tail)
reclaim_rx_buffers(macb, rx_tail);
@@ -220,7 +221,7 @@ static int macb_recv(struct eth_device *edev)
}
if (status & MACB_BIT(RX_EOF)) {
- buffer = macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail;
+ buffer = macb->rx_buffer_phys + macb->rx_buffer_size * macb->rx_tail;
length = MACB_BFEXT(RX_FRMLEN, status);
if (wrapped) {
unsigned int headlen, taillen;
@@ -228,26 +229,24 @@ static int macb_recv(struct eth_device *edev)
headlen = macb->rx_buffer_size * (macb->rx_ring_size
- macb->rx_tail);
taillen = length - headlen;
- dma_sync_single_for_cpu((unsigned long)buffer,
- headlen, DMA_FROM_DEVICE);
- memcpy((void *)NetRxPackets[0], buffer, headlen);
- dma_sync_single_for_cpu((unsigned long)macb->rx_buffer,
+ dma_sync_single_for_cpu(macb->dev, buffer, headlen, DMA_FROM_DEVICE);
+ memcpy(macb->rx_packet_buf,
+ macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail,
+ headlen);
+ dma_sync_single_for_cpu(macb->dev, macb->rx_buffer_phys,
taillen, DMA_FROM_DEVICE);
- memcpy((void *)NetRxPackets[0] + headlen,
- macb->rx_buffer, taillen);
- dma_sync_single_for_device((unsigned long)buffer,
- headlen, DMA_FROM_DEVICE);
- dma_sync_single_for_device((unsigned long)macb->rx_buffer,
+ memcpy(macb->rx_packet_buf + headlen, macb->rx_buffer, taillen);
+ dma_sync_single_for_device(macb->dev, buffer, headlen, DMA_FROM_DEVICE);
+ dma_sync_single_for_device(macb->dev, macb->rx_buffer_phys,
taillen, DMA_FROM_DEVICE);
- net_receive(edev, NetRxPackets[0], length);
+ net_receive(edev, macb->rx_packet_buf, length);
} else {
- dma_sync_single_for_cpu((unsigned long)buffer, length,
- DMA_FROM_DEVICE);
- net_receive(edev, buffer, length);
- dma_sync_single_for_device((unsigned long)buffer, length,
- DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(macb->dev, buffer, length, DMA_FROM_DEVICE);
+ net_receive(edev,
+ macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail,
+ length);
+ dma_sync_single_for_device(macb->dev, buffer, length, DMA_FROM_DEVICE);
}
- barrier();
if (++rx_tail >= macb->rx_ring_size)
rx_tail = 0;
reclaim_rx_buffers(macb, rx_tail);
@@ -367,9 +366,9 @@ static int gmac_init_dummy_tx_queues(struct macb_device *macb)
if (queue_mask & (1 << i))
num_queues++;
- macb->gem_q1_descs[0].addr = 0;
- macb->gem_q1_descs[0].ctrl = MACB_BIT(TX_WRAP) |
- MACB_BIT(TX_LAST) | MACB_BIT(TX_USED);
+ writel(0, &macb->gem_q1_descs[0].addr);
+ writel(MACB_BIT(TX_WRAP) | MACB_BIT(TX_LAST) | MACB_BIT(TX_USED),
+ &macb->gem_q1_descs[0].ctrl);
for (i = 1; i < num_queues; i++)
gem_writel_queue_TBQP(macb, (ulong)macb->gem_q1_descs, i - 1);
@@ -377,7 +376,7 @@ static int gmac_init_dummy_tx_queues(struct macb_device *macb)
return 0;
}
-static void macb_init(struct macb_device *macb)
+static int macb_init(struct macb_device *macb)
{
unsigned long paddr, val = 0;
int i;
@@ -386,21 +385,26 @@ static void macb_init(struct macb_device *macb)
* macb_halt should have been called at some point before now,
* so we'll assume the controller is idle.
*/
+ macb->rx_buffer_phys = dma_map_single(macb->dev, macb->rx_buffer,
+ macb->rx_buffer_size * macb->rx_ring_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(macb->dev, macb->rx_buffer_phys))
+ return -EFAULT;
/* initialize DMA descriptors */
paddr = (ulong)macb->rx_buffer;
for (i = 0; i < macb->rx_ring_size; i++) {
- macb->rx_ring[i].addr = paddr;
- macb->rx_ring[i].ctrl = 0;
+ writel(paddr, &macb->rx_ring[i].addr);
+ writel(0, &macb->rx_ring[i].ctrl);
paddr += macb->rx_buffer_size;
}
- macb->rx_ring[macb->rx_ring_size - 1].addr |= MACB_BIT(RX_WRAP);
+ setbits_le32(&macb->rx_ring[macb->rx_ring_size - 1].addr, MACB_BIT(RX_WRAP));
for (i = 0; i < TX_RING_SIZE; i++) {
- macb->tx_ring[i].addr = 0;
- macb->tx_ring[i].ctrl = MACB_BIT(TX_USED);
+ writel(0, &macb->tx_ring[i].addr);
+ writel(MACB_BIT(TX_USED), &macb->tx_ring[i].ctrl);
}
- macb->tx_ring[TX_RING_SIZE - 1].addr |= MACB_BIT(TX_WRAP);
+ setbits_le32(&macb->tx_ring[TX_RING_SIZE - 1].addr, MACB_BIT(TX_WRAP));
macb->rx_tail = macb->tx_head = 0;
@@ -413,9 +417,8 @@ static void macb_init(struct macb_device *macb)
gmac_init_dummy_tx_queues(macb);
/* Disable the second priority rx queue */
- macb->gem_q1_descs[1].addr = MACB_BIT(RX_USED) |
- MACB_BIT(RX_WRAP);
- macb->gem_q1_descs[1].ctrl = 0;
+ writel(MACB_BIT(RX_USED) | MACB_BIT(RX_WRAP), &macb->gem_q1_descs[1].addr);
+ writel(0, &macb->gem_q1_descs[1].ctrl);
gem_writel(macb, RQ1, (ulong)&macb->gem_q1_descs[1]);
}
@@ -442,6 +445,7 @@ static void macb_init(struct macb_device *macb)
macb_or_gem_writel(macb, USRIO, val);
+ return 0;
}
static void macb_halt(struct eth_device *edev)
@@ -460,6 +464,13 @@ static void macb_halt(struct eth_device *edev)
/* Disable TX and RX, and clear statistics */
macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
+
+ dma_unmap_single(macb->dev, macb->rx_buffer_phys,
+ macb->rx_buffer_size * macb->rx_ring_size,
+ DMA_FROM_DEVICE);
+ free(macb->rx_buffer);
+ dma_free_coherent((void *)macb->rx_ring, macb->rx_ring_phys, RX_RING_BYTES(macb));
+ dma_free_coherent((void *)macb->tx_ring, macb->tx_ring_phys, TX_RING_BYTES);
}
static int macb_phy_read(struct mii_bus *bus, int addr, int reg)
@@ -730,7 +741,7 @@ static const struct clk_ops fu540_c000_ops = {
.set_rate = fu540_macb_tx_set_rate,
};
-static int fu540_c000_txclk_init(struct device_d *dev, struct clk **tx_clk)
+static int fu540_c000_txclk_init(struct device *dev, struct clk **tx_clk)
{
struct clk *clk;
struct resource *res;
@@ -766,13 +777,13 @@ static int fu540_c000_txclk_init(struct device_d *dev, struct clk **tx_clk)
return 0;
}
#else
-static int fu540_c000_txclk_init(struct device_d *dev, struct clk **tx_clk)
+static int fu540_c000_txclk_init(struct device *dev, struct clk **tx_clk)
{
return -ENOSYS;
}
#endif
-static int macb_probe(struct device_d *dev)
+static int macb_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
@@ -780,6 +791,7 @@ static int macb_probe(struct device_d *dev)
const char *pclk_name, *hclk_name;
const struct macb_config *config = NULL;
u32 ncfgr;
+ int ret;
macb = xzalloc(sizeof(*macb));
edev = &macb->netdev;
@@ -815,19 +827,19 @@ static int macb_probe(struct device_d *dev)
macb->phy_flags = pdata->phy_flags;
pclk_name = "macb_clk";
hclk_name = NULL;
- } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) {
+ } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) {
int ret;
struct device_node *mdiobus;
- ret = of_get_phy_mode(dev->device_node);
+ ret = of_get_phy_mode(dev->of_node);
if (ret < 0)
macb->interface = PHY_INTERFACE_MODE_MII;
else
macb->interface = ret;
- mdiobus = of_get_child_by_name(dev->device_node, "mdio");
+ mdiobus = of_get_child_by_name(dev->of_node, "mdio");
if (mdiobus)
- macb->miibus.dev.device_node = mdiobus;
+ macb->miibus.dev.of_node = mdiobus;
macb->phy_addr = -1;
pclk_name = "pclk";
@@ -877,7 +889,7 @@ static int macb_probe(struct device_d *dev)
clk_enable(macb->rxclk);
if (config) {
- int ret = config->txclk_init(dev, &macb->txclk);
+ ret = config->txclk_init(dev, &macb->txclk);
if (ret)
return ret;
}
@@ -891,13 +903,15 @@ static int macb_probe(struct device_d *dev)
macb_init_rx_buffer_size(macb, PKTSIZE);
macb->rx_buffer = dma_alloc(macb->rx_buffer_size * macb->rx_ring_size);
- macb->rx_ring = dma_alloc_coherent(RX_RING_BYTES(macb), DMA_ADDRESS_BROKEN);
- macb->tx_ring = dma_alloc_coherent(TX_RING_BYTES, DMA_ADDRESS_BROKEN);
+ macb->rx_ring = dma_alloc_coherent(RX_RING_BYTES(macb), &macb->rx_ring_phys);
+ macb->tx_ring = dma_alloc_coherent(TX_RING_BYTES, &macb->tx_ring_phys);
if (macb->is_gem)
macb->gem_q1_descs = dma_alloc_coherent(GEM_Q1_DESC_BYTES,
DMA_ADDRESS_BROKEN);
+ macb->rx_packet_buf = net_alloc_packet();
+
macb_reset_hw(macb);
ncfgr = macb_mdc_clk_div(macb);
ncfgr |= MACB_BIT(PAE); /* PAuse Enable */
@@ -905,7 +919,9 @@ static int macb_probe(struct device_d *dev)
ncfgr |= macb_dbw(macb);
macb_writel(macb, NCFGR, ncfgr);
- macb_init(macb);
+ ret = macb_init(macb);
+ if (ret)
+ return ret;
mdiobus_register(&macb->miibus);
eth_register(edev);
@@ -916,11 +932,13 @@ static int macb_probe(struct device_d *dev)
return 0;
}
-static void macb_remove(struct device_d *dev)
+static void macb_remove(struct device *dev)
{
struct macb_device *macb = dev->priv;
macb_halt(&macb->netdev);
+
+ net_free_packet(macb->rx_packet_buf);
}
static const struct macb_config fu540_c000_config = {
@@ -931,13 +949,15 @@ static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "cdns,at91sam9260-macb",},
{ .compatible = "atmel,sama5d2-gem",},
{ .compatible = "atmel,sama5d3-gem",},
- { .compatible = "cdns,zynq-gem",},
- { .compatible = "cdns,zynqmp-gem",},
+ { .compatible = "atmel,sama5d4-gem",},
+ { .compatible = "xlnx,zynq-gem",},
+ { .compatible = "xlnx,zynqmp-gem",},
{ .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, macb_dt_ids);
-static struct driver_d macb_driver = {
+static struct driver macb_driver = {
.name = "macb",
.probe = macb_probe,
.remove = macb_remove,
diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c
index 359c70c927..1e176dbdc2 100644
--- a/drivers/net/mvneta.c
+++ b/drivers/net/mvneta.c
@@ -166,7 +166,7 @@
#define MVNETA_MH_SIZE 2
#define TXQ_NUM 8
-#define RX_RING_SIZE 4
+#define RX_RING_SIZE 128
#define TRANSFER_TIMEOUT (10 * MSECOND)
struct rxdesc {
@@ -198,7 +198,7 @@ struct txdesc {
struct mvneta_port {
void __iomem *reg;
- struct device_d dev;
+ struct device dev;
struct eth_device edev;
struct clk *clk;
@@ -383,7 +383,7 @@ static int mvneta_send(struct eth_device *edev, void *data, int len)
int ret, error, last_desc;
/* Flush transmit data */
- dma_sync_single_for_device((unsigned long)data, len, DMA_TO_DEVICE);
+ dma_sync_single_for_device(&priv->dev, (unsigned long)data, len, DMA_TO_DEVICE);
memset(txdesc, 0, sizeof(*txdesc));
/* Fill the Tx descriptor */
@@ -400,7 +400,7 @@ static int mvneta_send(struct eth_device *edev, void *data, int len)
* the Tx port status register (PTXS).
*/
ret = wait_on_timeout(TRANSFER_TIMEOUT, !mvneta_pending_tx(priv));
- dma_sync_single_for_cpu((unsigned long)data, len, DMA_TO_DEVICE);
+ dma_sync_single_for_cpu(&priv->dev, (unsigned long)data, len, DMA_TO_DEVICE);
if (ret) {
dev_err(&edev->dev, "transmit timeout\n");
return ret;
@@ -451,7 +451,7 @@ static int mvneta_recv(struct eth_device *edev)
}
/* invalidate current receive buffer */
- dma_sync_single_for_cpu((unsigned long)rxdesc->buf_phys_addr,
+ dma_sync_single_for_cpu(&priv->dev, (unsigned long)rxdesc->buf_phys_addr,
ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE);
/* received packet is padded with two null bytes (Marvell header) */
@@ -459,7 +459,7 @@ static int mvneta_recv(struct eth_device *edev)
rxdesc->data_size - MVNETA_MH_SIZE);
ret = 0;
- dma_sync_single_for_device((unsigned long)rxdesc->buf_phys_addr,
+ dma_sync_single_for_device(&priv->dev, (unsigned long)rxdesc->buf_phys_addr,
ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE);
recv_err:
@@ -706,7 +706,7 @@ static int mvneta_port_config(struct mvneta_port *priv)
return 0;
}
-static int mvneta_probe(struct device_d *dev)
+static int mvneta_probe(struct device *dev)
{
struct mvneta_port *priv;
int ret;
@@ -720,7 +720,7 @@ static int mvneta_probe(struct device_d *dev)
return PTR_ERR(priv->clk);
clk_enable(priv->clk);
- ret = of_get_phy_mode(dev->device_node);
+ ret = of_get_phy_mode(dev->of_node);
if (ret < 0)
return ret;
priv->intf = ret;
@@ -755,8 +755,9 @@ static struct of_device_id mvneta_dt_ids[] = {
{ .compatible = "marvell,armada-xp-neta" },
{ }
};
+MODULE_DEVICE_TABLE(of, mvneta_dt_ids);
-static struct driver_d mvneta_driver = {
+static struct driver mvneta_driver = {
.name = "mvneta",
.probe = mvneta_probe,
.of_compatible = DRV_OF_COMPAT(mvneta_dt_ids),
diff --git a/drivers/net/orion-gbe.c b/drivers/net/orion-gbe.c
index 0ae94e6840..e1b763893d 100644
--- a/drivers/net/orion-gbe.c
+++ b/drivers/net/orion-gbe.c
@@ -42,7 +42,7 @@ struct txdesc {
};
struct port_priv {
- struct device_d dev;
+ struct device dev;
struct eth_device edev;
void __iomem *regs;
struct device_node *np;
@@ -228,7 +228,7 @@ static int port_send(struct eth_device *edev, void *data, int len)
int ret;
/* flush transmit data */
- dma_sync_single_for_device((unsigned long)data, len, DMA_TO_DEVICE);
+ dma_sync_single_for_device(&port->dev, (unsigned long)data, len, DMA_TO_DEVICE);
txdesc->cmd_sts = TXDESC_OWNED_BY_DMA;
txdesc->cmd_sts |= TXDESC_FIRST | TXDESC_LAST;
@@ -243,7 +243,7 @@ static int port_send(struct eth_device *edev, void *data, int len)
/* wait for packet transmit completion */
ret = wait_on_timeout(TRANSFER_TIMEOUT,
(readl(&txdesc->cmd_sts) & TXDESC_OWNED_BY_DMA) == 0);
- dma_sync_single_for_cpu((unsigned long)data, len, DMA_TO_DEVICE);
+ dma_sync_single_for_cpu(&port->dev, (unsigned long)data, len, DMA_TO_DEVICE);
if (ret) {
dev_err(&edev->dev, "transmit timeout\n");
return ret;
@@ -287,13 +287,13 @@ static int port_recv(struct eth_device *edev)
}
/* invalidate current receive buffer */
- dma_sync_single_for_cpu((unsigned long)rxdesc->buf_ptr,
+ dma_sync_single_for_cpu(&port->dev, (unsigned long)rxdesc->buf_ptr,
ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE);
/* received packet is padded with two null bytes */
net_receive(edev, rxdesc->buf_ptr + 0x2, rxdesc->byte_cnt - 0x2);
- dma_sync_single_for_device((unsigned long)rxdesc->buf_ptr,
+ dma_sync_single_for_device(&port->dev, (unsigned long)rxdesc->buf_ptr,
ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE);
ret = 0;
@@ -387,10 +387,10 @@ static int port_open(struct eth_device *edev)
return 0;
}
-static int port_probe(struct device_d *parent, struct port_priv *port)
+static int port_probe(struct device *parent, struct port_priv *port)
{
struct orion_gbe *gbe = parent->priv;
- struct device_d *dev = &port->dev;
+ struct device *dev = &port->dev;
u32 reg;
int ret;
@@ -451,7 +451,7 @@ static int port_probe(struct device_d *parent, struct port_priv *port)
dev_set_name(dev, "%08x.ethernet-port", (u32)gbe->regs);
dev->id = port->portno;
dev->parent = parent;
- dev->device_node = port->np;
+ dev->of_node = port->np;
ret = register_device(dev);
if (ret)
return ret;
@@ -473,7 +473,7 @@ static int port_probe(struct device_d *parent, struct port_priv *port)
return 0;
}
-static int orion_gbe_probe(struct device_d *dev)
+static int orion_gbe_probe(struct device *dev)
{
struct orion_gbe *gbe;
struct port_priv *ppriv;
@@ -499,13 +499,13 @@ static int orion_gbe_probe(struct device_d *dev)
* Although untested, the driver should also be able to
* deal with multi-port controllers.
*/
- for_each_child_of_node(dev->device_node, pnp)
+ for_each_child_of_node(dev->of_node, pnp)
gbe->num_ports++;
gbe->ports = xzalloc(gbe->num_ports * sizeof(*gbe->ports));
ppriv = gbe->ports;
- for_each_child_of_node(dev->device_node, pnp) {
+ for_each_child_of_node(dev->of_node, pnp) {
ppriv->np = pnp;
ret = port_probe(dev, ppriv);
@@ -518,7 +518,7 @@ static int orion_gbe_probe(struct device_d *dev)
return 0;
}
-static void orion_gbe_remove(struct device_d *dev)
+static void orion_gbe_remove(struct device *dev)
{
struct orion_gbe *gbe = dev->priv;
int n;
@@ -538,8 +538,9 @@ static struct of_device_id orion_gbe_dt_ids[] = {
{ .compatible = "marvell,kirkwood-eth", },
{ }
};
+MODULE_DEVICE_TABLE(of, orion_gbe_dt_ids);
-static struct driver_d orion_gbe_driver = {
+static struct driver orion_gbe_driver = {
.name = "orion-gbe",
.probe = orion_gbe_probe,
.remove = orion_gbe_remove,
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index cd20e1de27..8e12671801 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -35,6 +35,12 @@ config DP83TD510_PHY
Support for the DP83TD510 Ethernet 10Base-T1L PHY. This PHY supports
a 10M single pair Ethernet connection for up to 1000 meter cable.
+config DP83TG720_PHY
+ tristate "Texas Instruments DP83TG720 Ethernet 1000Base-T1 PHY"
+ help
+ Support for the DP83TG720 Ethernet 10000Base-T1 PHY. This PHY supports
+ a 1000M single pair Ethernet.
+
config LXT_PHY
bool "Driver for the Intel LXT PHYs"
help
@@ -50,6 +56,11 @@ config MICREL_PHY
help
Supports the KSZ9021, VSC8201, KS8001 PHYs.
+config MOTORCOMM_PHY
+ bool "Driver for Motorcomm PHYs"
+ help
+ Currently supports the YT8511 PHY.
+
config NATIONAL_PHY
bool "Driver for National Semiconductor PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 83f46f11d3..ce15e1bab7 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DAVICOM_PHY) += davicom.o
obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MICREL_PHY) += micrel.o
+obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o
obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
obj-$(CONFIG_SMSC_PHY) += smsc.o
@@ -18,4 +19,5 @@ obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o
obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
obj-$(CONFIG_DP83867_PHY) += dp83867.o
obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o
+obj-$(CONFIG_DP83TG720_PHY) += dp83tg720.o
diff --git a/drivers/net/phy/ar8327.c b/drivers/net/phy/ar8327.c
index f13d574b30..7717861c74 100644
--- a/drivers/net/phy/ar8327.c
+++ b/drivers/net/phy/ar8327.c
@@ -132,7 +132,7 @@ static int ar8327n_phy_is_link_alive(struct phy_device *phydev, int phy_addr)
static int ar8327n_phy_setup(struct phy_device *phydev)
{
- struct device_d *dev = &phydev->dev;
+ struct device *dev = &phydev->dev;
int phy_addr;
/* start auto negotiation on each phy */
@@ -194,7 +194,7 @@ static int ar8327n_get_link(struct phy_device *phydev)
static int ar8327n_config_init(struct phy_device *phydev)
{
- struct device_d *dev = &phydev->dev;
+ struct device *dev = &phydev->dev;
int phy_addr = 0;
if (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 93a8bb9df1..8d6b879a27 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -32,6 +32,9 @@
#define AT803X_DEBUG_REG_5 0x05
#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
+#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15)
+
/* AT803x supports either the XTAL input pad, an internal PLL or the
* DSP as clock reference for the clock output pad. The XTAL reference
* is only used for 25 MHz output, all other frequencies need the PLL.
@@ -59,6 +62,9 @@
*/
#define AT8035_CLK_OUT_MASK GENMASK(4, 3)
+#define AT803X_MMD3_SMARTEEE_CTL3 0x805d
+#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8)
+
#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7)
#define AT803X_CLK_OUT_STRENGTH_FULL 0
#define AT803X_CLK_OUT_STRENGTH_HALF 1
@@ -128,6 +134,15 @@ static int at803x_disable_tx_delay(struct phy_device *phydev)
AT803X_DEBUG_TX_CLK_DLY_EN, 0);
}
+static int at803x_hibernation_mode_config(struct phy_device *phydev)
+{
+ /* The default after hardware reset is hibernation mode enabled. After
+ * software reset, the value is retained.
+ */
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
+ AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0);
+}
+
static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id)
{
struct phy_driver *drv = to_phy_driver(phydev->dev.driver);
@@ -138,8 +153,8 @@ static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id)
static int at803x_parse_dt(struct phy_device *phydev)
{
- const struct device_d *dev = &phydev->dev;
- const struct device_node *node = dev->device_node;
+ const struct device *dev = &phydev->dev;
+ const struct device_node *node = dev->of_node;
struct at803x_priv *priv = phydev->priv;
unsigned int sel;
u32 freq, strength;
@@ -221,6 +236,12 @@ static int at803x_probe(struct phy_device *phydev)
return at803x_parse_dt(phydev);
}
+static int at803x_smarteee_config(struct phy_device *phydev)
+{
+ return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
+}
+
static int at803x_clk_out_config(struct phy_device *phydev)
{
struct at803x_priv *priv = phydev->priv;
@@ -229,14 +250,14 @@ static int at803x_clk_out_config(struct phy_device *phydev)
if (!priv->clk_25m_mask)
return 0;
- val = phy_read_mmd_indirect(phydev, AT803X_MMD7_CLK25M, MDIO_MMD_AN);
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M);
if (val < 0)
return val;
val &= ~priv->clk_25m_mask;
val |= priv->clk_25m_reg;
- phy_write_mmd_indirect(phydev, AT803X_MMD7_CLK25M, MDIO_MMD_AN, val);
+ phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val);
return 0;
}
@@ -270,7 +291,25 @@ static int at803x_config_init(struct phy_device *phydev)
if (ret < 0)
return ret;
- return at803x_clk_out_config(phydev);
+ ret = at803x_smarteee_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = at803x_clk_out_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = at803x_hibernation_mode_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* Ar803x extended next page bit is enabled by default. Cisco
+ * multigig switches read this bit and attempt to negotiate 10Gbps
+ * rates even if the next page bit is disabled. This is incorrect
+ * behaviour but we still need to accommodate it. XNP is only needed
+ * for 10Gbps support, so disable XNP.
+ */
+ return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
}
static struct phy_driver at803x_driver[] = {
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 2435df1b47..5dc5bac125 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -1,6 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Driver for the Texas Instruments DP83867 PHY
+// SPDX-License-Identifier: GPL-2.0
+/* Driver for the Texas Instruments DP83867 PHY
*
* Copyright (C) 2015 Texas Instruments Inc.
*/
@@ -13,23 +12,43 @@
#include <linux/mdio.h>
#define DP83867_PHY_ID 0x2000a231
-#define DP83867_DEVADDR MDIO_MMD_VEND2
+#define DP83867_DEVADDR 0x1f
#define MII_DP83867_PHYCTRL 0x10
#define MII_DP83867_PHYSTS 0x11
#define MII_DP83867_MICR 0x12
#define MII_DP83867_ISR 0x13
-#define MII_DP83867_CFG2 0x14
-#define MII_DP83867_BISCR 0x16
-#define DP83867_CTRL 0x1f
+#define DP83867_CFG2 0x14
+#define DP83867_LEDCR1 0x18
+#define DP83867_LEDCR2 0x19
#define DP83867_CFG3 0x1e
+#define DP83867_CTRL 0x1f
/* Extended Registers */
+#define DP83867_FLD_THR_CFG 0x002e
#define DP83867_CFG4 0x0031
+#define DP83867_CFG4_SGMII_ANEG_MASK (BIT(5) | BIT(6))
+#define DP83867_CFG4_SGMII_ANEG_TIMER_11MS (3 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_800US (2 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_2US (1 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_16MS (0 << 5)
+
#define DP83867_RGMIICTL 0x0032
#define DP83867_STRAP_STS1 0x006E
+#define DP83867_STRAP_STS2 0x006f
#define DP83867_RGMIIDCTL 0x0086
+#define DP83867_DSP_FFE_CFG 0x012c
+#define DP83867_RXFCFG 0x0134
+#define DP83867_RXFPMD1 0x0136
+#define DP83867_RXFPMD2 0x0137
+#define DP83867_RXFPMD3 0x0138
+#define DP83867_RXFSOP1 0x0139
+#define DP83867_RXFSOP2 0x013A
+#define DP83867_RXFSOP3 0x013B
#define DP83867_IO_MUX_CFG 0x0170
+#define DP83867_SGMIICTL 0x00D3
+#define DP83867_10M_SGMII_CFG 0x016F
+#define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7)
#define DP83867_SW_RESET BIT(15)
#define DP83867_SW_RESTART BIT(14)
@@ -52,53 +71,86 @@
#define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1)
#define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0)
+/* SGMIICTL bits */
+#define DP83867_SGMII_TYPE BIT(14)
+
+/* RXFCFG bits*/
+#define DP83867_WOL_MAGIC_EN BIT(0)
+#define DP83867_WOL_BCAST_EN BIT(2)
+#define DP83867_WOL_UCAST_EN BIT(4)
+#define DP83867_WOL_SEC_EN BIT(5)
+#define DP83867_WOL_ENH_MAC BIT(7)
+
/* STRAP_STS1 bits */
#define DP83867_STRAP_STS1_RESERVED BIT(11)
+/* STRAP_STS2 bits */
+#define DP83867_STRAP_STS2_CLK_SKEW_TX_MASK GENMASK(6, 4)
+#define DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT 4
+#define DP83867_STRAP_STS2_CLK_SKEW_RX_MASK GENMASK(2, 0)
+#define DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT 0
+#define DP83867_STRAP_STS2_CLK_SKEW_NONE BIT(2)
+#define DP83867_STRAP_STS2_STRAP_FLD BIT(10)
+
/* PHY CTRL bits */
-#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14
-#define DP83867_PHYCR_FIFO_DEPTH_MASK (3 << 14)
-#define DP83867_MDI_CROSSOVER 5
-#define DP83867_MDI_CROSSOVER_AUTO 0b10
-#define DP83867_MDI_CROSSOVER_MDIX 0b01
-#define DP83867_PHYCTRL_SGMIIEN 0x0800
-#define DP83867_PHYCTRL_RXFIFO_SHIFT 12
-#define DP83867_PHYCTRL_TXFIFO_SHIFT 14
+#define DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT 14
+#define DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT 12
+#define DP83867_PHYCR_FIFO_DEPTH_MAX 0x03
+#define DP83867_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14)
+#define DP83867_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12)
#define DP83867_PHYCR_RESERVED_MASK BIT(11)
-
-/* PHY STS bits */
-#define DP83867_PHYSTS_SPEED_1000 BIT(15)
-#define DP83867_PHYSTS_SPEED_100 BIT(14)
-#define DP83867_PHYSTS_DUPLEX_FULL BIT(13)
+#define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10)
/* RGMIIDCTL bits */
+#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf
#define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83867_RGMII_TX_CLK_DELAY_INV (DP83867_RGMII_TX_CLK_DELAY_MAX + 1)
+#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf
+#define DP83867_RGMII_RX_CLK_DELAY_SHIFT 0
+#define DP83867_RGMII_RX_CLK_DELAY_INV (DP83867_RGMII_RX_CLK_DELAY_MAX + 1)
+
+/* IO_MUX_CFG bits */
+#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK 0x1f
+#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0
+#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f
+#define DP83867_IO_MUX_CFG_CLK_O_DISABLE BIT(6)
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8)
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
+
+/* PHY STS bits */
+#define DP83867_PHYSTS_1000 BIT(15)
+#define DP83867_PHYSTS_100 BIT(14)
+#define DP83867_PHYSTS_DUPLEX BIT(13)
+#define DP83867_PHYSTS_LINK BIT(10)
/* CFG2 bits */
-#define DP83867_DOWNSHIFT_EN (BIT(8) | BIT(9))
-#define DP83867_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11))
-#define DP83867_DOWNSHIFT_1_COUNT_VAL 0
-#define DP83867_DOWNSHIFT_2_COUNT_VAL 1
-#define DP83867_DOWNSHIFT_4_COUNT_VAL 2
-#define DP83867_DOWNSHIFT_8_COUNT_VAL 3
+#define DP83867_DOWNSHIFT_EN (BIT(8) | BIT(9))
+#define DP83867_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11))
+#define DP83867_DOWNSHIFT_1_COUNT_VAL 0
+#define DP83867_DOWNSHIFT_2_COUNT_VAL 1
+#define DP83867_DOWNSHIFT_4_COUNT_VAL 2
+#define DP83867_DOWNSHIFT_8_COUNT_VAL 3
+#define DP83867_DOWNSHIFT_1_COUNT 1
+#define DP83867_DOWNSHIFT_2_COUNT 2
+#define DP83867_DOWNSHIFT_4_COUNT 4
+#define DP83867_DOWNSHIFT_8_COUNT 8
+#define DP83867_SGMII_AUTONEG_EN BIT(7)
+
+/* CFG3 bits */
+#define DP83867_CFG3_INT_OE BIT(7)
+#define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9)
/* CFG4 bits */
-#define DP83867_CFG4_SGMII_AUTONEG_TIMER_MASK 0x60
-#define DP83867_CFG4_SGMII_AUTONEG_TIMER_16MS 0x00
-#define DP83867_CFG4_SGMII_AUTONEG_TIMER_2US 0x20
-#define DP83867_CFG4_SGMII_AUTONEG_TIMER_800US 0x40
-#define DP83867_CFG4_SGMII_AUTONEG_TIMER_11MS 0x60
-#define DP83867_CFG4_RESVDBIT7 BIT(7)
-#define DP83867_CFG4_RESVDBIT8 BIT(8)
+#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
-/* IO_MUX_CFG bits */
-#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f
+/* FLD_THR_CFG */
+#define DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK 0x7
-#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0
-#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f
+#define DP83867_LED_COUNT 4
-/* CFG4 bits */
-#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
+/* LED_DRV bits */
+#define DP83867_LED_DRV_EN(x) BIT((x) * 4)
+#define DP83867_LED_DRV_VAL(x) BIT((x) * 4 + 1)
enum {
DP83867_PORT_MIRROING_KEEP,
@@ -107,39 +159,41 @@ enum {
};
struct dp83867_private {
- int rx_id_delay;
- int tx_id_delay;
- int fifo_depth;
+ u32 rx_id_delay;
+ u32 tx_id_delay;
+ u32 tx_fifo_depth;
+ u32 rx_fifo_depth;
int io_impedance;
int port_mirroring;
bool rxctrl_strap_quirk;
+ bool set_clk_output;
+ u32 clk_output_sel;
+ bool sgmii_ref_clk_en;
};
static int dp83867_read_status(struct phy_device *phydev)
{
- int status;
+ int status = phy_read(phydev, MII_DP83867_PHYSTS);
int ret;
- ret = genphy_update_link(phydev);
+ ret = genphy_read_status(phydev);
if (ret)
return ret;
- status = phy_read(phydev, MII_DP83867_PHYSTS);
if (status < 0)
return status;
- phydev->speed = SPEED_10;
- phydev->duplex = DUPLEX_HALF;
+ if (status & DP83867_PHYSTS_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
- if (status & DP83867_PHYSTS_SPEED_1000)
+ if (status & DP83867_PHYSTS_1000)
phydev->speed = SPEED_1000;
- else if (status & DP83867_PHYSTS_SPEED_100)
+ else if (status & DP83867_PHYSTS_100)
phydev->speed = SPEED_100;
-
- if (status & DP83867_PHYSTS_DUPLEX_FULL)
- phydev->duplex = DUPLEX_FULL;
-
- phydev->pause = phydev->asym_pause = 0;
+ else
+ phydev->speed = SPEED_10;
return 0;
}
@@ -147,16 +201,70 @@ static int dp83867_read_status(struct phy_device *phydev)
static int dp83867_config_port_mirroring(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
- u16 val;
-
- val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR);
if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN)
- val |= DP83867_CFG4_PORT_MIRROR_EN;
+ phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+ DP83867_CFG4_PORT_MIRROR_EN);
else
- val &= ~DP83867_CFG4_PORT_MIRROR_EN;
+ phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+ DP83867_CFG4_PORT_MIRROR_EN);
+ return 0;
+}
- phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, val);
+static int dp83867_verify_rgmii_cfg(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 = phydev->priv;
+
+ /* Existing behavior was to use default pin strapping delay in rgmii
+ * mode, but rgmii should have meant no delay. Warn existing users.
+ */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
+ const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_STRAP_STS2);
+ const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
+ const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT;
+
+ if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE ||
+ rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE)
+ phydev_warn(phydev,
+ "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
+ "Should be 'rgmii-id' to use internal delays txskew:%x rxskew:%x\n",
+ txskew, rxskew);
+ }
+
+ /* RX delay *must* be specified if internal delay of RX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) &&
+ dp83867->rx_id_delay == DP83867_RGMII_RX_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,rx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ /* TX delay *must* be specified if internal delay of TX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) &&
+ dp83867->tx_id_delay == DP83867_RGMII_TX_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,tx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dp83867_of_init_io_impedance(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 = phydev->priv;
+ struct device *dev = &phydev->dev;
+ struct device_node *of_node = dev->of_node;
+
+ if (of_property_read_bool(of_node, "ti,max-output-impedance"))
+ dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
+ else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
+ dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN;
+ else
+ dp83867->io_impedance = -1; /* leave at default */
return 0;
}
@@ -164,36 +272,59 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev)
static int dp83867_of_init(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
- struct device_d *dev = &phydev->dev;
- struct device_node *of_node = dev->device_node;
+ struct device *dev = &phydev->dev;
+ struct device_node *of_node = dev->of_node;
int ret;
if (!of_node)
return -ENODEV;
- dp83867->io_impedance = -EINVAL;
-
/* Optional configuration */
- if (of_property_read_bool(of_node, "ti,max-output-impedance"))
- dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
- else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
- dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN;
+ ret = of_property_read_u32(of_node, "ti,clk-output-sel",
+ &dp83867->clk_output_sel);
+ /* If not set, keep default */
+ if (!ret) {
+ dp83867->set_clk_output = true;
+ /* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or
+ * DP83867_CLK_O_SEL_OFF.
+ */
+ if (dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK &&
+ dp83867->clk_output_sel != DP83867_CLK_O_SEL_OFF) {
+ phydev_err(phydev, "ti,clk-output-sel value %u out of range\n",
+ dp83867->clk_output_sel);
+ return -EINVAL;
+ }
+ }
+
+ ret = dp83867_of_init_io_impedance(phydev);
+ if (ret)
+ return ret;
- dp83867->rxctrl_strap_quirk =
- of_property_read_bool(of_node,
- "ti,dp83867-rxctrl-strap-quirk");
+ dp83867->rxctrl_strap_quirk = of_property_read_bool(of_node,
+ "ti,dp83867-rxctrl-strap-quirk");
+ dp83867->sgmii_ref_clk_en = of_property_read_bool(of_node,
+ "ti,sgmii-ref-clock-output-enable");
+
+ dp83867->rx_id_delay = DP83867_RGMII_RX_CLK_DELAY_INV;
ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
&dp83867->rx_id_delay);
- if (ret && (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
- phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID))
- return ret;
+ if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ phydev_err(phydev,
+ "ti,rx-internal-delay value of %u out of range\n",
+ dp83867->rx_id_delay);
+ return -EINVAL;
+ }
+ dp83867->tx_id_delay = DP83867_RGMII_TX_CLK_DELAY_INV;
ret = of_property_read_u32(of_node, "ti,tx-internal-delay",
&dp83867->tx_id_delay);
- if (ret && (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
- phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID))
- return ret;
+ if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ phydev_err(phydev,
+ "ti,tx-internal-delay value of %u out of range\n",
+ dp83867->tx_id_delay);
+ return -EINVAL;
+ }
if (of_property_read_bool(of_node, "enet-phy-lane-swap"))
dp83867->port_mirroring = DP83867_PORT_MIRROING_EN;
@@ -201,125 +332,252 @@ static int dp83867_of_init(struct phy_device *phydev)
if (of_property_read_bool(of_node, "enet-phy-lane-no-swap"))
dp83867->port_mirroring = DP83867_PORT_MIRROING_DIS;
- return of_property_read_u32(of_node, "ti,fifo-depth",
- &dp83867->fifo_depth);
+ ret = of_property_read_u32(of_node, "ti,fifo-depth",
+ &dp83867->tx_fifo_depth);
+ if (ret) {
+ ret = of_property_read_u32(of_node, "tx-fifo-depth",
+ &dp83867->tx_fifo_depth);
+ if (ret)
+ dp83867->tx_fifo_depth =
+ DP83867_PHYCR_FIFO_DEPTH_4_B_NIB;
+ }
+
+ if (dp83867->tx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) {
+ phydev_err(phydev, "tx-fifo-depth value %u out of range\n",
+ dp83867->tx_fifo_depth);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(of_node, "rx-fifo-depth",
+ &dp83867->rx_fifo_depth);
+ if (ret)
+ dp83867->rx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB;
+
+ if (dp83867->rx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) {
+ phydev_err(phydev, "rx-fifo-depth value %u out of range\n",
+ dp83867->rx_fifo_depth);
+ return -EINVAL;
+ }
+
+ return 0;
}
-static inline bool phy_interface_is_sgmii(struct phy_device *phydev)
+static int dp83867_probe(struct phy_device *phydev)
{
- return phydev->interface == PHY_INTERFACE_MODE_SGMII ||
- phydev->interface == PHY_INTERFACE_MODE_QSGMII;
+ struct dp83867_private *dp83867;
+
+ dp83867 = xzalloc(sizeof(*dp83867));
+
+ phydev->priv = dp83867;
+
+ return dp83867_of_init(phydev);
}
static int dp83867_config_init(struct phy_device *phydev)
{
- struct dp83867_private *dp83867;
- int ret;
- u16 val, delay;
+ struct dp83867_private *dp83867 = phydev->priv;
+ int ret, val, bs;
+ u16 delay;
- if (!phydev->priv) {
- dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL);
- if (!dp83867)
- return -ENOMEM;
+ /* Force speed optimization for the PHY even if it strapped */
+ ret = phy_modify(phydev, DP83867_CFG2, DP83867_DOWNSHIFT_EN,
+ DP83867_DOWNSHIFT_EN);
+ if (ret)
+ return ret;
- phydev->priv = dp83867;
- ret = dp83867_of_init(phydev);
+ ret = dp83867_verify_rgmii_cfg(phydev);
+ if (ret)
+ return ret;
+
+ /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */
+ if (dp83867->rxctrl_strap_quirk)
+ phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+ BIT(7));
+
+ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS2);
+ if (bs & DP83867_STRAP_STS2_STRAP_FLD) {
+ /* When using strap to enable FLD, the ENERGY_LOST_FLD_THR will
+ * be set to 0x2. This may causes the PHY link to be unstable -
+ * the default value 0x1 need to be restored.
+ */
+ ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
+ DP83867_FLD_THR_CFG,
+ DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK,
+ 0x1);
if (ret)
return ret;
- } else {
- dp83867 = phydev->priv;
}
- /* Restart the PHY. */
- val = phy_read(phydev, DP83867_CTRL);
- phy_write(phydev, DP83867_CTRL, val | DP83867_SW_RESTART);
+ if (phy_interface_is_rgmii(phydev) ||
+ phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ val = phy_read(phydev, MII_DP83867_PHYCTRL);
+ if (val < 0)
+ return val;
+
+ val &= ~DP83867_PHYCR_TX_FIFO_DEPTH_MASK;
+ val |= (dp83867->tx_fifo_depth <<
+ DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ val &= ~DP83867_PHYCR_RX_FIFO_DEPTH_MASK;
+ val |= (dp83867->rx_fifo_depth <<
+ DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT);
+ }
- if (dp83867->rxctrl_strap_quirk) {
- val = phy_read_mmd_indirect(phydev, DP83867_CFG4,
- DP83867_DEVADDR);
- val &= ~BIT(7);
- phy_write_mmd_indirect(phydev, DP83867_CFG4,
- DP83867_DEVADDR, val);
+ ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
+ if (ret)
+ return ret;
}
if (phy_interface_is_rgmii(phydev)) {
- val = DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER |
- dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT;
+ val = phy_read(phydev, MII_DP83867_PHYCTRL);
+ if (val < 0)
+ return val;
+
+ /* The code below checks if "port mirroring" N/A MODE4 has been
+ * enabled during power on bootstrap.
+ *
+ * Such N/A mode enabled by mistake can put PHY IC in some
+ * internal testing mode and disable RGMII transmission.
+ *
+ * In this particular case one needs to check STRAP_STS1
+ * register's bit 11 (marked as RESERVED).
+ */
+
+ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1);
+ if (bs & DP83867_STRAP_STS1_RESERVED)
+ val &= ~DP83867_PHYCR_RESERVED_MASK;
+
ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
if (ret)
return ret;
- val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR);
+ /* If rgmii mode with no internal delay is selected, we do NOT use
+ * aligned mode as one might expect. Instead we use the PHY's default
+ * based on pin strapping. And the "mode 0" default is to *use*
+ * internal delay with a value of 7 (2.00 ns).
+ *
+ * Set up RGMII delays
+ */
+ val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL);
+
+ val &= ~(DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);
- switch (phydev->interface) {
- case PHY_INTERFACE_MODE_RGMII_ID:
- val |= DP83867_RGMII_TX_CLK_DELAY_EN |
- DP83867_RGMII_RX_CLK_DELAY_EN;
- break;
- case PHY_INTERFACE_MODE_RGMII_TXID:
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
val |= DP83867_RGMII_TX_CLK_DELAY_EN;
- break;
- case PHY_INTERFACE_MODE_RGMII_RXID:
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
val |= DP83867_RGMII_RX_CLK_DELAY_EN;
- break;
- default:
- break;
- }
- phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR, val);
- delay = (dp83867->rx_id_delay |
- (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val);
- phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL,
- DP83867_DEVADDR, delay);
+ delay = 0;
+ if (dp83867->rx_id_delay != DP83867_RGMII_RX_CLK_DELAY_INV)
+ delay |= dp83867->rx_id_delay;
+ if (dp83867->tx_id_delay != DP83867_RGMII_TX_CLK_DELAY_INV)
+ delay |= dp83867->tx_id_delay <<
+ DP83867_RGMII_TX_CLK_DELAY_SHIFT;
- if (dp83867->io_impedance >= 0) {
- val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG,
- DP83867_DEVADDR);
- val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
- val |= (dp83867->io_impedance &
- DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL);
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL,
+ delay);
+ }
- phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG,
- DP83867_DEVADDR, val);
- }
- } else if (phy_interface_is_sgmii(phydev)) {
- phy_write(phydev, MII_BMCR,
- BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000);
+ /* If specified, set io impedance */
+ if (dp83867->io_impedance >= 0)
+ phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG,
+ DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK,
+ dp83867->io_impedance);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* For support SPEED_10 in SGMII mode
+ * DP83867_10M_SGMII_RATE_ADAPT bit
+ * has to be cleared by software. That
+ * does not affect SPEED_100 and
+ * SPEED_1000.
+ */
+ ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
+ DP83867_10M_SGMII_CFG,
+ DP83867_10M_SGMII_RATE_ADAPT_MASK,
+ 0);
+ if (ret)
+ return ret;
- phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR, 0x0);
+ /* After reset SGMII Autoneg timer is set to 2us (bits 6 and 5
+ * are 01). That is not enough to finalize autoneg on some
+ * devices. Increase this timer duration to maximum 16ms.
+ */
+ ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
+ DP83867_CFG4,
+ DP83867_CFG4_SGMII_ANEG_MASK,
+ DP83867_CFG4_SGMII_ANEG_TIMER_16MS);
- val = DP83867_PHYCTRL_SGMIIEN |
- DP83867_MDI_CROSSOVER_MDIX << DP83867_MDI_CROSSOVER |
- dp83867->fifo_depth << DP83867_PHYCTRL_RXFIFO_SHIFT |
- dp83867->fifo_depth << DP83867_PHYCTRL_TXFIFO_SHIFT;
+ if (ret)
+ return ret;
- phy_write(phydev, MII_DP83867_PHYCTRL, val);
- phy_write(phydev, MII_DP83867_BISCR, 0x0);
+ val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL);
+ /* SGMII type is set to 4-wire mode by default.
+ * If we place appropriate property in dts (see above)
+ * switch on 6-wire mode.
+ */
+ if (dp83867->sgmii_ref_clk_en)
+ val |= DP83867_SGMII_TYPE;
+ else
+ val &= ~DP83867_SGMII_TYPE;
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL, val);
+
+ /* This is a SW workaround for link instability if RX_CTRL is
+ * not strapped to mode 3 or 4 in HW. This is required for SGMII
+ * in addition to clearing bit 7, handled above.
+ */
+ if (dp83867->rxctrl_strap_quirk)
+ phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+ BIT(8));
}
- val = phy_read(phydev, MII_DP83867_CFG2);
- val |= DP83867_DOWNSHIFT_EN;
- phy_write(phydev, MII_DP83867_CFG2, val);
+ val = phy_read(phydev, DP83867_CFG3);
+
+ val |= DP83867_CFG3_ROBUST_AUTO_MDIX;
+ phy_write(phydev, DP83867_CFG3, val);
if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP)
dp83867_config_port_mirroring(phydev);
+ /* Clock output selection if muxing property is set */
+ if (dp83867->set_clk_output) {
+ u16 mask = DP83867_IO_MUX_CFG_CLK_O_DISABLE;
+
+ if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) {
+ val = DP83867_IO_MUX_CFG_CLK_O_DISABLE;
+ } else {
+ mask |= DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
+ val = dp83867->clk_output_sel <<
+ DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT;
+ }
+
+ phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG,
+ mask, val);
+ }
+
return 0;
}
static struct phy_driver dp83867_driver[] = {
{
- .phy_id = DP83867_PHY_ID,
- .phy_id_mask = 0xfffffff0,
- .drv.name = "TI DP83867",
- .features = PHY_GBIT_FEATURES,
- .config_init = dp83867_config_init,
- .read_status = dp83867_read_status,
+ .phy_id = DP83867_PHY_ID,
+ .phy_id_mask = 0xfffffff0,
+ .drv.name = "TI DP83867",
+ .features = PHY_GBIT_FEATURES,
+
+ .probe = dp83867_probe,
+ .config_init = dp83867_config_init,
+
+ .read_status = dp83867_read_status,
},
};
-
device_phy_drivers(dp83867_driver);
+
+MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/phy/dp83tg720.c b/drivers/net/phy/dp83tg720.c
new file mode 100644
index 0000000000..0571f4cb52
--- /dev/null
+++ b/drivers/net/phy/dp83tg720.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Driver for the Texas Instruments DP83TG720 PHY
+ * Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
+ */
+#include <common.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+
+#define DP83TG720S_PHY_ID 0x2000a284
+
+/* MDIO_MMD_VEND2 registers */
+#define DP83TG720S_MII_REG_10 0x10
+#define DP83TG720S_LINK_STATUS BIT(0)
+
+#define DP83TG720S_RGMII_DELAY_CTRL 0x602
+/* In RGMII mode, Enable or disable the internal delay for RXD */
+#define DP83TG720S_RGMII_RX_CLK_SEL BIT(1)
+/* In RGMII mode, Enable or disable the internal delay for TXD */
+#define DP83TG720S_RGMII_TX_CLK_SEL BIT(0)
+
+#define DP83TG720S_PHY_RESET 0x1f
+#define DP83TG720S_HW_RESET BIT(15)
+
+static int dp83tg720_config_rgmii_delay(struct phy_device *phydev)
+{
+ u16 rgmii_delay_mask;
+ u16 rgmii_delay = 0;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ rgmii_delay = 0;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL |
+ DP83TG720S_RGMII_TX_CLK_SEL;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ rgmii_delay = DP83TG720S_RGMII_TX_CLK_SEL;
+ break;
+ default:
+ return 0;
+ }
+
+ rgmii_delay_mask = DP83TG720S_RGMII_RX_CLK_SEL |
+ DP83TG720S_RGMII_TX_CLK_SEL;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
+ DP83TG720S_RGMII_DELAY_CTRL, rgmii_delay_mask,
+ rgmii_delay);
+}
+
+static int dp83tg720_phy_init(struct phy_device *phydev)
+{
+ /* HW reset is needed to recover link if previous link was lost. SW
+ * reset is not enough.
+ */
+ phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET);
+
+ phydev->supported = SUPPORTED_1000baseT_Full;
+ phydev->advertising = SUPPORTED_1000baseT_Full;
+
+ if (phy_interface_is_rgmii(phydev))
+ return dp83tg720_config_rgmii_delay(phydev);
+
+ return 0;
+}
+
+static int dp83tg720_read_status(struct phy_device *phydev)
+{
+ u16 phy_sts;
+
+ phy_sts = phy_read(phydev, DP83TG720S_MII_REG_10);
+ phydev->link = !!(phy_sts & DP83TG720S_LINK_STATUS);
+ if (!phydev->link) {
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+
+ /* According to the "DP83TC81x, DP83TG72x Software
+ * Implementation Guide", the PHY needs to be reset after a
+ * link loss or if no link is created after at least 100ms.
+ */
+ dp83tg720_phy_init(phydev);
+ return 0;
+ }
+
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_1000;
+
+ return 0;
+}
+
+static struct phy_driver dp83tg720_driver[] = {
+ {
+ PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID),
+ .drv.name = "TI DP83TG720S",
+ .read_status = dp83tg720_read_status,
+ .config_init = dp83tg720_phy_init,
+ }
+};
+device_phy_drivers(dp83tg720_driver);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index d81632dfc7..c0b819b109 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -188,10 +188,10 @@ static int marvell_of_reg_init(struct phy_device *phydev)
const __be32 *paddr;
int len, i, saved_page, current_page, page_changed, ret;
- if (!phydev->dev.device_node)
+ if (!phydev->dev.of_node)
return 0;
- paddr = of_get_property(phydev->dev.device_node,
+ paddr = of_get_property(phydev->dev.of_node,
"marvell,reg-init", &len);
if (!paddr || len < (4 * sizeof(*paddr)))
return 0;
diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
index 839a7d1eb8..656557589d 100644
--- a/drivers/net/phy/mdio-bitbang.c
+++ b/drivers/net/phy/mdio-bitbang.c
@@ -158,7 +158,7 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
reg = mdiobb_cmd_addr(ctrl, phy, reg);
mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
} else
- mdiobb_cmd(ctrl, MDIO_READ, phy, reg);
+ mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg);
ctrl->ops->set_mdio_dir(ctrl, 0);
@@ -188,7 +188,7 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
reg = mdiobb_cmd_addr(ctrl, phy, reg);
mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
} else
- mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
+ mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg);
/* send the turnaround (10) */
mdiobb_send_bit(ctrl, 1);
@@ -219,6 +219,10 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
bus->write = mdiobb_write;
bus->reset = mdiobb_reset;
bus->priv = ctrl;
+ if (!ctrl->override_op_c22) {
+ ctrl->op_c22_read = MDIO_READ;
+ ctrl->op_c22_write = MDIO_WRITE;
+ }
return bus;
}
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 80d2394f4b..a28fb961e4 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -42,7 +42,7 @@ struct mdio_gpio_info {
int mdc_active_low, mdio_active_low, mdo_active_low;
};
-static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev)
+static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device *dev)
{
int ret;
enum of_gpio_flags flags;
@@ -50,7 +50,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev)
info = xzalloc(sizeof(*info));
- ret = of_get_gpio_flags(dev->device_node, 0, &flags);
+ ret = of_get_gpio_flags(dev->of_node, 0, &flags);
if (ret < 0) {
dev_dbg(dev, "failed to get MDC information from DT\n");
goto free_info;
@@ -59,7 +59,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev)
info->mdc = ret;
info->mdc_active_low = flags & OF_GPIO_ACTIVE_LOW;
- ret = of_get_gpio_flags(dev->device_node, 1, &flags);
+ ret = of_get_gpio_flags(dev->of_node, 1, &flags);
if (ret < 0) {
dev_dbg(dev, "failed to get MDIO information from DT\n");
goto free_info;
@@ -68,7 +68,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev)
info->mdio = ret;
info->mdio_active_low = flags & OF_GPIO_ACTIVE_LOW;
- ret = of_get_gpio_flags(dev->device_node, 2, &flags);
+ ret = of_get_gpio_flags(dev->of_node, 2, &flags);
if (ret > 0) {
dev_dbg(dev, "found MDO information in DT\n");
info->mdo = ret;
@@ -142,10 +142,10 @@ static struct mdiobb_ops mdio_gpio_ops = {
.get_mdio_data = mdio_get,
};
-static int mdio_gpio_probe(struct device_d *dev)
+static int mdio_gpio_probe(struct device *dev)
{
int ret;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct mdio_gpio_info *info;
struct mii_bus *bus;
@@ -193,9 +193,16 @@ static int mdio_gpio_probe(struct device_d *dev)
goto free_mdo;
}
+ if (np &&
+ of_device_is_compatible(np, "microchip,mdio-smi0")) {
+ info->ctrl.op_c22_read = 0;
+ info->ctrl.op_c22_write = 0;
+ info->ctrl.override_op_c22 = 1;
+ }
+
bus = alloc_mdio_bitbang(&info->ctrl);
bus->parent = dev;
- bus->dev.device_node = np;
+ bus->dev.of_node = np;
dev->priv = bus;
@@ -217,10 +224,12 @@ free_info:
static const struct of_device_id gpio_mdio_dt_ids[] = {
{ .compatible = "virtual,mdio-gpio", },
+ { .compatible = "microchip,mdio-smi0" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, gpio_mdio_dt_ids);
-static struct driver_d mdio_gpio_driver = {
+static struct driver mdio_gpio_driver = {
.name = "mdio-gpio",
.probe = mdio_gpio_probe,
.of_compatible = DRV_OF_COMPAT(gpio_mdio_dt_ids),
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index a36782c0b6..3dd04830a5 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -67,14 +67,14 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
return 0;
}
-static int mdio_mux_gpio_probe(struct device_d *dev)
+static int mdio_mux_gpio_probe(struct device *dev)
{
struct mdio_mux_gpio_state *s;
int i, r;
s = xzalloc(sizeof(*s));
- s->gpios_num = of_gpio_count(dev->device_node);
+ s->gpios_num = of_gpio_count(dev->of_node);
if (s->gpios_num <= 0) {
dev_err(dev, "No GPIOs specified\n");
r = -EINVAL;
@@ -86,7 +86,7 @@ static int mdio_mux_gpio_probe(struct device_d *dev)
for (i = 0; i < s->gpios_num; i++) {
enum of_gpio_flags flags;
- r = of_get_gpio_flags(dev->device_node, i, &flags);
+ r = of_get_gpio_flags(dev->of_node, i, &flags);
if (!gpio_is_valid(r)) {
r = (r < 0) ? r : -EINVAL;
goto free_mem;
@@ -105,7 +105,7 @@ static int mdio_mux_gpio_probe(struct device_d *dev)
goto free_gpios;
- r = mdio_mux_init(dev, dev->device_node,
+ r = mdio_mux_init(dev, dev->of_node,
mdio_mux_gpio_switch_fn, s, NULL);
if (r < 0)
goto free_gpios;
@@ -126,8 +126,9 @@ static const struct of_device_id mdio_mux_gpio_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, mdio_mux_gpio_match);
-static struct driver_d mdio_mux_gpio_driver = {
+static struct driver mdio_mux_gpio_driver = {
.name = "mdio-mux-gpio",
.probe = mdio_mux_gpio_probe,
.of_compatible = mdio_mux_gpio_match,
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 1e6278ef35..c4088c16ca 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -56,7 +56,7 @@ static int mdio_mux_write(struct mii_bus *bus, int phy_id,
return mdio_mux_read_or_write(bus, phy_id, regnum, &val);
}
-int mdio_mux_init(struct device_d *dev,
+int mdio_mux_init(struct device *dev,
struct device_node *mux_node,
int (*switch_fn)(int cur, int desired, void *data),
void *data,
@@ -117,7 +117,7 @@ int mdio_mux_init(struct device_d *dev,
cb->mii_bus.parent = dev;
cb->mii_bus.read = mdio_mux_read;
cb->mii_bus.write = mdio_mux_write;
- cb->mii_bus.dev.device_node = child_bus_node;
+ cb->mii_bus.dev.of_node = child_bus_node;
r = mdiobus_register(&cb->mii_bus);
if (r) {
diff --git a/drivers/net/phy/mdio-mvebu.c b/drivers/net/phy/mdio-mvebu.c
index 289ff4b05d..cd90ddd221 100644
--- a/drivers/net/phy/mdio-mvebu.c
+++ b/drivers/net/phy/mdio-mvebu.c
@@ -103,7 +103,7 @@ static int mvebu_mdio_write(struct mii_bus *bus, int addr, int reg, u16 data)
return 0;
}
-static int mvebu_mdio_probe(struct device_d *dev)
+static int mvebu_mdio_probe(struct device *dev)
{
struct mdio_priv *priv;
@@ -119,7 +119,7 @@ static int mvebu_mdio_probe(struct device_d *dev)
return PTR_ERR(priv->clk);
clk_enable(priv->clk);
- priv->miibus.dev.device_node = dev->device_node;
+ priv->miibus.dev.of_node = dev->of_node;
priv->miibus.priv = priv;
priv->miibus.parent = dev;
priv->miibus.read = mvebu_mdio_read;
@@ -128,7 +128,7 @@ static int mvebu_mdio_probe(struct device_d *dev)
return mdiobus_register(&priv->miibus);
}
-static void mvebu_mdio_remove(struct device_d *dev)
+static void mvebu_mdio_remove(struct device *dev)
{
struct mdio_priv *priv = dev->priv;
@@ -141,8 +141,9 @@ static struct of_device_id mvebu_mdio_dt_ids[] = {
{ .compatible = "marvell,orion-mdio" },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_mdio_dt_ids);
-static struct driver_d mvebu_mdio_driver = {
+static struct driver mvebu_mdio_driver = {
.name = "mvebu-mdio",
.probe = mvebu_mdio_probe,
.remove = mvebu_mdio_remove,
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index e37ab79f3e..eed7c779e7 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -19,6 +19,7 @@
#include <clock.h>
#include <net.h>
#include <errno.h>
+#include <linux/mdio.h>
#include <linux/phy.h>
#include <linux/err.h>
#include <of_device.h>
@@ -29,7 +30,57 @@
LIST_HEAD(mii_bus_list);
-int mdiobus_detect(struct device_d *dev)
+static struct phy_device *mdio_device_create(struct mii_bus *bus, int addr)
+{
+ struct phy_device *phydev;
+
+ phydev = xzalloc(sizeof(*phydev));
+
+ phydev->addr = addr;
+ phydev->bus = bus;
+ phydev->dev.bus = &mdio_bus_type;
+
+ dev_set_name(&phydev->dev, "mdio%d-dev%02x", phydev->bus->dev.id,
+ phydev->addr);
+ phydev->dev.id = DEVICE_ID_SINGLE;
+
+ return phydev;
+}
+
+static int mdio_register_device(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->registered)
+ return -EBUSY;
+
+ if (!phydev->dev.parent)
+ phydev->dev.parent = &phydev->bus->dev;
+
+ ret = register_device(&phydev->dev);
+ if (ret)
+ return ret;
+
+ if (phydev->bus)
+ phydev->bus->phy_map[phydev->addr] = phydev;
+
+ phydev->registered = 1;
+
+ if (phydev->dev.driver)
+ return 0;
+
+ return ret;
+}
+
+int mdio_driver_register(struct phy_driver *phydrv)
+{
+ phydrv->drv.bus = &mdio_bus_type;
+ phydrv->is_phy = false;
+
+ return register_driver(&phydrv->drv);
+}
+
+int mdiobus_detect(struct device *dev)
{
struct mii_bus *mii = to_mii_bus(dev);
int i, ret;
@@ -68,7 +119,8 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
* Associate the OF node with the device structure so it
* can be looked up later
*/
- phy->dev.device_node = child;
+ child->dev = &phy->dev;
+ phy->dev.of_node = child;
/*
* All data is now stored in the phy struct;
@@ -84,6 +136,29 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
return 0;
}
+static int of_mdiobus_register_device(struct mii_bus *mdio,
+ struct device_node *child, u32 addr)
+{
+ struct phy_device *mdiodev;
+ int ret;
+
+ mdiodev = mdio_device_create(mdio, addr);
+ if (IS_ERR(mdiodev))
+ return PTR_ERR(mdiodev);
+
+ child->dev = &mdiodev->dev;
+ mdiodev->dev.of_node = child;
+
+ ret = mdio_register_device(mdiodev);
+ if (ret)
+ return ret;
+
+ dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n",
+ child->name, addr);
+
+ return 0;
+}
+
/*
* Node is considered a PHY node if:
* o Compatible string of "ethernet-phy-idX.X"
@@ -176,36 +251,27 @@ static int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
/* Loop over the child nodes and register a phy_device for each one */
for_each_available_child_of_node(np, child) {
- if (!of_mdiobus_child_is_phy(child)) {
- if (of_get_property(child, "compatible", NULL)) {
- if (!of_platform_device_create(child,
- &mdio->dev)) {
- dev_err(&mdio->dev,
- "Failed to create device "
- "for %s\n",
- child->full_name);
- }
- }
-
- continue;
- }
-
ret = of_property_read_u32(child, "reg", &addr);
if (ret) {
- dev_dbg(&mdio->dev, "%s has invalid PHY address\n",
- child->full_name);
+ dev_dbg(&mdio->dev, "%pOF has invalid PHY address\n",
+ child);
continue;
}
if (addr >= PHY_MAX_ADDR) {
- dev_err(&mdio->dev, "%s PHY address %i is too large\n",
- child->full_name, addr);
+ dev_err(&mdio->dev, "%pOF PHY address %i is too large\n",
+ child, addr);
continue;
}
of_pinctrl_select_state_default(child);
- of_mdiobus_reset_phy(mdio, child);
- of_mdiobus_register_phy(mdio, child, addr);
+
+ if (of_mdiobus_child_is_phy(child)) {
+ of_mdiobus_reset_phy(mdio, child);
+ of_mdiobus_register_phy(mdio, child, addr);
+ } else {
+ of_mdiobus_register_device(mdio, child, addr);
+ }
}
return 0;
@@ -250,16 +316,14 @@ int mdiobus_register(struct mii_bus *bus)
pr_info("%s: probed\n", dev_name(&bus->dev));
- if (bus->dev.device_node) {
+ if (!bus->dev.of_node)
+ bus->dev.of_node = bus->parent->of_node;
+
+ if (bus->dev.of_node) {
+ bus->dev.of_node->dev = &bus->dev;
+
/* Register PHY's as child node to mdio node */
- of_mdiobus_register(bus, bus->dev.device_node);
- }
- else if (bus->parent->device_node) {
- /*
- * Register PHY's as child node to the ethernet node,
- * if there was no mdio node
- */
- of_mdiobus_register(bus, bus->parent->device_node);
+ of_mdiobus_register(bus, bus->dev.of_node);
}
return 0;
@@ -335,7 +399,7 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np)
return NULL;
for_each_mii_bus(mii)
- if (mii->dev.device_node == mdio_bus_np)
+ if (mii->dev.of_node == mdio_bus_np)
return mii;
return NULL;
@@ -351,14 +415,18 @@ EXPORT_SYMBOL(of_mdio_find_bus);
* Description: Given a PHY device, and a PHY driver, return 0 if
* the driver supports the device. Otherwise, return 1.
*/
-static int mdio_bus_match(struct device_d *dev, struct driver_d *drv)
+static int mdio_bus_match(struct device *dev, struct driver *drv)
{
struct phy_device *phydev = to_phy_device(dev);
struct phy_driver *phydrv = to_phy_driver(drv);
- if ((phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->phy_id & phydrv->phy_id_mask))
+ if (phydrv->is_phy) {
+ if ((phydrv->phy_id & phydrv->phy_id_mask) ==
+ (phydev->phy_id & phydrv->phy_id_mask))
return 0;
+ } else {
+ return device_match(dev, drv);
+ }
return 1;
}
@@ -441,7 +509,7 @@ static struct cdev_operations phydev_ops = {
static void of_set_phy_supported(struct phy_device *phydev)
{
- struct device_node *node = phydev->dev.device_node;
+ struct device_node *node = phydev->dev.of_node;
u32 max_speed;
if (!IS_ENABLED(CONFIG_OFDEVICE))
@@ -472,7 +540,7 @@ static void of_set_phy_supported(struct phy_device *phydev)
}
}
-static int mdio_bus_probe(struct device_d *_dev)
+static int mdio_bus_probe(struct device *_dev)
{
struct phy_device *dev = to_phy_device(_dev);
struct phy_driver *drv = to_phy_driver(_dev->driver);
@@ -526,7 +594,7 @@ err:
return ret;
}
-static void mdio_bus_remove(struct device_d *_dev)
+static void mdio_bus_remove(struct device *_dev)
{
struct phy_device *dev = to_phy_device(_dev);
struct phy_driver *drv = to_phy_driver(_dev->driver);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 8a30faa92b..a203669353 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -128,8 +128,8 @@ static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val)
/* Handle LED mode, shift = position of first led mode bit, usually 4 or 14 */
static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift)
{
- const struct device_d *dev = &phydev->dev;
- const struct device_node *of_node = dev->device_node ? : dev->parent->device_node;
+ const struct device *dev = &phydev->dev;
+ const struct device_node *of_node = dev->of_node ? : dev->parent->of_node;
u32 val;
if (!of_property_read_u32(of_node, "micrel,led-mode", &val)) {
@@ -144,7 +144,7 @@ static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift)
static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
{
- const struct device_d *dev = &phydev->dev;
+ const struct device *dev = &phydev->dev;
int rc, temp, shift;
switch (reg) {
@@ -179,7 +179,7 @@ out:
*/
static int kszphy_broadcast_disable(struct phy_device *phydev)
{
- const struct device_d *dev = &phydev->dev;
+ const struct device *dev = &phydev->dev;
int ret;
ret = phy_read(phydev, MII_KSZPHY_OMSO);
@@ -196,7 +196,7 @@ out:
static int kszphy_nand_tree_disable(struct phy_device *phydev)
{
- const struct device_d *dev = &phydev->dev;
+ const struct device *dev = &phydev->dev;
int ret;
ret = phy_read(phydev, MII_KSZPHY_OMSO);
@@ -278,8 +278,8 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev,
static int ksz9021_config_init(struct phy_device *phydev)
{
- const struct device_d *dev = &phydev->dev;
- const struct device_node *of_node = dev->device_node;
+ const struct device *dev = &phydev->dev;
+ const struct device_node *of_node = dev->of_node;
const char *clk_pad_skew_names[] = {
"txen-skew-ps", "txc-skew-ps",
"rxdv-skew-ps", "rxc-skew-ps"
@@ -293,8 +293,8 @@ static int ksz9021_config_init(struct phy_device *phydev)
"txd2-skew-ps", "txd3-skew-ps"
};
- if (!of_node && dev->parent->device_node)
- of_node = dev->parent->device_node;
+ if (!of_node && dev->parent->of_node)
+ of_node = dev->parent->of_node;
if (of_node) {
ksz9021_load_values_from_of(phydev, of_node,
@@ -387,7 +387,7 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev,
return 0;
if (matches < numfields)
- newval = phy_read_mmd_indirect(phydev, reg, MDIO_MMD_WIS);
+ newval = phy_read_mmd(phydev, MDIO_MMD_WIS, reg);
else
newval = 0;
@@ -401,15 +401,15 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev,
<< (field_sz * i));
}
- phy_write_mmd_indirect(phydev, reg, MDIO_MMD_WIS, newval);
+ phy_write_mmd(phydev, MDIO_MMD_WIS, reg, newval);
return 0;
}
static int ksz9031_center_flp_timing(struct phy_device *phydev)
{
/* Center KSZ9031RNX FLP timing at 16ms. */
- phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_HI, 0, 0x0006);
- phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_LO, 0, 0x1a80);
+ phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI, 0x0006);
+ phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO, 0x1a80);
return genphy_restart_aneg(phydev);
}
@@ -447,32 +447,32 @@ static int ksz9031_config_rgmii_delay(struct phy_device *phydev)
return 0;
}
- phy_write_mmd_indirect(phydev, MII_KSZ9031RN_CONTROL_PAD_SKEW, 2,
- FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) |
- FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx));
-
- phy_write_mmd_indirect(phydev, MII_KSZ9031RN_RX_DATA_PAD_SKEW, 2,
- FIELD_PREP(MII_KSZ9031RN_RXD3, rx) |
- FIELD_PREP(MII_KSZ9031RN_RXD2, rx) |
- FIELD_PREP(MII_KSZ9031RN_RXD1, rx) |
- FIELD_PREP(MII_KSZ9031RN_RXD0, rx));
-
- phy_write_mmd_indirect(phydev, MII_KSZ9031RN_TX_DATA_PAD_SKEW, 2,
- FIELD_PREP(MII_KSZ9031RN_TXD3, tx) |
- FIELD_PREP(MII_KSZ9031RN_TXD2, tx) |
- FIELD_PREP(MII_KSZ9031RN_TXD1, tx) |
- FIELD_PREP(MII_KSZ9031RN_TXD0, tx));
-
- phy_write_mmd_indirect(phydev, MII_KSZ9031RN_CLK_PAD_SKEW, 2,
- FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) |
- FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk));
+ phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_CONTROL_PAD_SKEW,
+ FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) |
+ FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx));
+
+ phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_RX_DATA_PAD_SKEW,
+ FIELD_PREP(MII_KSZ9031RN_RXD3, rx) |
+ FIELD_PREP(MII_KSZ9031RN_RXD2, rx) |
+ FIELD_PREP(MII_KSZ9031RN_RXD1, rx) |
+ FIELD_PREP(MII_KSZ9031RN_RXD0, rx));
+
+ phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_TX_DATA_PAD_SKEW,
+ FIELD_PREP(MII_KSZ9031RN_TXD3, tx) |
+ FIELD_PREP(MII_KSZ9031RN_TXD2, tx) |
+ FIELD_PREP(MII_KSZ9031RN_TXD1, tx) |
+ FIELD_PREP(MII_KSZ9031RN_TXD0, tx));
+
+ phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_CLK_PAD_SKEW,
+ FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) |
+ FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk));
return 0;
}
static int ksz9031_config_init(struct phy_device *phydev)
{
- const struct device_d *dev = &phydev->dev;
- const struct device_node *of_node = dev->device_node;
+ const struct device *dev = &phydev->dev;
+ const struct device_node *of_node = dev->of_node;
static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
static const char *rx_data_skews[4] = {
"rxd0-skew-ps", "rxd1-skew-ps",
@@ -485,8 +485,8 @@ static int ksz9031_config_init(struct phy_device *phydev)
static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
int ret;
- if (!of_node && dev->parent->device_node)
- of_node = dev->parent->device_node;
+ if (!of_node && dev->parent->of_node)
+ of_node = dev->parent->of_node;
if (of_node) {
if (phy_interface_is_rgmii(phydev)) {
@@ -546,6 +546,186 @@ err_force_master:
return ret;
}
+#define KSZ9131_SKEW_5BIT_MAX 2400
+#define KSZ9131_SKEW_4BIT_MAX 800
+#define KSZ9131_OFFSET 700
+#define KSZ9131_STEP 100
+
+static int ksz9131_of_load_skew_values(struct phy_device *phydev,
+ const struct device_node *of_node,
+ u16 reg, size_t field_sz,
+ const char *field[], u8 numfields)
+{
+ int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET),
+ -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)};
+ int skewval, skewmax = 0;
+ int matches = 0;
+ u16 maxval;
+ u16 newval;
+ u16 mask;
+ int i;
+
+ /* psec properties in dts should mean x pico seconds */
+ if (field_sz == 5)
+ skewmax = KSZ9131_SKEW_5BIT_MAX;
+ else
+ skewmax = KSZ9131_SKEW_4BIT_MAX;
+
+ for (i = 0; i < numfields; i++)
+ if (!of_property_read_s32(of_node, field[i], &skewval)) {
+ if (skewval < -KSZ9131_OFFSET)
+ skewval = -KSZ9131_OFFSET;
+ else if (skewval > skewmax)
+ skewval = skewmax;
+
+ val[i] = skewval + KSZ9131_OFFSET;
+ matches++;
+ }
+
+ if (!matches)
+ return 0;
+
+ if (matches < numfields)
+ newval = phy_read_mmd(phydev, 2, reg);
+ else
+ newval = 0;
+
+ maxval = (field_sz == 4) ? 0xf : 0x1f;
+ for (i = 0; i < numfields; i++)
+ if (val[i] != -(i + 1 + KSZ9131_OFFSET)) {
+ mask = 0xffff;
+ mask ^= maxval << (field_sz * i);
+ newval = (newval & mask) |
+ (((val[i] / KSZ9131_STEP) & maxval)
+ << (field_sz * i));
+ }
+
+ return phy_write_mmd(phydev, 2, reg, newval);
+}
+
+#define KSZ9131RN_MMD_COMMON_CTRL_REG 2
+#define KSZ9131RN_RXC_DLL_CTRL 76
+#define KSZ9131RN_TXC_DLL_CTRL 77
+#define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12)
+#define KSZ9131RN_DLL_ENABLE_DELAY 0
+#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12)
+
+static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
+{
+ u16 rxcdll_val, txcdll_val;
+ int ret;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
+ txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
+ break;
+ default:
+ return 0;
+ }
+
+ ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
+ rxcdll_val);
+ if (ret < 0)
+ return ret;
+
+ return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
+ txcdll_val);
+}
+
+/* Silicon Errata DS80000693B
+ *
+ * When LEDs are configured in Individual Mode, LED1 is ON in a no-link
+ * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves
+ * according to the datasheet (off if there is no link).
+ */
+static int ksz9131_led_errata(struct phy_device *phydev)
+{
+ int reg;
+
+ reg = phy_read_mmd(phydev, 2, 0);
+ if (reg < 0)
+ return reg;
+
+ if (!(reg & BIT(4)))
+ return 0;
+
+ return phy_set_bits(phydev, 0x1e, BIT(9));
+}
+
+static int ksz9131_config_init(struct phy_device *phydev)
+{
+ const struct device *dev = &phydev->dev;
+ const struct device_node *of_node = dev->of_node;
+ static const char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"};
+ static const char *rx_data_skews[4] = {
+ "rxd0-skew-psec", "rxd1-skew-psec",
+ "rxd2-skew-psec", "rxd3-skew-psec"
+ };
+ static const char *tx_data_skews[4] = {
+ "txd0-skew-psec", "txd1-skew-psec",
+ "txd2-skew-psec", "txd3-skew-psec"
+ };
+ static const char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"};
+ int ret;
+
+ if (!of_node && dev->parent->of_node)
+ of_node = dev->parent->of_node;
+
+ if (!of_node)
+ return 0;
+
+ if (phy_interface_is_rgmii(phydev)) {
+ ret = ksz9131_config_rgmii_delay(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ksz9131_of_load_skew_values(phydev, of_node,
+ MII_KSZ9031RN_CLK_PAD_SKEW, 5,
+ clk_skews, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_of_load_skew_values(phydev, of_node,
+ MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
+ control_skews, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_of_load_skew_values(phydev, of_node,
+ MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
+ rx_data_skews, 4);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_of_load_skew_values(phydev, of_node,
+ MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
+ tx_data_skews, 4);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_led_errata(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06
#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6)
#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4)
@@ -611,8 +791,8 @@ static int ksz8873mll_config_init(struct phy_device *phydev)
static int kszphy_probe(struct phy_device *phydev)
{
- struct device_d *dev = &phydev->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = &phydev->dev;
+ struct device_node *np = dev->of_node;
struct phy_driver *drv = to_phy_driver(dev->driver);
const struct kszphy_type *type = drv->driver_data;
struct kszphy_priv *priv;
@@ -758,6 +938,14 @@ static struct phy_driver ksphy_driver[] = {
.config_aneg = genphy_config_aneg,
.read_status = ksz9031_read_status,
}, {
+ .phy_id = PHY_ID_KSZ9131,
+ .phy_id_mask = 0x00fffff0,
+ .drv.name = "Microchip KSZ9131 Gigabit PHY",
+ .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
+ .config_init = ksz9131_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+}, {
.phy_id = PHY_ID_KSZ8873MLL,
.phy_id_mask = 0x00fffff0,
.drv.name = "Micrel KSZ8873MLL Switch",
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
new file mode 100644
index 0000000000..d4cd05a1f6
--- /dev/null
+++ b/drivers/net/phy/motorcomm.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * drivers/net/phy/motorcomm.c
+ *
+ * Driver for Motorcomm PHYs
+ *
+ * Author: Peter Geis <pgwipeout@gmail.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <linux/phy.h>
+#include <linux/mdio.h>
+
+#define PHY_ID_YT8511 0x0000010a
+
+#define YT8511_PAGE_SELECT 0x1e
+#define YT8511_PAGE 0x1f
+#define YT8511_EXT_CLK_GATE 0x0c
+#define YT8511_EXT_DELAY_DRIVE 0x0d
+#define YT8511_EXT_SLEEP_CTRL 0x27
+
+/* 2b00 25m from pll
+ * 2b01 25m from xtl *default*
+ * 2b10 62.m from pll
+ * 2b11 125m from pll
+ */
+#define YT8511_CLK_125M (BIT(2) | BIT(1))
+#define YT8511_PLLON_SLP BIT(14)
+
+/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */
+#define YT8511_DELAY_RX BIT(0)
+
+/* TX Gig-E Delay is bits 7:4, default 0x5
+ * TX Fast-E Delay is bits 15:12, default 0xf
+ * Delay = 150ps * N - 250ps
+ * On = 2000ps, off = 50ps
+ */
+#define YT8511_DELAY_GE_TX_EN (0xf << 4)
+#define YT8511_DELAY_GE_TX_DIS (0x2 << 4)
+#define YT8511_DELAY_FE_TX_EN (0xf << 12)
+#define YT8511_DELAY_FE_TX_DIS (0x2 << 12)
+
+static int yt8511_read_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, YT8511_PAGE_SELECT);
+};
+
+static int yt8511_write_page(struct phy_device *phydev, int page)
+{
+ return phy_write(phydev, YT8511_PAGE_SELECT, page);
+};
+
+static int yt8511_config_init(struct phy_device *phydev)
+{
+ int oldpage, ret = 0;
+ unsigned int ge, fe;
+
+ oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE);
+ if (oldpage < 0)
+ goto err_restore_page;
+
+ /* set rgmii delay mode */
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ ge = YT8511_DELAY_GE_TX_DIS;
+ fe = YT8511_DELAY_FE_TX_DIS;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS;
+ fe = YT8511_DELAY_FE_TX_DIS;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ ge = YT8511_DELAY_GE_TX_EN;
+ fe = YT8511_DELAY_FE_TX_EN;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN;
+ fe = YT8511_DELAY_FE_TX_EN;
+ break;
+ default: /* do not support other modes */
+ ret = -EOPNOTSUPP;
+ goto err_restore_page;
+ }
+
+ ret = phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* set clock mode to 125mhz */
+ ret = phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* fast ethernet delay is in a separate page */
+ ret = phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* leave pll enabled in sleep */
+ ret = phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP);
+ if (ret < 0)
+ goto err_restore_page;
+
+err_restore_page:
+ return phy_restore_page(phydev, oldpage, ret);
+}
+
+static struct phy_driver motorcomm_phy_drvs[] = {
+ {
+ .phy_id = PHY_ID_YT8511,
+ .phy_id_mask = 0xffffffff,
+ .drv.name = "YT8511 Gigabit Ethernet",
+ .config_init = yt8511_config_init,
+ .features = PHY_GBIT_FEATURES,
+ .read_page = yt8511_read_page,
+ .write_page = yt8511_write_page,
+ },
+};
+
+device_phy_drivers(motorcomm_phy_drvs);
diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c
index a7d707095b..b9b02c52f2 100644
--- a/drivers/net/phy/mv88e6xxx/chip.c
+++ b/drivers/net/phy/mv88e6xxx/chip.c
@@ -832,14 +832,9 @@ static int mv88e6xxx_eeprom_write(void *ctx, unsigned offset, const void *val, s
return chip->info->ops->set_eeprom(chip, &eeprom, (void *)val);
}
-static const struct nvmem_bus mv88e6xxx_eeprom_nvmem_bus = {
- .write = mv88e6xxx_eeprom_write,
- .read = mv88e6xxx_eeprom_read,
-};
-
-static int mv88e6xxx_probe(struct device_d *dev)
+static int mv88e6xxx_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct device_node *mdio_node;
struct mv88e6xxx_chip *chip;
enum of_gpio_flags of_flags;
@@ -911,7 +906,8 @@ static int mv88e6xxx_probe(struct device_d *dev)
.stride = 1,
.size = eeprom_len,
.read_only = false,
- .bus = &mv88e6xxx_eeprom_nvmem_bus,
+ .reg_write = mv88e6xxx_eeprom_write,
+ .reg_read = mv88e6xxx_eeprom_read,
};
if (IS_ERR(nvmem_register(&config)))
@@ -941,7 +937,7 @@ static int mv88e6xxx_probe(struct device_d *dev)
mdio_node = of_get_child_by_name(np, "mdio");
if (mdio_node)
- chip->miibus.dev.device_node = mdio_node;
+ chip->miibus.dev.of_node = mdio_node;
err = mv88e6xxx_port_probe(chip);
if (err)
@@ -965,8 +961,9 @@ static const struct of_device_id mv88e6xxx_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
-static struct driver_d mv88e6xxx_driver = {
+static struct driver mv88e6xxx_driver = {
.name = "mv88e6085",
.probe = mv88e6xxx_probe,
.of_compatible = mv88e6xxx_of_match,
diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h
index 30fdac9a9f..aec6c2891f 100644
--- a/drivers/net/phy/mv88e6xxx/chip.h
+++ b/drivers/net/phy/mv88e6xxx/chip.h
@@ -50,7 +50,7 @@ struct mv88e6xxx_chip {
const struct mv88e6xxx_info *info;
struct mii_bus *parent_miibus;
struct mii_bus miibus;
- struct device_d *dev;
+ struct device *dev;
int reset;
/* Array of port structures. */
diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c
index 93a789e658..29ea4ec882 100644
--- a/drivers/net/phy/mv88e6xxx/port.c
+++ b/drivers/net/phy/mv88e6xxx/port.c
@@ -564,8 +564,8 @@ device_phy_driver(mv88e6xxx_port_driver);
int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip)
{
- struct device_d *dev = chip->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = chip->dev;
+ struct device_node *np = dev->of_node;
struct device_node *port_node, *switch_node;
struct device_node *port_nodes[DSA_MAX_PORTS] = { NULL };
int err, i;
@@ -580,8 +580,8 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip)
err = of_property_read_u32(port_node, "reg", &nr);
if (err) {
dev_err(dev,
- "Error: Failed to find reg for child %s\n",
- port_node->full_name);
+ "Error: Failed to find reg for child %pOF\n",
+ port_node);
continue;
}
@@ -659,7 +659,7 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip)
phydev = phy_device_create(chip->parent_miibus,
chip->info->port_base_addr + i,
MV88E6XXX_SWITCH_PORT_PHY_ID);
- phydev->dev.device_node = port_nodes[i];
+ phydev->dev.of_node = port_nodes[i];
phydev->dev.priv = chip;
phydev->duplex = DUPLEX_UNFORCED;
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index ab52de35b5..b12f54ed38 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -27,6 +27,11 @@ static int phy_read_page(struct phy_device *phydev)
{
struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+ if (!phydrv->read_page) {
+ dev_warn_once(&phydev->dev, "read_page callback not available, PHY driver not loaded?\n");
+ return -EOPNOTSUPP;
+ }
+
return phydrv->read_page(phydev);
}
@@ -34,6 +39,11 @@ static int phy_write_page(struct phy_device *phydev, int page)
{
struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+ if (!phydrv->write_page) {
+ dev_warn_once(&phydev->dev, "write_page callback not available, PHY driver not loaded?\n");
+ return -EOPNOTSUPP;
+ }
+
return phydrv->write_page(phydev, page);
}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 60b5839b40..abd78b2c80 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -20,8 +20,6 @@
#include <linux/phy.h>
#include <linux/err.h>
-#define PHY_AN_TIMEOUT 10
-
static struct phy_driver genphy_driver;
/**
@@ -330,7 +328,7 @@ struct phy_device *of_phy_register_fixed_link(struct device_node *np,
static struct phy_device *of_mdio_find_phy(struct eth_device *edev)
{
- struct device_d *dev;
+ struct device *dev;
struct device_node *phy_node;
struct mii_bus *bus;
int addr;
@@ -338,16 +336,18 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev)
if (!IS_ENABLED(CONFIG_OFDEVICE))
return NULL;
- if (!edev->parent || !edev->parent->device_node)
+ if (!edev->parent || !edev->parent->of_node)
return NULL;
- phy_node = of_parse_phandle(edev->parent->device_node, "phy-handle", 0);
+ phy_node = of_parse_phandle(edev->parent->of_node, "phy-handle", 0);
if (!phy_node)
- phy_node = of_parse_phandle(edev->parent->device_node, "phy", 0);
+ phy_node = of_parse_phandle(edev->parent->of_node, "phy", 0);
if (!phy_node)
- phy_node = of_parse_phandle(edev->parent->device_node, "phy-device", 0);
+ phy_node = of_parse_phandle(edev->parent->of_node,
+ "phy-device", 0);
if (!phy_node) {
- phy_node = of_get_child_by_name(edev->parent->device_node, "fixed-link");
+ phy_node = of_get_child_by_name(edev->parent->of_node,
+ "fixed-link");
if (phy_node)
return of_phy_register_fixed_link(phy_node, edev);
}
@@ -358,7 +358,7 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev)
if (!of_property_read_u32(phy_node, "reg", &addr)) {
of_device_ensure_probed(phy_node->parent);
for_each_mii_bus(bus) {
- if (bus->parent->device_node == phy_node->parent) {
+ if (bus->dev.of_node == phy_node->parent) {
struct phy_device *phy = mdiobus_scan(bus, addr);
if (!IS_ERR(phy))
return phy;
@@ -367,7 +367,7 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev)
}
bus_for_each_device(&mdio_bus_type, dev) {
- if (dev->device_node == phy_node)
+ if (dev->of_node == phy_node)
return container_of(dev, struct phy_device, dev);
}
@@ -462,7 +462,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr,
ret = -ENODEV;
out:
if (ret)
- puts("Unable to find a PHY (unknown ID?)\n");
+ dev_err(&edev->dev, "Unable to find a PHY (unknown ID?)\n");
return ret;
}
@@ -675,7 +675,7 @@ int genphy_aneg_done(struct phy_device *phydev)
/* Restart auto-negotiation if remote fault */
if (bmsr & BMSR_RFAULT) {
- puts("PHY remote fault detected\n"
+ dev_info(&phydev->dev, "PHY remote fault detected\n"
"PHY restarting auto-negotiation\n");
phy_write(phydev, MII_BMCR,
BMCR_ANENABLE | BMCR_ANRESTART);
@@ -819,14 +819,14 @@ int genphy_read_status(struct phy_device *phydev)
return 0;
}
-static inline void mmd_phy_indirect(struct phy_device *phydev, int prtad,
- int devad)
+static inline void mmd_phy_indirect(struct phy_device *phydev, int devad,
+ u16 regnum)
{
/* Write the desired MMD Devad */
phy_write(phydev, MII_MMD_CTRL, devad);
/* Write the desired MMD register address */
- phy_write(phydev, MII_MMD_DATA, prtad);
+ phy_write(phydev, MII_MMD_DATA, regnum);
/* Select the Function : DATA with no post increment */
phy_write(phydev, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
@@ -850,7 +850,10 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad)
{
u32 ret;
- mmd_phy_indirect(phydev, prtad, devad);
+ phydev_warn(phydev, "%s is deprectated use phy_read_mmd instead\n",
+ __func__);
+
+ mmd_phy_indirect(phydev, devad, prtad);
/* Read the content of the MMD's selected register */
ret = phy_read(phydev, MII_MMD_DATA);
@@ -876,12 +879,150 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad)
void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, int devad,
u16 data)
{
- mmd_phy_indirect(phydev, prtad, devad);
+ phydev_warn(phydev, "%s is deprectated use phy_write_mmd instead\n",
+ __func__);
+
+ mmd_phy_indirect(phydev, devad, prtad);
/* Write the data into MMD's selected register */
phy_write(phydev, MII_MMD_DATA, data);
}
+/**
+ * phy_modify_mmd_indirect - Convenience function for modifying a MMD register
+ * @phydev: phy device
+ * @prtad: MMD Address
+ * @devad: MMD DEVAD
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in @mask
+ *
+ */
+int phy_modify_mmd_indirect(struct phy_device *phydev, int prtad, int devad,
+ u16 mask, u16 set)
+{
+ int ret;
+
+ phydev_warn(phydev, "%s is deprectated use phy_modify_mmd instead\n",
+ __func__);
+
+ ret = phy_read_mmd_indirect(phydev, prtad, devad);
+ if (ret < 0)
+ return ret;
+
+ phy_write_mmd_indirect(phydev, prtad, devad, (ret & ~mask) | set);
+
+ return 0;
+}
+
+/**
+ * phy_read_mmd - Convenience function for reading a register
+ * from an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ *
+ * Same rules as for phy_read();
+ */
+int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
+{
+ struct mii_bus *bus = phydev->bus;
+ int phy_addr = phydev->addr;
+
+ if (regnum > (u16)~0 || devad > 32)
+ return -EINVAL;
+
+ if (phydev->is_c45) {
+ phydev_warn(phydev, "Clause45 is not supported yet\n");
+ return -EOPNOTSUPP;
+ }
+
+ mmd_phy_indirect(phydev, devad, regnum);
+
+ /* Read the content of the MMD's selected register */
+ return mdiobus_read(bus, phy_addr, MII_MMD_DATA);
+}
+EXPORT_SYMBOL(phy_read_mmd);
+
+/**
+ * phy_write_mmd - Convenience function for writing a register
+ * on an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ * @val: value to write to @regnum
+ *
+ * Same rules as for phy_write();
+ */
+int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+{
+ struct mii_bus *bus = phydev->bus;
+ int phy_addr = phydev->addr;
+
+ if (regnum > (u16)~0 || devad > 32)
+ return -EINVAL;
+
+ if (phydev->is_c45) {
+ phydev_warn(phydev, "Clause45 is not supported yet\n");
+ return -EOPNOTSUPP;
+ }
+
+ mmd_phy_indirect(phydev, devad, regnum);
+
+ /* Write the data into MMD's selected register */
+ mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
+
+ return 0;
+}
+EXPORT_SYMBOL(phy_write_mmd);
+
+/**
+ * phy_modify_mmd_changed - Function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
+ */
+int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
+ u16 mask, u16 set)
+{
+ int new, ret;
+
+ ret = phy_read_mmd(phydev, devad, regnum);
+ if (ret < 0)
+ return ret;
+
+ new = (ret & ~mask) | set;
+ if (new == ret)
+ return 0;
+
+ ret = phy_write_mmd(phydev, devad, regnum, new);
+
+ return ret < 0 ? ret : 1;
+}
+EXPORT_SYMBOL_GPL(phy_modify_mmd_changed);
+
+/**
+ * phy_modify_mmd - Convenience function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ */
+int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
+ u16 mask, u16 set)
+{
+ int ret;
+
+ ret = phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
+
+ return ret < 0 ? ret : 0;
+}
+EXPORT_SYMBOL_GPL(phy_modify_mmd);
+
int genphy_config_init(struct phy_device *phydev)
{
int val;
@@ -933,6 +1074,8 @@ int phy_driver_register(struct phy_driver *phydrv)
{
phydrv->drv.bus = &mdio_bus_type;
+ phydrv->is_phy = true;
+
if (!phydrv->config_init)
phydrv->config_init = genphy_config_init;
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index 4df3db1387..8ab3d63091 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -80,7 +80,7 @@ static int rtl8211c_config_init(struct phy_device *phydev)
static int rtl8211f_config_init(struct phy_device *phydev)
{
- struct device_d *dev = &phydev->dev;
+ struct device *dev = &phydev->dev;
u16 val_txdly, val_rxdly;
int ret;
@@ -155,6 +155,12 @@ static struct phy_driver realtek_drvs[] = {
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
+ PHY_ID_MATCH_EXACT(0x001cc840),
+ .drv.name = "RTL8226B_RTL8221B 2.5Gbps PHY",
+ .features = PHY_GBIT_FEATURES,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ }, {
PHY_ID_MATCH_EXACT(0x001cc910),
.drv.name = "RTL8211 Gigabit Ethernet",
.features = PHY_GBIT_FEATURES,
@@ -184,6 +190,13 @@ static struct phy_driver realtek_drvs[] = {
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
+ PHY_ID_MATCH_EXACT(0x001cc800),
+ .drv.name = "Generic FE-GE Realtek PHY",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = &rtl8211f_config_init,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ }, {
PHY_ID_MATCH_EXACT(0x001cc961),
.drv.name = "RTL8366RB Gigabit Ethernet",
.features = PHY_GBIT_FEATURES,
diff --git a/drivers/net/r8169.h b/drivers/net/r8169.h
new file mode 100644
index 0000000000..55ef8251fe
--- /dev/null
+++ b/drivers/net/r8169.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* r8169.h: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include <linux/types.h>
+#include <linux/phy.h>
+
+enum mac_version {
+ /* support for ancient RTL_GIGA_MAC_VER_01 has been removed */
+ RTL_GIGA_MAC_VER_02,
+ RTL_GIGA_MAC_VER_03,
+ RTL_GIGA_MAC_VER_04,
+ RTL_GIGA_MAC_VER_05,
+ RTL_GIGA_MAC_VER_06,
+ RTL_GIGA_MAC_VER_07,
+ RTL_GIGA_MAC_VER_08,
+ RTL_GIGA_MAC_VER_09,
+ RTL_GIGA_MAC_VER_10,
+ RTL_GIGA_MAC_VER_11,
+ /* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */
+ /* RTL_GIGA_MAC_VER_13 was merged with VER_10 */
+ RTL_GIGA_MAC_VER_14,
+ /* RTL_GIGA_MAC_VER_16 was merged with VER_10 */
+ RTL_GIGA_MAC_VER_17,
+ RTL_GIGA_MAC_VER_18,
+ RTL_GIGA_MAC_VER_19,
+ RTL_GIGA_MAC_VER_20,
+ RTL_GIGA_MAC_VER_21,
+ RTL_GIGA_MAC_VER_22,
+ RTL_GIGA_MAC_VER_23,
+ RTL_GIGA_MAC_VER_24,
+ RTL_GIGA_MAC_VER_25,
+ RTL_GIGA_MAC_VER_26,
+ /* support for RTL_GIGA_MAC_VER_27 has been removed */
+ RTL_GIGA_MAC_VER_28,
+ RTL_GIGA_MAC_VER_29,
+ RTL_GIGA_MAC_VER_30,
+ RTL_GIGA_MAC_VER_31,
+ RTL_GIGA_MAC_VER_32,
+ RTL_GIGA_MAC_VER_33,
+ RTL_GIGA_MAC_VER_34,
+ RTL_GIGA_MAC_VER_35,
+ RTL_GIGA_MAC_VER_36,
+ RTL_GIGA_MAC_VER_37,
+ RTL_GIGA_MAC_VER_38,
+ RTL_GIGA_MAC_VER_39,
+ RTL_GIGA_MAC_VER_40,
+ /* support for RTL_GIGA_MAC_VER_41 has been removed */
+ RTL_GIGA_MAC_VER_42,
+ RTL_GIGA_MAC_VER_43,
+ RTL_GIGA_MAC_VER_44,
+ /* support for RTL_GIGA_MAC_VER_45 has been removed */
+ RTL_GIGA_MAC_VER_46,
+ /* support for RTL_GIGA_MAC_VER_47 has been removed */
+ RTL_GIGA_MAC_VER_48,
+ /* support for RTL_GIGA_MAC_VER_49 has been removed */
+ /* support for RTL_GIGA_MAC_VER_50 has been removed */
+ RTL_GIGA_MAC_VER_51,
+ RTL_GIGA_MAC_VER_52,
+ RTL_GIGA_MAC_VER_53,
+ /* support for RTL_GIGA_MAC_VER_60 has been removed */
+ RTL_GIGA_MAC_VER_61,
+ RTL_GIGA_MAC_VER_63,
+ RTL_GIGA_MAC_NONE
+};
+
+struct rtl8169_private;
+
+void r8169_apply_firmware(struct rtl8169_private *tp);
+u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
+u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr);
+void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
+ enum mac_version ver);
diff --git a/drivers/net/r8169_firmware.c b/drivers/net/r8169_firmware.c
new file mode 100644
index 0000000000..29c6be50a7
--- /dev/null
+++ b/drivers/net/r8169_firmware.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include <common.h>
+#include <firmware.h>
+
+#include "r8169_firmware.h"
+
+enum rtl_fw_opcode {
+ PHY_READ = 0x0,
+ PHY_DATA_OR = 0x1,
+ PHY_DATA_AND = 0x2,
+ PHY_BJMPN = 0x3,
+ PHY_MDIO_CHG = 0x4,
+ PHY_CLEAR_READCOUNT = 0x7,
+ PHY_WRITE = 0x8,
+ PHY_READCOUNT_EQ_SKIP = 0x9,
+ PHY_COMP_EQ_SKIPN = 0xa,
+ PHY_COMP_NEQ_SKIPN = 0xb,
+ PHY_WRITE_PREVIOUS = 0xc,
+ PHY_SKIPN = 0xd,
+ PHY_DELAY_MS = 0xe,
+};
+
+struct fw_info {
+ u32 magic;
+ char version[RTL_VER_SIZE];
+ __le32 fw_start;
+ __le32 fw_len;
+ u8 chksum;
+} __packed;
+
+#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0])
+
+static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw)
+{
+ const struct firmware *fw = rtl_fw->fw;
+ struct fw_info *fw_info = (struct fw_info *)fw->data;
+ struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
+
+ if (fw->size < FW_OPCODE_SIZE)
+ return false;
+
+ if (!fw_info->magic) {
+ size_t i, size, start;
+ u8 checksum = 0;
+
+ if (fw->size < sizeof(*fw_info))
+ return false;
+
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+ if (checksum != 0)
+ return false;
+
+ start = le32_to_cpu(fw_info->fw_start);
+ if (start > fw->size)
+ return false;
+
+ size = le32_to_cpu(fw_info->fw_len);
+ if (size > (fw->size - start) / FW_OPCODE_SIZE)
+ return false;
+
+ strlcpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE);
+
+ pa->code = (__le32 *)(fw->data + start);
+ pa->size = size;
+ } else {
+ if (fw->size % FW_OPCODE_SIZE)
+ return false;
+
+ strlcpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE);
+
+ pa->code = (__le32 *)fw->data;
+ pa->size = fw->size / FW_OPCODE_SIZE;
+ }
+
+ return true;
+}
+
+static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw)
+{
+ struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
+ size_t index;
+
+ for (index = 0; index < pa->size; index++) {
+ u32 action = le32_to_cpu(pa->code[index]);
+ u32 val = action & 0x0000ffff;
+ u32 regno = (action & 0x0fff0000) >> 16;
+
+ switch (action >> 28) {
+ case PHY_READ:
+ case PHY_DATA_OR:
+ case PHY_DATA_AND:
+ case PHY_CLEAR_READCOUNT:
+ case PHY_WRITE:
+ case PHY_WRITE_PREVIOUS:
+ case PHY_DELAY_MS:
+ break;
+
+ case PHY_MDIO_CHG:
+ if (val > 1)
+ goto out;
+ break;
+
+ case PHY_BJMPN:
+ if (regno > index)
+ goto out;
+ break;
+ case PHY_READCOUNT_EQ_SKIP:
+ if (index + 2 >= pa->size)
+ goto out;
+ break;
+ case PHY_COMP_EQ_SKIPN:
+ case PHY_COMP_NEQ_SKIPN:
+ case PHY_SKIPN:
+ if (index + 1 + regno >= pa->size)
+ goto out;
+ break;
+
+ default:
+ dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action);
+ return false;
+ }
+ }
+
+ return true;
+out:
+ dev_err(rtl_fw->dev, "Out of range of firmware\n");
+ return false;
+}
+
+void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
+{
+ struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
+ rtl_fw_write_t fw_write = rtl_fw->phy_write;
+ rtl_fw_read_t fw_read = rtl_fw->phy_read;
+ int predata = 0, count = 0;
+ size_t index;
+
+ for (index = 0; index < pa->size; index++) {
+ u32 action = le32_to_cpu(pa->code[index]);
+ u32 data = action & 0x0000ffff;
+ u32 regno = (action & 0x0fff0000) >> 16;
+ enum rtl_fw_opcode opcode = action >> 28;
+
+ if (!action)
+ break;
+
+ switch (opcode) {
+ case PHY_READ:
+ predata = fw_read(tp, regno);
+ count++;
+ break;
+ case PHY_DATA_OR:
+ predata |= data;
+ break;
+ case PHY_DATA_AND:
+ predata &= data;
+ break;
+ case PHY_BJMPN:
+ index -= (regno + 1);
+ break;
+ case PHY_MDIO_CHG:
+ if (data) {
+ fw_write = rtl_fw->mac_mcu_write;
+ fw_read = rtl_fw->mac_mcu_read;
+ } else {
+ fw_write = rtl_fw->phy_write;
+ fw_read = rtl_fw->phy_read;
+ }
+
+ break;
+ case PHY_CLEAR_READCOUNT:
+ count = 0;
+ break;
+ case PHY_WRITE:
+ fw_write(tp, regno, data);
+ break;
+ case PHY_READCOUNT_EQ_SKIP:
+ if (count == data)
+ index++;
+ break;
+ case PHY_COMP_EQ_SKIPN:
+ if (predata == data)
+ index += regno;
+ break;
+ case PHY_COMP_NEQ_SKIPN:
+ if (predata != data)
+ index += regno;
+ break;
+ case PHY_WRITE_PREVIOUS:
+ fw_write(tp, regno, predata);
+ break;
+ case PHY_SKIPN:
+ index += regno;
+ break;
+ case PHY_DELAY_MS:
+ mdelay(data);
+ break;
+ }
+ }
+}
+
+void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
+{
+ release_firmware(rtl_fw->fw);
+}
+
+int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
+{
+ int rc;
+
+ rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev);
+ if (rc < 0)
+ goto out;
+
+ if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) {
+ release_firmware(rtl_fw->fw);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+out:
+ /* At least some NiCs work without firmware, even if there is one */
+ dev_dbg(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
+ rtl_fw->fw_name, rc);
+ return rc;
+}
diff --git a/drivers/net/r8169_firmware.h b/drivers/net/r8169_firmware.h
new file mode 100644
index 0000000000..8d3b037225
--- /dev/null
+++ b/drivers/net/r8169_firmware.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* r8169_firmware.h: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+struct rtl8169_private;
+typedef void (*rtl_fw_write_t)(struct rtl8169_private *tp, int reg, int val);
+typedef int (*rtl_fw_read_t)(struct rtl8169_private *tp, int reg);
+
+#define RTL_VER_SIZE 32
+
+struct rtl_fw {
+ rtl_fw_write_t phy_write;
+ rtl_fw_read_t phy_read;
+ rtl_fw_write_t mac_mcu_write;
+ rtl_fw_read_t mac_mcu_read;
+ const struct firmware *fw;
+ const char *fw_name;
+ struct device *dev;
+
+ char version[RTL_VER_SIZE];
+
+ struct rtl_fw_phy_action {
+ __le32 *code;
+ size_t size;
+ } phy_action;
+};
+
+int rtl_fw_request_firmware(struct rtl_fw *rtl_fw);
+void rtl_fw_release_firmware(struct rtl_fw *rtl_fw);
+void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw);
diff --git a/drivers/net/r8169_main.c b/drivers/net/r8169_main.c
new file mode 100644
index 0000000000..fd53ec1bc3
--- /dev/null
+++ b/drivers/net/r8169_main.c
@@ -0,0 +1,3215 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * r8169.c: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include <common.h>
+#include <dma.h>
+#include <init.h>
+#include <net.h>
+#include <malloc.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <asm/unaligned.h>
+
+#include "r8169.h"
+#include "r8169_firmware.h"
+
+#define FIRMWARE_8168D_1 "rtl_nic/rtl8168d-1.fw"
+#define FIRMWARE_8168D_2 "rtl_nic/rtl8168d-2.fw"
+#define FIRMWARE_8168E_1 "rtl_nic/rtl8168e-1.fw"
+#define FIRMWARE_8168E_2 "rtl_nic/rtl8168e-2.fw"
+#define FIRMWARE_8168E_3 "rtl_nic/rtl8168e-3.fw"
+#define FIRMWARE_8168F_1 "rtl_nic/rtl8168f-1.fw"
+#define FIRMWARE_8168F_2 "rtl_nic/rtl8168f-2.fw"
+#define FIRMWARE_8105E_1 "rtl_nic/rtl8105e-1.fw"
+#define FIRMWARE_8402_1 "rtl_nic/rtl8402-1.fw"
+#define FIRMWARE_8411_1 "rtl_nic/rtl8411-1.fw"
+#define FIRMWARE_8411_2 "rtl_nic/rtl8411-2.fw"
+#define FIRMWARE_8106E_1 "rtl_nic/rtl8106e-1.fw"
+#define FIRMWARE_8106E_2 "rtl_nic/rtl8106e-2.fw"
+#define FIRMWARE_8168G_2 "rtl_nic/rtl8168g-2.fw"
+#define FIRMWARE_8168G_3 "rtl_nic/rtl8168g-3.fw"
+#define FIRMWARE_8168H_2 "rtl_nic/rtl8168h-2.fw"
+#define FIRMWARE_8168FP_3 "rtl_nic/rtl8168fp-3.fw"
+#define FIRMWARE_8107E_2 "rtl_nic/rtl8107e-2.fw"
+#define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw"
+#define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw"
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+#define MC_FILTER_LIMIT 32
+
+#define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */
+#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
+
+#define R8169_REGS_SIZE 256
+#define R8169_RX_BUF_SIZE (SZ_16K - 1)
+#define NUM_TX_DESC 256 /* Number of Tx descriptor registers */
+#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */
+#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))
+#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
+
+#define OCP_STD_PHY_BASE 0xa400
+
+#define RTL_CFG_NO_GBIT 1
+
+/* write/read MMIO register */
+#define RTL_W8(tp, reg, val8) writeb((val8), tp->mmio_addr + (reg))
+#define RTL_W16(tp, reg, val16) writew((val16), tp->mmio_addr + (reg))
+#define RTL_W32(tp, reg, val32) writel((val32), tp->mmio_addr + (reg))
+#define RTL_R8(tp, reg) readb(tp->mmio_addr + (reg))
+#define RTL_R16(tp, reg) readw(tp->mmio_addr + (reg))
+#define RTL_R32(tp, reg) readl(tp->mmio_addr + (reg))
+
+#define JUMBO_4K (4 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define JUMBO_6K (6 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define JUMBO_7K (7 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define JUMBO_9K (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+
+static const struct {
+ const char *name;
+ const char *fw_name;
+} rtl_chip_infos[] = {
+ /* PCI devices. */
+ [RTL_GIGA_MAC_VER_02] = {"RTL8169s" },
+ [RTL_GIGA_MAC_VER_03] = {"RTL8110s" },
+ [RTL_GIGA_MAC_VER_04] = {"RTL8169sb/8110sb" },
+ [RTL_GIGA_MAC_VER_05] = {"RTL8169sc/8110sc" },
+ [RTL_GIGA_MAC_VER_06] = {"RTL8169sc/8110sc" },
+ /* PCI-E devices. */
+ [RTL_GIGA_MAC_VER_07] = {"RTL8102e" },
+ [RTL_GIGA_MAC_VER_08] = {"RTL8102e" },
+ [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" },
+ [RTL_GIGA_MAC_VER_10] = {"RTL8101e/RTL8100e" },
+ [RTL_GIGA_MAC_VER_11] = {"RTL8168b/8111b" },
+ [RTL_GIGA_MAC_VER_14] = {"RTL8401" },
+ [RTL_GIGA_MAC_VER_17] = {"RTL8168b/8111b" },
+ [RTL_GIGA_MAC_VER_18] = {"RTL8168cp/8111cp" },
+ [RTL_GIGA_MAC_VER_19] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_20] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_21] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_22] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_23] = {"RTL8168cp/8111cp" },
+ [RTL_GIGA_MAC_VER_24] = {"RTL8168cp/8111cp" },
+ [RTL_GIGA_MAC_VER_25] = {"RTL8168d/8111d", FIRMWARE_8168D_1},
+ [RTL_GIGA_MAC_VER_26] = {"RTL8168d/8111d", FIRMWARE_8168D_2},
+ [RTL_GIGA_MAC_VER_28] = {"RTL8168dp/8111dp" },
+ [RTL_GIGA_MAC_VER_29] = {"RTL8105e", FIRMWARE_8105E_1},
+ [RTL_GIGA_MAC_VER_30] = {"RTL8105e", FIRMWARE_8105E_1},
+ [RTL_GIGA_MAC_VER_31] = {"RTL8168dp/8111dp" },
+ [RTL_GIGA_MAC_VER_32] = {"RTL8168e/8111e", FIRMWARE_8168E_1},
+ [RTL_GIGA_MAC_VER_33] = {"RTL8168e/8111e", FIRMWARE_8168E_2},
+ [RTL_GIGA_MAC_VER_34] = {"RTL8168evl/8111evl", FIRMWARE_8168E_3},
+ [RTL_GIGA_MAC_VER_35] = {"RTL8168f/8111f", FIRMWARE_8168F_1},
+ [RTL_GIGA_MAC_VER_36] = {"RTL8168f/8111f", FIRMWARE_8168F_2},
+ [RTL_GIGA_MAC_VER_37] = {"RTL8402", FIRMWARE_8402_1 },
+ [RTL_GIGA_MAC_VER_38] = {"RTL8411", FIRMWARE_8411_1 },
+ [RTL_GIGA_MAC_VER_39] = {"RTL8106e", FIRMWARE_8106E_1},
+ [RTL_GIGA_MAC_VER_40] = {"RTL8168g/8111g", FIRMWARE_8168G_2},
+ [RTL_GIGA_MAC_VER_42] = {"RTL8168gu/8111gu", FIRMWARE_8168G_3},
+ [RTL_GIGA_MAC_VER_43] = {"RTL8106eus", FIRMWARE_8106E_2},
+ [RTL_GIGA_MAC_VER_44] = {"RTL8411b", FIRMWARE_8411_2 },
+ [RTL_GIGA_MAC_VER_46] = {"RTL8168h/8111h", FIRMWARE_8168H_2},
+ [RTL_GIGA_MAC_VER_48] = {"RTL8107e", FIRMWARE_8107E_2},
+ [RTL_GIGA_MAC_VER_51] = {"RTL8168ep/8111ep" },
+ [RTL_GIGA_MAC_VER_52] = {"RTL8168fp/RTL8117", FIRMWARE_8168FP_3},
+ [RTL_GIGA_MAC_VER_53] = {"RTL8168fp/RTL8117", },
+ [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3},
+ /* reserve 62 for CFG_METHOD_4 in the vendor driver */
+ [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2},
+};
+
+static const struct pci_device_id rtl8169_pci_tbl[] = {
+ { PCI_VDEVICE(REALTEK, 0x2502) },
+ { PCI_VDEVICE(REALTEK, 0x2600) },
+ { PCI_VDEVICE(REALTEK, 0x8129) },
+ { PCI_VDEVICE(REALTEK, 0x8136), RTL_CFG_NO_GBIT },
+ { PCI_VDEVICE(REALTEK, 0x8161) },
+ { PCI_VDEVICE(REALTEK, 0x8162) },
+ { PCI_VDEVICE(REALTEK, 0x8167) },
+ { PCI_VDEVICE(REALTEK, 0x8168) },
+ { PCI_VDEVICE(NCUBE, 0x8168) },
+ { PCI_VDEVICE(REALTEK, 0x8169) },
+ { PCI_VENDOR_ID_DLINK, 0x4300,
+ PCI_VENDOR_ID_DLINK, 0x4b10, 0, 0 },
+ { PCI_VDEVICE(DLINK, 0x4300) },
+ { PCI_VDEVICE(DLINK, 0x4302) },
+ { PCI_VDEVICE(AT, 0xc107) },
+ { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 },
+ { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 },
+ { PCI_VDEVICE(REALTEK, 0x8125) },
+ { PCI_VDEVICE(REALTEK, 0x3000) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
+
+enum rtl_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAC4 = 4,
+ MAR0 = 8, /* Multicast filter. */
+ CounterAddrLow = 0x10,
+ CounterAddrHigh = 0x14,
+ TxDescStartAddrLow = 0x20,
+ TxDescStartAddrHigh = 0x24,
+ TxHDescStartAddrLow = 0x28,
+ TxHDescStartAddrHigh = 0x2c,
+ FLASH = 0x30,
+ ERSR = 0x36,
+ ChipCmd = 0x37,
+ TxPoll = 0x38,
+ IntrMask = 0x3c,
+ IntrStatus = 0x3e,
+
+ TxConfig = 0x40,
+#define TXCFG_AUTO_FIFO (1 << 7) /* 8111e-vl */
+#define TXCFG_EMPTY (1 << 11) /* 8111e-vl */
+
+ RxConfig = 0x44,
+#define RX128_INT_EN (1 << 15) /* 8111c and later */
+#define RX_MULTI_EN (1 << 14) /* 8111c only */
+#define RXCFG_FIFO_SHIFT 13
+ /* No threshold before first PCI xfer */
+#define RX_FIFO_THRESH (7 << RXCFG_FIFO_SHIFT)
+#define RX_EARLY_OFF (1 << 11)
+#define RXCFG_DMA_SHIFT 8
+ /* Unlimited maximum PCI burst. */
+#define RX_DMA_BURST (7 << RXCFG_DMA_SHIFT)
+ RxMissed = 0x4c,
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ Config2 = 0x53,
+#define PME_SIGNAL (1 << 5) /* 8168c and later */
+
+ Config3 = 0x54,
+ Config4 = 0x55,
+ Config5 = 0x56,
+ PHYAR = 0x60,
+ PHYstatus = 0x6c,
+ RxMaxSize = 0xda,
+ CPlusCmd = 0xe0,
+ IntrMitigate = 0xe2,
+
+#define RTL_COALESCE_TX_USECS GENMASK(15, 12)
+#define RTL_COALESCE_TX_FRAMES GENMASK(11, 8)
+#define RTL_COALESCE_RX_USECS GENMASK(7, 4)
+#define RTL_COALESCE_RX_FRAMES GENMASK(3, 0)
+
+#define RTL_COALESCE_T_MAX 0x0fU
+#define RTL_COALESCE_FRAME_MAX (RTL_COALESCE_T_MAX * 4)
+
+ RxDescAddrLow = 0xe4,
+ RxDescAddrHigh = 0xe8,
+ EarlyTxThres = 0xec, /* 8169. Unit of 32 bytes. */
+
+#define NoEarlyTx 0x3f /* Max value : no early transmit. */
+
+ MaxTxPacketSize = 0xec, /* 8101/8168. Unit of 128 bytes. */
+
+#define TxPacketMax (8064 >> 7)
+#define EarlySize 0x27
+
+ FuncEvent = 0xf0,
+ FuncEventMask = 0xf4,
+ FuncPresetState = 0xf8,
+ IBCR0 = 0xf8,
+ IBCR2 = 0xf9,
+ IBIMR0 = 0xfa,
+ IBISR0 = 0xfb,
+ FuncForceEvent = 0xfc,
+};
+
+enum rtl8168_8101_registers {
+ CSIDR = 0x64,
+ CSIAR = 0x68,
+#define CSIAR_FLAG 0x80000000
+#define CSIAR_WRITE_CMD 0x80000000
+#define CSIAR_BYTE_ENABLE 0x0000f000
+#define CSIAR_ADDR_MASK 0x00000fff
+ PMCH = 0x6f,
+#define D3COLD_NO_PLL_DOWN BIT(7)
+#define D3HOT_NO_PLL_DOWN BIT(6)
+#define D3_NO_PLL_DOWN (BIT(7) | BIT(6))
+ EPHYAR = 0x80,
+#define EPHYAR_FLAG 0x80000000
+#define EPHYAR_WRITE_CMD 0x80000000
+#define EPHYAR_REG_MASK 0x1f
+#define EPHYAR_REG_SHIFT 16
+#define EPHYAR_DATA_MASK 0xffff
+ DLLPR = 0xd0,
+#define PFM_EN (1 << 6)
+#define TX_10M_PS_EN (1 << 7)
+ DBG_REG = 0xd1,
+#define FIX_NAK_1 (1 << 4)
+#define FIX_NAK_2 (1 << 3)
+ TWSI = 0xd2,
+ MCU = 0xd3,
+#define NOW_IS_OOB (1 << 7)
+#define TX_EMPTY (1 << 5)
+#define RX_EMPTY (1 << 4)
+#define RXTX_EMPTY (TX_EMPTY | RX_EMPTY)
+#define EN_NDP (1 << 3)
+#define EN_OOB_RESET (1 << 2)
+#define LINK_LIST_RDY (1 << 1)
+ EFUSEAR = 0xdc,
+#define EFUSEAR_FLAG 0x80000000
+#define EFUSEAR_WRITE_CMD 0x80000000
+#define EFUSEAR_READ_CMD 0x00000000
+#define EFUSEAR_REG_MASK 0x03ff
+#define EFUSEAR_REG_SHIFT 8
+#define EFUSEAR_DATA_MASK 0xff
+ MISC_1 = 0xf2,
+#define PFM_D3COLD_EN (1 << 6)
+};
+
+enum rtl8168_registers {
+ LED_FREQ = 0x1a,
+ EEE_LED = 0x1b,
+ ERIDR = 0x70,
+ ERIAR = 0x74,
+#define ERIAR_FLAG 0x80000000
+#define ERIAR_WRITE_CMD 0x80000000
+#define ERIAR_READ_CMD 0x00000000
+#define ERIAR_ADDR_BYTE_ALIGN 4
+#define ERIAR_TYPE_SHIFT 16
+#define ERIAR_EXGMAC (0x00 << ERIAR_TYPE_SHIFT)
+#define ERIAR_MSIX (0x01 << ERIAR_TYPE_SHIFT)
+#define ERIAR_ASF (0x02 << ERIAR_TYPE_SHIFT)
+#define ERIAR_OOB (0x02 << ERIAR_TYPE_SHIFT)
+#define ERIAR_MASK_SHIFT 12
+#define ERIAR_MASK_0001 (0x1 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0011 (0x3 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0100 (0x4 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0101 (0x5 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_1111 (0xf << ERIAR_MASK_SHIFT)
+ EPHY_RXER_NUM = 0x7c,
+ OCPDR = 0xb0, /* OCP GPHY access */
+#define OCPDR_WRITE_CMD 0x80000000
+#define OCPDR_READ_CMD 0x00000000
+#define OCPDR_REG_MASK 0x7f
+#define OCPDR_GPHY_REG_SHIFT 16
+#define OCPDR_DATA_MASK 0xffff
+ OCPAR = 0xb4,
+#define OCPAR_FLAG 0x80000000
+#define OCPAR_GPHY_WRITE_CMD 0x8000f060
+#define OCPAR_GPHY_READ_CMD 0x0000f060
+ GPHY_OCP = 0xb8,
+ RDSAR1 = 0xd0, /* 8168c only. Undocumented on 8168dp */
+ MISC = 0xf0, /* 8168e only. */
+#define TXPLA_RST (1 << 29)
+#define DISABLE_LAN_EN (1 << 23) /* Enable GPIO pin */
+#define PWM_EN (1 << 22)
+#define RXDV_GATED_EN (1 << 19)
+#define EARLY_TALLY_EN (1 << 16)
+};
+
+enum rtl8125_registers {
+ IntrMask_8125 = 0x38,
+ IntrStatus_8125 = 0x3c,
+ TxPoll_8125 = 0x90,
+ MAC0_BKP = 0x19e0,
+ EEE_TXIDLE_TIMER_8125 = 0x6048,
+};
+
+#define RX_VLAN_INNER_8125 BIT(22)
+#define RX_VLAN_OUTER_8125 BIT(23)
+#define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125)
+
+#define RX_FETCH_DFLT_8125 (8 << 27)
+
+enum rtl_register_content {
+ /* InterruptStatusBits */
+ SYSErr = 0x8000,
+ PCSTimeout = 0x4000,
+ SWInt = 0x0100,
+ TxDescUnavail = 0x0080,
+ RxFIFOOver = 0x0040,
+ LinkChg = 0x0020,
+ RxOverflow = 0x0010,
+ TxErr = 0x0008,
+ TxOK = 0x0004,
+ RxErr = 0x0002,
+ RxOK = 0x0001,
+
+ /* RxStatusDesc */
+ RxRWT = (1 << 22),
+ RxRES = (1 << 21),
+ RxRUNT = (1 << 20),
+ RxCRC = (1 << 19),
+
+ /* ChipCmdBits */
+ StopReq = 0x80,
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+
+ /* TXPoll register p.5 */
+ HPQ = 0x80, /* Poll cmd on the high prio queue */
+ NPQ = 0x40, /* Poll cmd on the low prio queue */
+ FSWInt = 0x01, /* Forced software interrupt */
+
+ /* Cfg9346Bits */
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xc0,
+
+ /* rx_mode_bits */
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+#define RX_CONFIG_ACCEPT_ERR_MASK 0x30
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+#define RX_CONFIG_ACCEPT_OK_MASK 0x0f
+#define RX_CONFIG_ACCEPT_MASK 0x3f
+
+ /* TxConfigBits */
+ TxInterFrameGapShift = 24,
+ TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
+
+ /* Config1 register p.24 */
+ LEDS1 = (1 << 7),
+ LEDS0 = (1 << 6),
+ Speed_down = (1 << 4),
+ MEMMAP = (1 << 3),
+ IOMAP = (1 << 2),
+ VPD = (1 << 1),
+ PMEnable = (1 << 0), /* Power Management Enable */
+
+ /* Config2 register p. 25 */
+ ClkReqEn = (1 << 7), /* Clock Request Enable */
+ MSIEnable = (1 << 5), /* 8169 only. Reserved in the 8168. */
+ PCI_Clock_66MHz = 0x01,
+ PCI_Clock_33MHz = 0x00,
+
+ /* Config3 register p.25 */
+ MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */
+ LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */
+ Jumbo_En0 = (1 << 2), /* 8168 only. Reserved in the 8168b */
+ Rdy_to_L23 = (1 << 1), /* L23 Enable */
+ Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */
+
+ /* Config4 register */
+ Jumbo_En1 = (1 << 1), /* 8168 only. Reserved in the 8168b */
+
+ /* Config5 register p.27 */
+ BWF = (1 << 6), /* Accept Broadcast wakeup frame */
+ MWF = (1 << 5), /* Accept Multicast wakeup frame */
+ UWF = (1 << 4), /* Accept Unicast wakeup frame */
+ Spi_en = (1 << 3),
+ LanWake = (1 << 1), /* LanWake enable/disable */
+ PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */
+ ASPM_en = (1 << 0), /* ASPM enable */
+
+ /* CPlusCmd p.31 */
+ EnableBist = (1 << 15), // 8168 8101
+ Mac_dbgo_oe = (1 << 14), // 8168 8101
+ EnAnaPLL = (1 << 14), // 8169
+ Normal_mode = (1 << 13), // unused
+ Force_half_dup = (1 << 12), // 8168 8101
+ Force_rxflow_en = (1 << 11), // 8168 8101
+ Force_txflow_en = (1 << 10), // 8168 8101
+ Cxpl_dbg_sel = (1 << 9), // 8168 8101
+ ASF = (1 << 8), // 8168 8101
+ PktCntrDisable = (1 << 7), // 8168 8101
+ Mac_dbgo_sel = 0x001c, // 8168
+ RxVlan = (1 << 6),
+ RxChkSum = (1 << 5),
+ PCIDAC = (1 << 4),
+ PCIMulRW = (1 << 3),
+#define INTT_MASK GENMASK(1, 0)
+#define CPCMD_MASK (Normal_mode | RxVlan | RxChkSum | INTT_MASK)
+
+ /* rtl8169_PHYstatus */
+ TBI_Enable = 0x80,
+ TxFlowCtrl = 0x40,
+ RxFlowCtrl = 0x20,
+ _1000bpsF = 0x10,
+ _100bps = 0x08,
+ _10bps = 0x04,
+ LinkStatus = 0x02,
+ FullDup = 0x01,
+
+ /* ResetCounterCommand */
+ CounterReset = 0x1,
+
+ /* DumpCounterCommand */
+ CounterDump = 0x8,
+
+ /* magic enable v2 */
+ MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
+};
+
+enum rtl_desc_bit {
+ /* First doubleword. */
+ DescOwn = (1 << 31), /* Descriptor is owned by NIC */
+ RingEnd = (1 << 30), /* End of descriptor ring */
+ FirstFrag = (1 << 29), /* First segment of a packet */
+ LastFrag = (1 << 28), /* Final segment of a packet */
+};
+
+/* Generic case. */
+enum rtl_tx_desc_bit {
+ /* First doubleword. */
+ TD_LSO = (1 << 27), /* Large Send Offload */
+#define TD_MSS_MAX 0x07ffu /* MSS value */
+
+ /* Second doubleword. */
+ TxVlanTag = (1 << 17), /* Add VLAN tag */
+};
+
+/* 8169, 8168b and 810x except 8102e. */
+enum rtl_tx_desc_bit_0 {
+ /* First doubleword. */
+#define TD0_MSS_SHIFT 16 /* MSS position (11 bits) */
+ TD0_TCP_CS = (1 << 16), /* Calculate TCP/IP checksum */
+ TD0_UDP_CS = (1 << 17), /* Calculate UDP/IP checksum */
+ TD0_IP_CS = (1 << 18), /* Calculate IP checksum */
+};
+
+/* 8102e, 8168c and beyond. */
+enum rtl_tx_desc_bit_1 {
+ /* First doubleword. */
+ TD1_GTSENV4 = (1 << 26), /* Giant Send for IPv4 */
+ TD1_GTSENV6 = (1 << 25), /* Giant Send for IPv6 */
+#define GTTCPHO_SHIFT 18
+#define GTTCPHO_MAX 0x7f
+
+ /* Second doubleword. */
+#define TCPHO_SHIFT 18
+#define TCPHO_MAX 0x3ff
+#define TD1_MSS_SHIFT 18 /* MSS position (11 bits) */
+ TD1_IPv6_CS = (1 << 28), /* Calculate IPv6 checksum */
+ TD1_IPv4_CS = (1 << 29), /* Calculate IPv4 checksum */
+ TD1_TCP_CS = (1 << 30), /* Calculate TCP/IP checksum */
+ TD1_UDP_CS = (1 << 31), /* Calculate UDP/IP checksum */
+};
+
+enum rtl_rx_desc_bit {
+ /* Rx private */
+ PID1 = (1 << 18), /* Protocol ID bit 1/2 */
+ PID0 = (1 << 17), /* Protocol ID bit 0/2 */
+
+#define RxProtoUDP (PID1)
+#define RxProtoTCP (PID0)
+#define RxProtoIP (PID1 | PID0)
+#define RxProtoMask RxProtoIP
+
+ IPFail = (1 << 16), /* IP checksum failed */
+ UDPFail = (1 << 15), /* UDP/IP checksum failed */
+ TCPFail = (1 << 14), /* TCP/IP checksum failed */
+
+#define RxCSFailMask (IPFail | UDPFail | TCPFail)
+
+ RxVlanTag = (1 << 16), /* VLAN tag available */
+};
+
+#define RTL_GSO_MAX_SIZE_V1 32000
+#define RTL_GSO_MAX_SEGS_V1 24
+#define RTL_GSO_MAX_SIZE_V2 64000
+#define RTL_GSO_MAX_SEGS_V2 64
+
+struct TxDesc {
+ __le32 opts1;
+ __le32 opts2;
+ __le64 addr;
+};
+
+struct RxDesc {
+ __le32 opts1;
+ __le32 opts2;
+ __le64 addr;
+};
+
+struct ring_info {
+ struct sk_buff *skb;
+ u32 len;
+};
+
+struct rtl8169_counters {
+ __le64 tx_packets;
+ __le64 rx_packets;
+ __le64 tx_errors;
+ __le32 rx_errors;
+ __le16 rx_missed;
+ __le16 align_errors;
+ __le32 tx_one_collision;
+ __le32 tx_multi_collision;
+ __le64 rx_unicast;
+ __le64 rx_broadcast;
+ __le32 rx_multicast;
+ __le16 tx_aborted;
+ __le16 tx_underun;
+};
+
+struct rtl8169_tc_offsets {
+ bool inited;
+ __le64 tx_errors;
+ __le32 tx_multi_collision;
+ __le16 tx_aborted;
+ __le16 rx_missed;
+};
+
+enum rtl_flag {
+ RTL_FLAG_TASK_ENABLED = 0,
+ RTL_FLAG_TASK_RESET_PENDING,
+ RTL_FLAG_TASK_TX_TIMEOUT,
+ RTL_FLAG_MAX
+};
+
+enum rtl_dash_type {
+ RTL_DASH_NONE,
+ RTL_DASH_DP,
+ RTL_DASH_EP,
+};
+
+struct rtl8169_private {
+ void __iomem *mmio_addr; /* memory map physical address */
+ struct pci_dev *pci_dev;
+ struct device *dev;
+ struct eth_device edev;
+ struct phy_device *phydev;
+ enum mac_version mac_version;
+ enum rtl_dash_type dash_type;
+ u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+ u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
+ u32 dirty_tx;
+ struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */
+ struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */
+ dma_addr_t TxPhyAddr;
+ dma_addr_t RxPhyAddr;
+ u16 cp_cmd;
+ void *tx_buf;
+ dma_addr_t tx_buf_phys;
+ void *rx_buf;
+ dma_addr_t rx_buf_phys;
+
+ unsigned supports_gmii:1;
+ unsigned aspm_manageable:1;
+
+ const char *fw_name;
+ struct rtl_fw *rtl_fw;
+
+ u32 ocp_base;
+
+ struct mii_bus miibus;
+};
+
+typedef void (*rtl_generic_fct)(struct rtl8169_private *tp);
+
+static inline struct device *tp_to_dev(struct rtl8169_private *tp)
+{
+ return &tp->pci_dev->dev;
+}
+
+static void rtl_lock_config_regs(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
+}
+
+static void rtl_unlock_config_regs(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
+}
+
+static void rtl_pci_commit(struct rtl8169_private *tp)
+{
+ /* Read an arbitrary register to commit a preceding PCI write */
+ RTL_R8(tp, ChipCmd);
+}
+
+static bool rtl_is_8125(struct rtl8169_private *tp)
+{
+ return tp->mac_version >= RTL_GIGA_MAC_VER_61;
+}
+
+static bool rtl_is_8168evl_up(struct rtl8169_private *tp)
+{
+ return tp->mac_version >= RTL_GIGA_MAC_VER_34 &&
+ tp->mac_version != RTL_GIGA_MAC_VER_39 &&
+ tp->mac_version <= RTL_GIGA_MAC_VER_53;
+}
+
+static void rtl_read_mac_from_reg(struct rtl8169_private *tp, u8 *mac, int reg)
+{
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ mac[i] = RTL_R8(tp, reg + i);
+}
+
+struct rtl_cond {
+ bool (*check)(struct rtl8169_private *);
+ const char *msg;
+};
+
+static bool rtl_loop_wait(struct rtl8169_private *tp, const struct rtl_cond *c,
+ unsigned long usecs, int n, bool high)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (c->check(tp) == high)
+ return true;
+ udelay(usecs);
+ }
+
+ dev_err(tp->dev, "%s == %d (loop: %d, delay: %lu).\n",
+ c->msg, !high, n, usecs);
+ return false;
+}
+
+static bool rtl_loop_wait_high(struct rtl8169_private *tp,
+ const struct rtl_cond *c,
+ unsigned long d, int n)
+{
+ return rtl_loop_wait(tp, c, d, n, true);
+}
+
+static bool rtl_loop_wait_low(struct rtl8169_private *tp,
+ const struct rtl_cond *c,
+ unsigned long d, int n)
+{
+ return rtl_loop_wait(tp, c, d, n, false);
+}
+
+#define DECLARE_RTL_COND(name) \
+static bool name ## _check(struct rtl8169_private *); \
+ \
+static const struct rtl_cond name = { \
+ .check = name ## _check, \
+ .msg = #name \
+}; \
+ \
+static bool name ## _check(struct rtl8169_private *tp)
+
+static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type)
+{
+ /* based on RTL8168FP_OOBMAC_BASE in vendor driver */
+ if (type == ERIAR_OOB &&
+ (tp->mac_version == RTL_GIGA_MAC_VER_52 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_53))
+ *cmd |= 0xf70 << 18;
+}
+
+DECLARE_RTL_COND(rtl_eriar_cond)
+{
+ return RTL_R32(tp, ERIAR) & ERIAR_FLAG;
+}
+
+static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
+ u32 val, int type)
+{
+ u32 cmd = ERIAR_WRITE_CMD | type | mask | addr;
+
+ if (WARN(addr & 3 || !mask, "addr: 0x%x, mask: 0x%08x\n", addr, mask))
+ return;
+
+ RTL_W32(tp, ERIDR, val);
+ r8168fp_adjust_ocp_cmd(tp, &cmd, type);
+ RTL_W32(tp, ERIAR, cmd);
+
+ rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
+}
+
+static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
+ u32 val)
+{
+ _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC);
+}
+
+static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type)
+{
+ u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr;
+
+ r8168fp_adjust_ocp_cmd(tp, &cmd, type);
+ RTL_W32(tp, ERIAR, cmd);
+
+ return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
+ RTL_R32(tp, ERIDR) : ~0;
+}
+
+static u32 rtl_eri_read(struct rtl8169_private *tp, int addr)
+{
+ return _rtl_eri_read(tp, addr, ERIAR_EXGMAC);
+}
+
+static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m)
+{
+ u32 val = rtl_eri_read(tp, addr);
+
+ rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p);
+}
+
+static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p)
+{
+ rtl_w0w1_eri(tp, addr, p, 0);
+}
+
+static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m)
+{
+ rtl_w0w1_eri(tp, addr, 0, m);
+}
+
+static bool rtl_ocp_reg_failure(u32 reg)
+{
+ return WARN_ONCE(reg & 0xffff0001, "Invalid ocp reg %x!\n", reg);
+}
+
+DECLARE_RTL_COND(rtl_ocp_gphy_cond)
+{
+ return RTL_R32(tp, GPHY_OCP) & OCPAR_FLAG;
+}
+
+static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
+{
+ if (rtl_ocp_reg_failure(reg))
+ return;
+
+ RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data);
+
+ rtl_loop_wait_low(tp, &rtl_ocp_gphy_cond, 25, 10);
+}
+
+static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
+{
+ if (rtl_ocp_reg_failure(reg))
+ return 0;
+
+ RTL_W32(tp, GPHY_OCP, reg << 15);
+
+ return rtl_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ?
+ (RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT;
+}
+
+static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
+{
+ if (rtl_ocp_reg_failure(reg))
+ return;
+
+ RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data);
+}
+
+static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
+{
+ if (rtl_ocp_reg_failure(reg))
+ return 0;
+
+ RTL_W32(tp, OCPDR, reg << 15);
+
+ return RTL_R32(tp, OCPDR);
+}
+
+static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask,
+ u16 set)
+{
+ u16 data = r8168_mac_ocp_read(tp, reg);
+
+ r8168_mac_ocp_write(tp, reg, (data & ~mask) | set);
+}
+
+/* Work around a hw issue with RTL8168g PHY, the quirk disables
+ * PHY MCU interrupts before PHY power-down.
+ */
+static void rtl8168g_phy_suspend_quirk(struct rtl8169_private *tp, int value)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_40:
+ if (value & BMCR_RESET || !(value & BMCR_PDOWN))
+ rtl_eri_set_bits(tp, 0x1a8, 0xfc000000);
+ else
+ rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000);
+ break;
+ default:
+ break;
+ }
+};
+
+static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value)
+{
+ if (reg == 0x1f) {
+ tp->ocp_base = value ? value << 4 : OCP_STD_PHY_BASE;
+ return;
+ }
+
+ if (tp->ocp_base != OCP_STD_PHY_BASE)
+ reg -= 0x10;
+
+ if (tp->ocp_base == OCP_STD_PHY_BASE && reg == MII_BMCR)
+ rtl8168g_phy_suspend_quirk(tp, value);
+
+ r8168_phy_ocp_write(tp, tp->ocp_base + reg * 2, value);
+}
+
+static int r8168g_mdio_read(struct rtl8169_private *tp, int reg)
+{
+ if (reg == 0x1f)
+ return tp->ocp_base == OCP_STD_PHY_BASE ? 0 : tp->ocp_base >> 4;
+
+ if (tp->ocp_base != OCP_STD_PHY_BASE)
+ reg -= 0x10;
+
+ return r8168_phy_ocp_read(tp, tp->ocp_base + reg * 2);
+}
+
+static void mac_mcu_write(struct rtl8169_private *tp, int reg, int value)
+{
+ if (reg == 0x1f) {
+ tp->ocp_base = value << 4;
+ return;
+ }
+
+ r8168_mac_ocp_write(tp, tp->ocp_base + reg, value);
+}
+
+static int mac_mcu_read(struct rtl8169_private *tp, int reg)
+{
+ return r8168_mac_ocp_read(tp, tp->ocp_base + reg);
+}
+
+DECLARE_RTL_COND(rtl_phyar_cond)
+{
+ return RTL_R32(tp, PHYAR) & 0x80000000;
+}
+
+static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value)
+{
+ RTL_W32(tp, PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff));
+
+ rtl_loop_wait_low(tp, &rtl_phyar_cond, 25, 20);
+ /*
+ * According to hardware specs a 20us delay is required after write
+ * complete indication, but before sending next command.
+ */
+ udelay(20);
+}
+
+static int r8169_mdio_read(struct rtl8169_private *tp, int reg)
+{
+ int value;
+
+ RTL_W32(tp, PHYAR, 0x0 | (reg & 0x1f) << 16);
+
+ value = rtl_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ?
+ RTL_R32(tp, PHYAR) & 0xffff : -ETIMEDOUT;
+
+ /*
+ * According to hardware specs a 20us delay is required after read
+ * complete indication, but before sending next command.
+ */
+ udelay(20);
+
+ return value;
+}
+
+DECLARE_RTL_COND(rtl_ocpar_cond)
+{
+ return RTL_R32(tp, OCPAR) & OCPAR_FLAG;
+}
+
+#define R8168DP_1_MDIO_ACCESS_BIT 0x00020000
+
+static void r8168dp_2_mdio_start(struct rtl8169_private *tp)
+{
+ RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) & ~R8168DP_1_MDIO_ACCESS_BIT);
+}
+
+static void r8168dp_2_mdio_stop(struct rtl8169_private *tp)
+{
+ RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) | R8168DP_1_MDIO_ACCESS_BIT);
+}
+
+static void r8168dp_2_mdio_write(struct rtl8169_private *tp, int reg, int value)
+{
+ r8168dp_2_mdio_start(tp);
+
+ r8169_mdio_write(tp, reg, value);
+
+ r8168dp_2_mdio_stop(tp);
+}
+
+static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg)
+{
+ int value;
+
+ /* Work around issue with chip reporting wrong PHY ID */
+ if (reg == MII_PHYSID2)
+ return 0xc912;
+
+ r8168dp_2_mdio_start(tp);
+
+ value = r8169_mdio_read(tp, reg);
+
+ r8168dp_2_mdio_stop(tp);
+
+ return value;
+}
+
+static void rtl_writephy(struct rtl8169_private *tp, int location, int val)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_28:
+ case RTL_GIGA_MAC_VER_31:
+ r8168dp_2_mdio_write(tp, location, val);
+ break;
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
+ r8168g_mdio_write(tp, location, val);
+ break;
+ default:
+ r8169_mdio_write(tp, location, val);
+ break;
+ }
+}
+
+static int rtl_readphy(struct rtl8169_private *tp, int location)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_28:
+ case RTL_GIGA_MAC_VER_31:
+ return r8168dp_2_mdio_read(tp, location);
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
+ return r8168g_mdio_read(tp, location);
+ default:
+ return r8169_mdio_read(tp, location);
+ }
+}
+
+DECLARE_RTL_COND(rtl_ephyar_cond)
+{
+ return RTL_R32(tp, EPHYAR) & EPHYAR_FLAG;
+}
+
+static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value)
+{
+ RTL_W32(tp, EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) |
+ (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+
+ rtl_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100);
+
+ udelay(10);
+}
+
+static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr)
+{
+ RTL_W32(tp, EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+
+ return rtl_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ?
+ RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0;
+}
+
+static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u16 reg)
+{
+ RTL_W32(tp, OCPAR, 0x0fu << 12 | (reg & 0x0fff));
+ return rtl_loop_wait_high(tp, &rtl_ocpar_cond, 100, 20) ?
+ RTL_R32(tp, OCPDR) : ~0;
+}
+
+static u32 r8168ep_ocp_read(struct rtl8169_private *tp, u16 reg)
+{
+ return _rtl_eri_read(tp, reg, ERIAR_OOB);
+}
+
+static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
+ u32 data)
+{
+ RTL_W32(tp, OCPDR, data);
+ RTL_W32(tp, OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
+ rtl_loop_wait_low(tp, &rtl_ocpar_cond, 100, 20);
+}
+
+static void r8168ep_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
+ u32 data)
+{
+ _rtl_eri_write(tp, reg, ((u32)mask & 0x0f) << ERIAR_MASK_SHIFT,
+ data, ERIAR_OOB);
+}
+
+static void r8168dp_oob_notify(struct rtl8169_private *tp, u8 cmd)
+{
+ rtl_eri_write(tp, 0xe8, ERIAR_MASK_0001, cmd);
+
+ r8168dp_ocp_write(tp, 0x1, 0x30, 0x00000001);
+}
+
+#define OOB_CMD_RESET 0x00
+#define OOB_CMD_DRIVER_START 0x05
+#define OOB_CMD_DRIVER_STOP 0x06
+
+static u16 rtl8168_get_ocp_reg(struct rtl8169_private *tp)
+{
+ return (tp->mac_version == RTL_GIGA_MAC_VER_31) ? 0xb8 : 0x10;
+}
+
+DECLARE_RTL_COND(rtl_dp_ocp_read_cond)
+{
+ u16 reg;
+
+ reg = rtl8168_get_ocp_reg(tp);
+
+ return r8168dp_ocp_read(tp, reg) & 0x00000800;
+}
+
+DECLARE_RTL_COND(rtl_ep_ocp_read_cond)
+{
+ return r8168ep_ocp_read(tp, 0x124) & 0x00000001;
+}
+
+DECLARE_RTL_COND(rtl_ocp_tx_cond)
+{
+ return RTL_R8(tp, IBISR0) & 0x20;
+}
+
+static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, IBCR2, RTL_R8(tp, IBCR2) & ~0x01);
+ rtl_loop_wait_high(tp, &rtl_ocp_tx_cond, 50000, 2000);
+ RTL_W8(tp, IBISR0, RTL_R8(tp, IBISR0) | 0x20);
+ RTL_W8(tp, IBCR0, RTL_R8(tp, IBCR0) & ~0x01);
+}
+
+static void rtl8168dp_driver_start(struct rtl8169_private *tp)
+{
+ r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START);
+ rtl_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10000, 10);
+}
+
+static void rtl8168ep_driver_start(struct rtl8169_private *tp)
+{
+ r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
+ r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01);
+ rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 10);
+}
+
+static void rtl8168_driver_start(struct rtl8169_private *tp)
+{
+ if (tp->dash_type == RTL_DASH_DP)
+ rtl8168dp_driver_start(tp);
+ else
+ rtl8168ep_driver_start(tp);
+}
+
+static bool r8168dp_check_dash(struct rtl8169_private *tp)
+{
+ u16 reg = rtl8168_get_ocp_reg(tp);
+
+ return r8168dp_ocp_read(tp, reg) & BIT(15);
+}
+
+static bool r8168ep_check_dash(struct rtl8169_private *tp)
+{
+ return r8168ep_ocp_read(tp, 0x128) & BIT(0);
+}
+
+static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_28:
+ case RTL_GIGA_MAC_VER_31:
+ return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE;
+ case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53:
+ return r8168ep_check_dash(tp) ? RTL_DASH_EP : RTL_DASH_NONE;
+ default:
+ return RTL_DASH_NONE;
+ }
+}
+
+static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26:
+ case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30:
+ case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
+ if (enable)
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
+ else
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | D3_NO_PLL_DOWN);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtl_reset_packet_filter(struct rtl8169_private *tp)
+{
+ rtl_eri_clear_bits(tp, 0xdc, BIT(0));
+ rtl_eri_set_bits(tp, 0xdc, BIT(0));
+}
+
+DECLARE_RTL_COND(rtl_efusear_cond)
+{
+ return RTL_R32(tp, EFUSEAR) & EFUSEAR_FLAG;
+}
+
+u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr)
+{
+ RTL_W32(tp, EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT);
+
+ return rtl_loop_wait_high(tp, &rtl_efusear_cond, 100, 300) ?
+ RTL_R32(tp, EFUSEAR) & EFUSEAR_DATA_MASK : ~0;
+}
+
+static void rtl_link_chg_patch(struct rtl8169_private *tp)
+{
+ struct phy_device *phydev = tp->phydev;
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_34 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_38) {
+ if (phydev->speed == SPEED_1000) {
+ rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011);
+ rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005);
+ } else if (phydev->speed == SPEED_100) {
+ rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f);
+ rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005);
+ } else {
+ rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f);
+ rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f);
+ }
+ rtl_reset_packet_filter(tp);
+ } else if (tp->mac_version == RTL_GIGA_MAC_VER_35 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_36) {
+ if (phydev->speed == SPEED_1000) {
+ rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011);
+ rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005);
+ } else {
+ rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f);
+ rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f);
+ }
+ } else if (tp->mac_version == RTL_GIGA_MAC_VER_37) {
+ if (phydev->speed == SPEED_10) {
+ rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x4d02);
+ rtl_eri_write(tp, 0x1dc, ERIAR_MASK_0011, 0x0060a);
+ } else {
+ rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x0000);
+ }
+ }
+}
+
+static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii)
+{
+ /*
+ * The driver currently handles the 8168Bf and the 8168Be identically
+ * but they can be identified more specifically through the test below
+ * if needed:
+ *
+ * (RTL_R32(tp, TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be
+ *
+ * Same thing for the 8101Eb and the 8101Ec:
+ *
+ * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
+ */
+ static const struct rtl_mac_info {
+ u16 mask;
+ u16 val;
+ enum mac_version ver;
+ } mac_info[] = {
+ /* 8125B family. */
+ { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 },
+
+ /* 8125A family. */
+ { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61 },
+ /* It seems only XID 609 made it to the mass market.
+ * { 0x7cf, 0x608, RTL_GIGA_MAC_VER_60 },
+ * { 0x7c8, 0x608, RTL_GIGA_MAC_VER_61 },
+ */
+
+ /* RTL8117 */
+ { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_53 },
+ { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52 },
+
+ /* 8168EP family. */
+ { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51 },
+ /* It seems this chip version never made it to
+ * the wild. Let's disable detection.
+ * { 0x7cf, 0x501, RTL_GIGA_MAC_VER_50 },
+ * { 0x7cf, 0x500, RTL_GIGA_MAC_VER_49 },
+ */
+
+ /* 8168H family. */
+ { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46 },
+ /* It seems this chip version never made it to
+ * the wild. Let's disable detection.
+ * { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 },
+ */
+
+ /* 8168G family. */
+ { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 },
+ { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42 },
+ /* It seems this chip version never made it to
+ * the wild. Let's disable detection.
+ * { 0x7cf, 0x4c1, RTL_GIGA_MAC_VER_41 },
+ */
+ { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40 },
+
+ /* 8168F family. */
+ { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 },
+ { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 },
+ { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 },
+
+ /* 8168E family. */
+ { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34 },
+ { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32 },
+ { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33 },
+
+ /* 8168D family. */
+ { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25 },
+ { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 },
+
+ /* 8168DP family. */
+ /* It seems this early RTL8168dp version never made it to
+ * the wild. Support has been removed.
+ * { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 },
+ */
+ { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 },
+ { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 },
+
+ /* 8168C family. */
+ { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23 },
+ { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18 },
+ { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24 },
+ { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19 },
+ { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20 },
+ { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21 },
+ { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 },
+
+ /* 8168B family. */
+ { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 },
+ { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
+
+ /* 8101 family. */
+ { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 },
+ { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37 },
+ { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29 },
+ { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30 },
+ { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08 },
+ { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 },
+ { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 },
+ { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 },
+ { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14 },
+ { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 },
+ { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 },
+ { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10 },
+
+ /* 8110 family. */
+ { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 },
+ { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05 },
+ { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04 },
+ { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03 },
+ { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02 },
+
+ /* Catch-all */
+ { 0x000, 0x000, RTL_GIGA_MAC_NONE }
+ };
+ const struct rtl_mac_info *p = mac_info;
+ enum mac_version ver;
+
+ while ((xid & p->mask) != p->val)
+ p++;
+ ver = p->ver;
+
+ if (ver != RTL_GIGA_MAC_NONE && !gmii) {
+ if (ver == RTL_GIGA_MAC_VER_42)
+ ver = RTL_GIGA_MAC_VER_43;
+ else if (ver == RTL_GIGA_MAC_VER_46)
+ ver = RTL_GIGA_MAC_VER_48;
+ }
+
+ return ver;
+}
+
+void r8169_apply_firmware(struct rtl8169_private *tp)
+{
+ int val;
+ u64 start;
+
+ /* TODO: release firmware if rtl_fw_write_firmware signals failure. */
+ if (tp->rtl_fw) {
+ rtl_fw_write_firmware(tp, tp->rtl_fw);
+ /* At least one firmware doesn't reset tp->ocp_base. */
+ tp->ocp_base = OCP_STD_PHY_BASE;
+
+ /* PHY soft reset may still be in progress */
+ start = get_time_ns();
+ while (is_timeout(start, SECOND)) {
+ val = phy_read(tp->phydev, MII_BMCR);
+ if (!(val & BMCR_RESET))
+ return;
+ }
+ }
+}
+
+static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr)
+{
+ rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, get_unaligned_le32(addr));
+ rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, get_unaligned_le16(addr + 4));
+ rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, get_unaligned_le16(addr) << 16);
+ rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, get_unaligned_le32(addr + 2));
+}
+
+u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp)
+{
+ u16 data1, data2, ioffset;
+
+ r8168_mac_ocp_write(tp, 0xdd02, 0x807d);
+ data1 = r8168_mac_ocp_read(tp, 0xdd02);
+ data2 = r8168_mac_ocp_read(tp, 0xdd00);
+
+ ioffset = (data2 >> 1) & 0x7ff8;
+ ioffset |= data2 & 0x0007;
+ if (data1 & BIT(7))
+ ioffset |= BIT(15);
+
+ return ioffset;
+}
+
+static void rtl8169_init_phy(struct rtl8169_private *tp)
+{
+ r8169_hw_phy_config(tp, tp->phydev, tp->mac_version);
+
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_06) {
+ pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40);
+ pci_write_config_byte(tp->pci_dev, PCI_CACHE_LINE_SIZE, 0x08);
+ /* set undocumented MAC Reg C+CR Offset 0x82h */
+ RTL_W8(tp, 0x82, 0x01);
+ }
+}
+
+static void rtl_rar_set(struct rtl8169_private *tp, const u8 *addr)
+{
+ rtl_unlock_config_regs(tp);
+
+ RTL_W32(tp, MAC4, get_unaligned_le16(addr + 4));
+ rtl_pci_commit(tp);
+
+ RTL_W32(tp, MAC0, get_unaligned_le32(addr));
+ rtl_pci_commit(tp);
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_34)
+ rtl_rar_exgmac_set(tp, addr);
+
+ rtl_lock_config_regs(tp);
+}
+
+static void rtl_init_rxcfg(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
+ case RTL_GIGA_MAC_VER_10 ... RTL_GIGA_MAC_VER_17:
+ RTL_W32(tp, RxConfig, RX_FIFO_THRESH | RX_DMA_BURST);
+ break;
+ case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24:
+ case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_36:
+ case RTL_GIGA_MAC_VER_38:
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
+ break;
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53:
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF);
+ break;
+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
+ RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST);
+ break;
+ default:
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_DMA_BURST);
+ break;
+ }
+}
+
+DECLARE_RTL_COND(rtl_chipcmd_cond)
+{
+ return RTL_R8(tp, ChipCmd) & CmdReset;
+}
+
+static void rtl_hw_reset(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, ChipCmd, CmdReset);
+
+ rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
+}
+
+static void rtl_request_firmware(struct rtl8169_private *tp)
+{
+ struct rtl_fw *rtl_fw;
+
+ /* firmware loaded already or no firmware available */
+ if (tp->rtl_fw || !tp->fw_name)
+ return;
+
+ rtl_fw = kzalloc(sizeof(*rtl_fw), GFP_KERNEL);
+ if (!rtl_fw)
+ return;
+
+ rtl_fw->phy_write = rtl_writephy;
+ rtl_fw->phy_read = rtl_readphy;
+ rtl_fw->mac_mcu_write = mac_mcu_write;
+ rtl_fw->mac_mcu_read = mac_mcu_read;
+ rtl_fw->fw_name = tp->fw_name;
+ rtl_fw->dev = tp_to_dev(tp);
+
+ if (rtl_fw_request_firmware(rtl_fw))
+ kfree(rtl_fw);
+ else
+ tp->rtl_fw = rtl_fw;
+}
+
+static void rtl_rx_close(struct rtl8169_private *tp)
+{
+ RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) & ~RX_CONFIG_ACCEPT_MASK);
+}
+
+DECLARE_RTL_COND(rtl_npq_cond)
+{
+ return RTL_R8(tp, TxPoll) & NPQ;
+}
+
+DECLARE_RTL_COND(rtl_txcfg_empty_cond)
+{
+ return RTL_R32(tp, TxConfig) & TXCFG_EMPTY;
+}
+
+DECLARE_RTL_COND(rtl_rxtx_empty_cond)
+{
+ return (RTL_R8(tp, MCU) & RXTX_EMPTY) == RXTX_EMPTY;
+}
+
+DECLARE_RTL_COND(rtl_rxtx_empty_cond_2)
+{
+ /* IntrMitigate has new functionality on RTL8125 */
+ return (RTL_R16(tp, IntrMitigate) & 0x0103) == 0x0103;
+}
+
+static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53:
+ rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42);
+ rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+ break;
+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61:
+ rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+ break;
+ case RTL_GIGA_MAC_VER_63:
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+ rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+ rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtl_disable_rxdvgate(struct rtl8169_private *tp)
+{
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN);
+}
+
+static void rtl_enable_rxdvgate(struct rtl8169_private *tp)
+{
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN);
+ udelay(2000);
+ rtl_wait_txrx_fifo_empty(tp);
+}
+
+static void rtl_set_tx_config_registers(struct rtl8169_private *tp)
+{
+ u32 val = TX_DMA_BURST << TxDMAShift |
+ InterFrameGap << TxInterFrameGapShift;
+
+ if (rtl_is_8168evl_up(tp))
+ val |= TXCFG_AUTO_FIFO;
+
+ RTL_W32(tp, TxConfig, val);
+}
+
+static void rtl_set_rx_max_size(struct rtl8169_private *tp)
+{
+ /* Low hurts. Let's disable the filtering. */
+ RTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1);
+}
+
+static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
+{
+ /*
+ * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
+ * register to be written before TxDescAddrLow to work.
+ * Switching from MMIO to I/O access fixes the issue as well.
+ */
+ RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32);
+ RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32));
+ RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
+ RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
+}
+
+static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
+{
+ u32 val;
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_05)
+ val = 0x000fff00;
+ else if (tp->mac_version == RTL_GIGA_MAC_VER_06)
+ val = 0x00ffff00;
+ else
+ return;
+
+ if (RTL_R8(tp, Config2) & PCI_Clock_66MHz)
+ val |= 0xff;
+
+ RTL_W32(tp, 0x7c, val);
+}
+
+static void rtl_set_rx_mode(struct rtl8169_private *tp)
+{
+ u32 rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
+ /* Multicast hash filter */
+ u32 mc_filter[2] = { 0xffffffff, 0xffffffff };
+ u32 tmp;
+
+ RTL_W32(tp, MAR0 + 4, mc_filter[1]);
+ RTL_W32(tp, MAR0 + 0, mc_filter[0]);
+
+ tmp = RTL_R32(tp, RxConfig);
+ RTL_W32(tp, RxConfig, (tmp & ~RX_CONFIG_ACCEPT_OK_MASK) | rx_mode);
+}
+
+DECLARE_RTL_COND(rtl_csiar_cond)
+{
+ return RTL_R32(tp, CSIAR) & CSIAR_FLAG;
+}
+
+static void rtl_csi_write(struct rtl8169_private *tp, int addr, int value)
+{
+ u32 func = PCI_FUNC(tp->pci_dev->devfn);
+
+ RTL_W32(tp, CSIDR, value);
+ RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
+ CSIAR_BYTE_ENABLE | func << 16);
+
+ rtl_loop_wait_low(tp, &rtl_csiar_cond, 10, 100);
+}
+
+static u32 rtl_csi_read(struct rtl8169_private *tp, int addr)
+{
+ u32 func = PCI_FUNC(tp->pci_dev->devfn);
+
+ RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) | func << 16 |
+ CSIAR_BYTE_ENABLE);
+
+ return rtl_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
+ RTL_R32(tp, CSIDR) : ~0;
+}
+
+static void rtl_set_aspm_entry_latency(struct rtl8169_private *tp, u8 val)
+{
+ struct pci_dev *pdev = tp->pci_dev;
+ u32 csi;
+
+ /* According to Realtek the value at config space address 0x070f
+ * controls the L0s/L1 entrance latency. We try standard ECAM access
+ * first and if it fails fall back to CSI.
+ * bit 0..2: L0: 0 = 1us, 1 = 2us .. 6 = 7us, 7 = 7us (no typo)
+ * bit 3..5: L1: 0 = 1us, 1 = 2us .. 6 = 64us, 7 = 64us
+ */
+ if (pci_write_config_byte(pdev, 0x070f, val) == PCIBIOS_SUCCESSFUL)
+ return;
+
+ dev_dbg(tp->dev,
+ "No native access to PCI extended config space, falling back to CSI\n");
+ csi = rtl_csi_read(tp, 0x070c) & 0x00ffffff;
+ rtl_csi_write(tp, 0x070c, csi | val << 24);
+}
+
+static void rtl_set_def_aspm_entry_latency(struct rtl8169_private *tp)
+{
+ /* L0 7us, L1 16us */
+ rtl_set_aspm_entry_latency(tp, 0x27);
+}
+
+struct ephy_info {
+ unsigned int offset;
+ u16 mask;
+ u16 bits;
+};
+
+static void __rtl_ephy_init(struct rtl8169_private *tp,
+ const struct ephy_info *e, int len)
+{
+ u16 w;
+
+ while (len-- > 0) {
+ w = (rtl_ephy_read(tp, e->offset) & ~e->mask) | e->bits;
+ rtl_ephy_write(tp, e->offset, w);
+ e++;
+ }
+}
+
+#define rtl_ephy_init(tp, a) __rtl_ephy_init(tp, a, ARRAY_SIZE(a))
+
+static void rtl_disable_clock_request(struct rtl8169_private *tp)
+{
+ /* Not yet implemented */
+}
+
+static void rtl_enable_clock_request(struct rtl8169_private *tp)
+{
+ /* Not yet implemented */
+}
+
+static void rtl_pcie_state_l2l3_disable(struct rtl8169_private *tp)
+{
+ /* work around an issue when PCI reset occurs during L2/L3 state */
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Rdy_to_L23);
+}
+
+static void rtl_enable_exit_l1(struct rtl8169_private *tp)
+{
+ /* Bits control which events trigger ASPM L1 exit:
+ * Bit 12: rxdv
+ * Bit 11: ltr_msg
+ * Bit 10: txdma_poll
+ * Bit 9: xadm
+ * Bit 8: pktavi
+ * Bit 7: txpla
+ */
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_36:
+ rtl_eri_set_bits(tp, 0xd4, 0x1f00);
+ break;
+ case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38:
+ rtl_eri_set_bits(tp, 0xd4, 0x0c00);
+ break;
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
+ r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtl_disable_exit_l1(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
+ rtl_eri_clear_bits(tp, 0xd4, 0x1f00);
+ break;
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
+ r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
+{
+ /* Don't enable ASPM in the chip if OS can't control ASPM */
+ if (enable && tp->aspm_manageable) {
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn);
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
+ /* reset ephy tx/rx disable timer */
+ r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0);
+ /* chip can trigger L1.2 */
+ r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, BIT(2));
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
+ r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
+ break;
+ default:
+ break;
+ }
+
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
+ }
+
+ udelay(10);
+}
+
+static void rtl_set_fifo_size(struct rtl8169_private *tp, u16 rx_stat,
+ u16 tx_stat, u16 rx_dyn, u16 tx_dyn)
+{
+ /* Usage of dynamic vs. static FIFO is controlled by bit
+ * TXCFG_AUTO_FIFO. Exact meaning of FIFO values isn't known.
+ */
+ rtl_eri_write(tp, 0xc8, ERIAR_MASK_1111, (rx_stat << 16) | rx_dyn);
+ rtl_eri_write(tp, 0xe8, ERIAR_MASK_1111, (tx_stat << 16) | tx_dyn);
+}
+
+static void rtl8168g_set_pause_thresholds(struct rtl8169_private *tp,
+ u8 low, u8 high)
+{
+ /* FIFO thresholds for pause flow control */
+ rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, low);
+ rtl_eri_write(tp, 0xd0, ERIAR_MASK_0001, high);
+}
+
+static void rtl_hw_start_8168b(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+}
+
+static void __rtl_hw_start_8168cp(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config1, RTL_R8(tp, Config1) | Speed_down);
+
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+
+ rtl_disable_clock_request(tp);
+}
+
+static void rtl_hw_start_8168cp_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168cp[] = {
+ { 0x01, 0, 0x0001 },
+ { 0x02, 0x0800, 0x1000 },
+ { 0x03, 0, 0x0042 },
+ { 0x06, 0x0080, 0x0000 },
+ { 0x07, 0, 0x2000 }
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_ephy_init(tp, e_info_8168cp);
+
+ __rtl_hw_start_8168cp(tp);
+}
+
+static void rtl_hw_start_8168cp_2(struct rtl8169_private *tp)
+{
+ rtl_set_def_aspm_entry_latency(tp);
+
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+}
+
+static void rtl_hw_start_8168cp_3(struct rtl8169_private *tp)
+{
+ rtl_set_def_aspm_entry_latency(tp);
+
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+
+ /* Magic. */
+ RTL_W8(tp, DBG_REG, 0x20);
+}
+
+static void rtl_hw_start_8168c_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168c_1[] = {
+ { 0x02, 0x0800, 0x1000 },
+ { 0x03, 0, 0x0002 },
+ { 0x06, 0x0080, 0x0000 }
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ RTL_W8(tp, DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2);
+
+ rtl_ephy_init(tp, e_info_8168c_1);
+
+ __rtl_hw_start_8168cp(tp);
+}
+
+static void rtl_hw_start_8168c_2(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168c_2[] = {
+ { 0x01, 0, 0x0001 },
+ { 0x03, 0x0400, 0x0020 }
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_ephy_init(tp, e_info_8168c_2);
+
+ __rtl_hw_start_8168cp(tp);
+}
+
+static void rtl_hw_start_8168c_4(struct rtl8169_private *tp)
+{
+ rtl_set_def_aspm_entry_latency(tp);
+
+ __rtl_hw_start_8168cp(tp);
+}
+
+static void rtl_hw_start_8168d(struct rtl8169_private *tp)
+{
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_disable_clock_request(tp);
+}
+
+static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168d_4[] = {
+ { 0x0b, 0x0000, 0x0048 },
+ { 0x19, 0x0020, 0x0050 },
+ { 0x0c, 0x0100, 0x0020 },
+ { 0x10, 0x0004, 0x0000 },
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_ephy_init(tp, e_info_8168d_4);
+
+ rtl_enable_clock_request(tp);
+}
+
+static void rtl_hw_start_8168e_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168e_1[] = {
+ { 0x00, 0x0200, 0x0100 },
+ { 0x00, 0x0000, 0x0004 },
+ { 0x06, 0x0002, 0x0001 },
+ { 0x06, 0x0000, 0x0030 },
+ { 0x07, 0x0000, 0x2000 },
+ { 0x00, 0x0000, 0x0020 },
+ { 0x03, 0x5800, 0x2000 },
+ { 0x03, 0x0000, 0x0001 },
+ { 0x01, 0x0800, 0x1000 },
+ { 0x07, 0x0000, 0x4000 },
+ { 0x1e, 0x0000, 0x2000 },
+ { 0x19, 0xffff, 0xfe6c },
+ { 0x0a, 0x0000, 0x0040 }
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_ephy_init(tp, e_info_8168e_1);
+
+ rtl_disable_clock_request(tp);
+
+ /* Reset tx FIFO pointer */
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST);
+
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
+}
+
+static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168e_2[] = {
+ { 0x09, 0x0000, 0x0080 },
+ { 0x19, 0x0000, 0x0224 },
+ { 0x00, 0x0000, 0x0004 },
+ { 0x0c, 0x3df0, 0x0200 },
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_ephy_init(tp, e_info_8168e_2);
+
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_1111, 0x0000);
+ rtl_set_fifo_size(tp, 0x10, 0x10, 0x02, 0x06);
+ rtl_eri_set_bits(tp, 0x1d0, BIT(1));
+ rtl_reset_packet_filter(tp);
+ rtl_eri_set_bits(tp, 0x1b0, BIT(4));
+ rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050);
+ rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x07ff0060);
+
+ rtl_disable_clock_request(tp);
+
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8168f(struct rtl8169_private *tp)
+{
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_1111, 0x0000);
+ rtl_set_fifo_size(tp, 0x10, 0x10, 0x02, 0x06);
+ rtl_reset_packet_filter(tp);
+ rtl_eri_set_bits(tp, 0x1b0, BIT(4));
+ rtl_eri_set_bits(tp, 0x1d0, BIT(4) | BIT(1));
+ rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050);
+ rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x00000060);
+
+ rtl_disable_clock_request(tp);
+
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
+}
+
+static void rtl_hw_start_8168f_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168f_1[] = {
+ { 0x06, 0x00c0, 0x0020 },
+ { 0x08, 0x0001, 0x0002 },
+ { 0x09, 0x0000, 0x0080 },
+ { 0x19, 0x0000, 0x0224 },
+ { 0x00, 0x0000, 0x0008 },
+ { 0x0c, 0x3df0, 0x0200 },
+ };
+
+ rtl_hw_start_8168f(tp);
+
+ rtl_ephy_init(tp, e_info_8168f_1);
+}
+
+static void rtl_hw_start_8411(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168f_1[] = {
+ { 0x06, 0x00c0, 0x0020 },
+ { 0x0f, 0xffff, 0x5200 },
+ { 0x19, 0x0000, 0x0224 },
+ { 0x00, 0x0000, 0x0008 },
+ { 0x0c, 0x3df0, 0x0200 },
+ };
+
+ rtl_hw_start_8168f(tp);
+ rtl_pcie_state_l2l3_disable(tp);
+
+ rtl_ephy_init(tp, e_info_8168f_1);
+}
+
+static void rtl_hw_start_8168g(struct rtl8169_private *tp)
+{
+ rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06);
+ rtl8168g_set_pause_thresholds(tp, 0x38, 0x48);
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_reset_packet_filter(tp);
+ rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f);
+
+ rtl_disable_rxdvgate(tp);
+
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
+
+ rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06);
+ rtl_eri_clear_bits(tp, 0x1b0, BIT(12));
+
+ rtl_pcie_state_l2l3_disable(tp);
+}
+
+static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168g_1[] = {
+ { 0x00, 0x0008, 0x0000 },
+ { 0x0c, 0x3ff0, 0x0820 },
+ { 0x1e, 0x0000, 0x0001 },
+ { 0x19, 0x8000, 0x0000 }
+ };
+
+ rtl_hw_start_8168g(tp);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8168g_1);
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8168g_2(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168g_2[] = {
+ { 0x00, 0x0008, 0x0000 },
+ { 0x0c, 0x3ff0, 0x0820 },
+ { 0x19, 0xffff, 0x7c00 },
+ { 0x1e, 0xffff, 0x20eb },
+ { 0x0d, 0xffff, 0x1666 },
+ { 0x00, 0xffff, 0x10a3 },
+ { 0x06, 0xffff, 0xf050 },
+ { 0x04, 0x0000, 0x0010 },
+ { 0x1d, 0x4000, 0x0000 },
+ };
+
+ rtl_hw_start_8168g(tp);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8168g_2);
+}
+
+static void rtl_hw_start_8411_2(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8411_2[] = {
+ { 0x00, 0x0008, 0x0000 },
+ { 0x0c, 0x37d0, 0x0820 },
+ { 0x1e, 0x0000, 0x0001 },
+ { 0x19, 0x8021, 0x0000 },
+ { 0x1e, 0x0000, 0x2000 },
+ { 0x0d, 0x0100, 0x0200 },
+ { 0x00, 0x0000, 0x0080 },
+ { 0x06, 0x0000, 0x0010 },
+ { 0x04, 0x0000, 0x0010 },
+ { 0x1d, 0x0000, 0x4000 },
+ };
+
+ rtl_hw_start_8168g(tp);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8411_2);
+
+ /* The following Realtek-provided magic fixes an issue with the RX unit
+ * getting confused after the PHY having been powered-down.
+ */
+ r8168_mac_ocp_write(tp, 0xFC28, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC2A, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC2C, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC2E, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC30, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC32, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC34, 0x0000);
+ r8168_mac_ocp_write(tp, 0xFC36, 0x0000);
+ mdelay(3);
+ r8168_mac_ocp_write(tp, 0xFC26, 0x0000);
+
+ r8168_mac_ocp_write(tp, 0xF800, 0xE008);
+ r8168_mac_ocp_write(tp, 0xF802, 0xE00A);
+ r8168_mac_ocp_write(tp, 0xF804, 0xE00C);
+ r8168_mac_ocp_write(tp, 0xF806, 0xE00E);
+ r8168_mac_ocp_write(tp, 0xF808, 0xE027);
+ r8168_mac_ocp_write(tp, 0xF80A, 0xE04F);
+ r8168_mac_ocp_write(tp, 0xF80C, 0xE05E);
+ r8168_mac_ocp_write(tp, 0xF80E, 0xE065);
+ r8168_mac_ocp_write(tp, 0xF810, 0xC602);
+ r8168_mac_ocp_write(tp, 0xF812, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF814, 0x0000);
+ r8168_mac_ocp_write(tp, 0xF816, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF818, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF81A, 0x074C);
+ r8168_mac_ocp_write(tp, 0xF81C, 0xC302);
+ r8168_mac_ocp_write(tp, 0xF81E, 0xBB00);
+ r8168_mac_ocp_write(tp, 0xF820, 0x080A);
+ r8168_mac_ocp_write(tp, 0xF822, 0x6420);
+ r8168_mac_ocp_write(tp, 0xF824, 0x48C2);
+ r8168_mac_ocp_write(tp, 0xF826, 0x8C20);
+ r8168_mac_ocp_write(tp, 0xF828, 0xC516);
+ r8168_mac_ocp_write(tp, 0xF82A, 0x64A4);
+ r8168_mac_ocp_write(tp, 0xF82C, 0x49C0);
+ r8168_mac_ocp_write(tp, 0xF82E, 0xF009);
+ r8168_mac_ocp_write(tp, 0xF830, 0x74A2);
+ r8168_mac_ocp_write(tp, 0xF832, 0x8CA5);
+ r8168_mac_ocp_write(tp, 0xF834, 0x74A0);
+ r8168_mac_ocp_write(tp, 0xF836, 0xC50E);
+ r8168_mac_ocp_write(tp, 0xF838, 0x9CA2);
+ r8168_mac_ocp_write(tp, 0xF83A, 0x1C11);
+ r8168_mac_ocp_write(tp, 0xF83C, 0x9CA0);
+ r8168_mac_ocp_write(tp, 0xF83E, 0xE006);
+ r8168_mac_ocp_write(tp, 0xF840, 0x74F8);
+ r8168_mac_ocp_write(tp, 0xF842, 0x48C4);
+ r8168_mac_ocp_write(tp, 0xF844, 0x8CF8);
+ r8168_mac_ocp_write(tp, 0xF846, 0xC404);
+ r8168_mac_ocp_write(tp, 0xF848, 0xBC00);
+ r8168_mac_ocp_write(tp, 0xF84A, 0xC403);
+ r8168_mac_ocp_write(tp, 0xF84C, 0xBC00);
+ r8168_mac_ocp_write(tp, 0xF84E, 0x0BF2);
+ r8168_mac_ocp_write(tp, 0xF850, 0x0C0A);
+ r8168_mac_ocp_write(tp, 0xF852, 0xE434);
+ r8168_mac_ocp_write(tp, 0xF854, 0xD3C0);
+ r8168_mac_ocp_write(tp, 0xF856, 0x49D9);
+ r8168_mac_ocp_write(tp, 0xF858, 0xF01F);
+ r8168_mac_ocp_write(tp, 0xF85A, 0xC526);
+ r8168_mac_ocp_write(tp, 0xF85C, 0x64A5);
+ r8168_mac_ocp_write(tp, 0xF85E, 0x1400);
+ r8168_mac_ocp_write(tp, 0xF860, 0xF007);
+ r8168_mac_ocp_write(tp, 0xF862, 0x0C01);
+ r8168_mac_ocp_write(tp, 0xF864, 0x8CA5);
+ r8168_mac_ocp_write(tp, 0xF866, 0x1C15);
+ r8168_mac_ocp_write(tp, 0xF868, 0xC51B);
+ r8168_mac_ocp_write(tp, 0xF86A, 0x9CA0);
+ r8168_mac_ocp_write(tp, 0xF86C, 0xE013);
+ r8168_mac_ocp_write(tp, 0xF86E, 0xC519);
+ r8168_mac_ocp_write(tp, 0xF870, 0x74A0);
+ r8168_mac_ocp_write(tp, 0xF872, 0x48C4);
+ r8168_mac_ocp_write(tp, 0xF874, 0x8CA0);
+ r8168_mac_ocp_write(tp, 0xF876, 0xC516);
+ r8168_mac_ocp_write(tp, 0xF878, 0x74A4);
+ r8168_mac_ocp_write(tp, 0xF87A, 0x48C8);
+ r8168_mac_ocp_write(tp, 0xF87C, 0x48CA);
+ r8168_mac_ocp_write(tp, 0xF87E, 0x9CA4);
+ r8168_mac_ocp_write(tp, 0xF880, 0xC512);
+ r8168_mac_ocp_write(tp, 0xF882, 0x1B00);
+ r8168_mac_ocp_write(tp, 0xF884, 0x9BA0);
+ r8168_mac_ocp_write(tp, 0xF886, 0x1B1C);
+ r8168_mac_ocp_write(tp, 0xF888, 0x483F);
+ r8168_mac_ocp_write(tp, 0xF88A, 0x9BA2);
+ r8168_mac_ocp_write(tp, 0xF88C, 0x1B04);
+ r8168_mac_ocp_write(tp, 0xF88E, 0xC508);
+ r8168_mac_ocp_write(tp, 0xF890, 0x9BA0);
+ r8168_mac_ocp_write(tp, 0xF892, 0xC505);
+ r8168_mac_ocp_write(tp, 0xF894, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF896, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF898, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF89A, 0x0300);
+ r8168_mac_ocp_write(tp, 0xF89C, 0x051E);
+ r8168_mac_ocp_write(tp, 0xF89E, 0xE434);
+ r8168_mac_ocp_write(tp, 0xF8A0, 0xE018);
+ r8168_mac_ocp_write(tp, 0xF8A2, 0xE092);
+ r8168_mac_ocp_write(tp, 0xF8A4, 0xDE20);
+ r8168_mac_ocp_write(tp, 0xF8A6, 0xD3C0);
+ r8168_mac_ocp_write(tp, 0xF8A8, 0xC50F);
+ r8168_mac_ocp_write(tp, 0xF8AA, 0x76A4);
+ r8168_mac_ocp_write(tp, 0xF8AC, 0x49E3);
+ r8168_mac_ocp_write(tp, 0xF8AE, 0xF007);
+ r8168_mac_ocp_write(tp, 0xF8B0, 0x49C0);
+ r8168_mac_ocp_write(tp, 0xF8B2, 0xF103);
+ r8168_mac_ocp_write(tp, 0xF8B4, 0xC607);
+ r8168_mac_ocp_write(tp, 0xF8B6, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF8B8, 0xC606);
+ r8168_mac_ocp_write(tp, 0xF8BA, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF8BC, 0xC602);
+ r8168_mac_ocp_write(tp, 0xF8BE, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF8C0, 0x0C4C);
+ r8168_mac_ocp_write(tp, 0xF8C2, 0x0C28);
+ r8168_mac_ocp_write(tp, 0xF8C4, 0x0C2C);
+ r8168_mac_ocp_write(tp, 0xF8C6, 0xDC00);
+ r8168_mac_ocp_write(tp, 0xF8C8, 0xC707);
+ r8168_mac_ocp_write(tp, 0xF8CA, 0x1D00);
+ r8168_mac_ocp_write(tp, 0xF8CC, 0x8DE2);
+ r8168_mac_ocp_write(tp, 0xF8CE, 0x48C1);
+ r8168_mac_ocp_write(tp, 0xF8D0, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF8D2, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF8D4, 0x00AA);
+ r8168_mac_ocp_write(tp, 0xF8D6, 0xE0C0);
+ r8168_mac_ocp_write(tp, 0xF8D8, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF8DA, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF8DC, 0x0132);
+
+ r8168_mac_ocp_write(tp, 0xFC26, 0x8000);
+
+ r8168_mac_ocp_write(tp, 0xFC2A, 0x0743);
+ r8168_mac_ocp_write(tp, 0xFC2C, 0x0801);
+ r8168_mac_ocp_write(tp, 0xFC2E, 0x0BE9);
+ r8168_mac_ocp_write(tp, 0xFC30, 0x02FD);
+ r8168_mac_ocp_write(tp, 0xFC32, 0x0C25);
+ r8168_mac_ocp_write(tp, 0xFC34, 0x00A9);
+ r8168_mac_ocp_write(tp, 0xFC36, 0x012D);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168h_1[] = {
+ { 0x1e, 0x0800, 0x0001 },
+ { 0x1d, 0x0000, 0x0800 },
+ { 0x05, 0xffff, 0x2089 },
+ { 0x06, 0xffff, 0x5881 },
+ { 0x04, 0xffff, 0x854a },
+ { 0x01, 0xffff, 0x068b }
+ };
+ int rg_saw_cnt;
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8168h_1);
+
+ rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06);
+ rtl8168g_set_pause_thresholds(tp, 0x38, 0x48);
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_reset_packet_filter(tp);
+
+ rtl_eri_set_bits(tp, 0xdc, 0x001c);
+
+ rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87);
+
+ rtl_disable_rxdvgate(tp);
+
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+ RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN);
+
+ rtl_eri_clear_bits(tp, 0x1b0, BIT(12));
+
+ rtl_pcie_state_l2l3_disable(tp);
+
+ rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff;
+ if (rg_saw_cnt > 0) {
+ u16 sw_cnt_1ms_ini;
+
+ sw_cnt_1ms_ini = 16000000/rg_saw_cnt;
+ sw_cnt_1ms_ini &= 0x0fff;
+ r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini);
+ }
+
+ r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0070);
+ r8168_mac_ocp_modify(tp, 0xe052, 0x6000, 0x8008);
+ r8168_mac_ocp_modify(tp, 0xe0d6, 0x01ff, 0x017f);
+ r8168_mac_ocp_modify(tp, 0xd420, 0x0fff, 0x047f);
+
+ r8168_mac_ocp_write(tp, 0xe63e, 0x0001);
+ r8168_mac_ocp_write(tp, 0xe63e, 0x0000);
+ r8168_mac_ocp_write(tp, 0xc094, 0x0000);
+ r8168_mac_ocp_write(tp, 0xc09e, 0x0000);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8168ep(struct rtl8169_private *tp)
+{
+ rtl8168ep_stop_cmac(tp);
+
+ rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06);
+ rtl8168g_set_pause_thresholds(tp, 0x2f, 0x5f);
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_reset_packet_filter(tp);
+
+ rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87);
+
+ rtl_disable_rxdvgate(tp);
+
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
+
+ rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN);
+
+ rtl_pcie_state_l2l3_disable(tp);
+}
+
+static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8168ep_3[] = {
+ { 0x00, 0x0000, 0x0080 },
+ { 0x0d, 0x0100, 0x0200 },
+ { 0x19, 0x8021, 0x0000 },
+ { 0x1e, 0x0000, 0x2000 },
+ };
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8168ep_3);
+
+ rtl_hw_start_8168ep(tp);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+ RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
+
+ r8168_mac_ocp_modify(tp, 0xd3e2, 0x0fff, 0x0271);
+ r8168_mac_ocp_modify(tp, 0xd3e4, 0x00ff, 0x0000);
+ r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8117(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8117[] = {
+ { 0x19, 0x0040, 0x1100 },
+ { 0x59, 0x0040, 0x1100 },
+ };
+ int rg_saw_cnt;
+
+ rtl8168ep_stop_cmac(tp);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8117);
+
+ rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06);
+ rtl8168g_set_pause_thresholds(tp, 0x2f, 0x5f);
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ rtl_reset_packet_filter(tp);
+
+ rtl_eri_set_bits(tp, 0xd4, 0x0010);
+
+ rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87);
+
+ rtl_disable_rxdvgate(tp);
+
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+ RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN);
+
+ rtl_eri_clear_bits(tp, 0x1b0, BIT(12));
+
+ rtl_pcie_state_l2l3_disable(tp);
+
+ rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff;
+ if (rg_saw_cnt > 0) {
+ u16 sw_cnt_1ms_ini;
+
+ sw_cnt_1ms_ini = (16000000 / rg_saw_cnt) & 0x0fff;
+ r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini);
+ }
+
+ r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0070);
+ r8168_mac_ocp_write(tp, 0xea80, 0x0003);
+ r8168_mac_ocp_modify(tp, 0xe052, 0x0000, 0x0009);
+ r8168_mac_ocp_modify(tp, 0xd420, 0x0fff, 0x047f);
+
+ r8168_mac_ocp_write(tp, 0xe63e, 0x0001);
+ r8168_mac_ocp_write(tp, 0xe63e, 0x0000);
+ r8168_mac_ocp_write(tp, 0xc094, 0x0000);
+ r8168_mac_ocp_write(tp, 0xc09e, 0x0000);
+
+ /* firmware is for MAC only */
+ r8169_apply_firmware(tp);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8102e_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8102e_1[] = {
+ { 0x01, 0, 0x6e65 },
+ { 0x02, 0, 0x091f },
+ { 0x03, 0, 0xc2f9 },
+ { 0x06, 0, 0xafb5 },
+ { 0x07, 0, 0x0e00 },
+ { 0x19, 0, 0xec80 },
+ { 0x01, 0, 0x2e65 },
+ { 0x01, 0, 0x6e65 }
+ };
+ u8 cfg1;
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ RTL_W8(tp, DBG_REG, FIX_NAK_1);
+
+ RTL_W8(tp, Config1,
+ LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+
+ cfg1 = RTL_R8(tp, Config1);
+ if ((cfg1 & LEDS0) && (cfg1 & LEDS1))
+ RTL_W8(tp, Config1, cfg1 & ~LEDS0);
+
+ rtl_ephy_init(tp, e_info_8102e_1);
+}
+
+static void rtl_hw_start_8102e_2(struct rtl8169_private *tp)
+{
+ rtl_set_def_aspm_entry_latency(tp);
+
+ RTL_W8(tp, Config1, MEMMAP | IOMAP | VPD | PMEnable);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+}
+
+static void rtl_hw_start_8102e_3(struct rtl8169_private *tp)
+{
+ rtl_hw_start_8102e_2(tp);
+
+ rtl_ephy_write(tp, 0x03, 0xc2f9);
+}
+
+static void rtl_hw_start_8401(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8401[] = {
+ { 0x01, 0xffff, 0x6fe5 },
+ { 0x03, 0xffff, 0x0599 },
+ { 0x06, 0xffff, 0xaf25 },
+ { 0x07, 0xffff, 0x8e68 },
+ };
+
+ rtl_ephy_init(tp, e_info_8401);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
+}
+
+static void rtl_hw_start_8105e_1(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8105e_1[] = {
+ { 0x07, 0, 0x4000 },
+ { 0x19, 0, 0x0200 },
+ { 0x19, 0, 0x0020 },
+ { 0x1e, 0, 0x2000 },
+ { 0x03, 0, 0x0001 },
+ { 0x19, 0, 0x0100 },
+ { 0x19, 0, 0x0004 },
+ { 0x0a, 0, 0x0020 }
+ };
+
+ /* Force LAN exit from ASPM if Rx/Tx are not idle */
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800);
+
+ /* Disable Early Tally Counter */
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) & ~0x010000);
+
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
+
+ rtl_ephy_init(tp, e_info_8105e_1);
+
+ rtl_pcie_state_l2l3_disable(tp);
+}
+
+static void rtl_hw_start_8105e_2(struct rtl8169_private *tp)
+{
+ rtl_hw_start_8105e_1(tp);
+ rtl_ephy_write(tp, 0x1e, rtl_ephy_read(tp, 0x1e) | 0x8000);
+}
+
+static void rtl_hw_start_8402(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8402[] = {
+ { 0x19, 0xffff, 0xff64 },
+ { 0x1e, 0, 0x4000 }
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ /* Force LAN exit from ASPM if Rx/Tx are not idle */
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800);
+
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
+
+ rtl_ephy_init(tp, e_info_8402);
+
+ rtl_set_fifo_size(tp, 0x00, 0x00, 0x02, 0x06);
+ rtl_reset_packet_filter(tp);
+ rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
+ rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
+ rtl_w0w1_eri(tp, 0x0d4, 0x0e00, 0xff00);
+
+ /* disable EEE */
+ rtl_eri_write(tp, 0x1b0, ERIAR_MASK_0011, 0x0000);
+
+ rtl_pcie_state_l2l3_disable(tp);
+}
+
+static void rtl_hw_start_8106(struct rtl8169_private *tp)
+{
+ rtl_hw_aspm_clkreq_enable(tp, false);
+
+ /* Force LAN exit from ASPM if Rx/Tx are not idle */
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800);
+
+ RTL_W32(tp, MISC, (RTL_R32(tp, MISC) | DISABLE_LAN_EN) & ~EARLY_TALLY_EN);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+
+ /* L0 7us, L1 32us - needed to avoid issues with link-up detection */
+ rtl_set_aspm_entry_latency(tp, 0x2f);
+
+ rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x0000);
+
+ /* disable EEE */
+ rtl_eri_write(tp, 0x1b0, ERIAR_MASK_0011, 0x0000);
+
+ rtl_pcie_state_l2l3_disable(tp);
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
+{
+ return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
+}
+
+static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
+{
+ rtl_pcie_state_l2l3_disable(tp);
+
+ RTL_W16(tp, 0x382, 0x221b);
+ RTL_W8(tp, 0x4500, 0);
+ RTL_W16(tp, 0x4800, 0);
+
+ /* disable UPS */
+ r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000);
+
+ RTL_W8(tp, Config1, RTL_R8(tp, Config1) & ~0x10);
+
+ r8168_mac_ocp_write(tp, 0xc140, 0xffff);
+ r8168_mac_ocp_write(tp, 0xc142, 0xffff);
+
+ r8168_mac_ocp_modify(tp, 0xd3e2, 0x0fff, 0x03a9);
+ r8168_mac_ocp_modify(tp, 0xd3e4, 0x00ff, 0x0000);
+ r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080);
+
+ /* disable new tx descriptor format */
+ r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000);
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+ r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200);
+ else
+ r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+ r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0000);
+ else
+ r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0020);
+
+ r8168_mac_ocp_modify(tp, 0xc0b4, 0x0000, 0x000c);
+ r8168_mac_ocp_modify(tp, 0xeb6a, 0x00ff, 0x0033);
+ r8168_mac_ocp_modify(tp, 0xeb50, 0x03e0, 0x0040);
+ r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030);
+ r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000);
+ r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001);
+ r8168_mac_ocp_modify(tp, 0xe0c0, 0x4f0f, 0x4403);
+ r8168_mac_ocp_modify(tp, 0xe052, 0x0080, 0x0068);
+ r8168_mac_ocp_modify(tp, 0xd430, 0x0fff, 0x047f);
+
+ r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000);
+ r8168_mac_ocp_modify(tp, 0xeb54, 0x0000, 0x0001);
+ udelay(1);
+ r8168_mac_ocp_modify(tp, 0xeb54, 0x0001, 0x0000);
+ RTL_W16(tp, 0x1880, RTL_R16(tp, 0x1880) & ~0x0030);
+
+ r8168_mac_ocp_write(tp, 0xe098, 0xc302);
+
+ rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10);
+
+ rtl_disable_rxdvgate(tp);
+}
+
+static void rtl_hw_start_8125a_2(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8125a_2[] = {
+ { 0x04, 0xffff, 0xd000 },
+ { 0x0a, 0xffff, 0x8653 },
+ { 0x23, 0xffff, 0xab66 },
+ { 0x20, 0xffff, 0x9455 },
+ { 0x21, 0xffff, 0x99ff },
+ { 0x29, 0xffff, 0xfe04 },
+
+ { 0x44, 0xffff, 0xd000 },
+ { 0x4a, 0xffff, 0x8653 },
+ { 0x63, 0xffff, 0xab66 },
+ { 0x60, 0xffff, 0x9455 },
+ { 0x61, 0xffff, 0x99ff },
+ { 0x69, 0xffff, 0xfe04 },
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8125a_2);
+
+ rtl_hw_start_8125_common(tp);
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_start_8125b(struct rtl8169_private *tp)
+{
+ static const struct ephy_info e_info_8125b[] = {
+ { 0x0b, 0xffff, 0xa908 },
+ { 0x1e, 0xffff, 0x20eb },
+ { 0x4b, 0xffff, 0xa908 },
+ { 0x5e, 0xffff, 0x20eb },
+ { 0x22, 0x0030, 0x0020 },
+ { 0x62, 0x0030, 0x0020 },
+ };
+
+ rtl_set_def_aspm_entry_latency(tp);
+ rtl_hw_aspm_clkreq_enable(tp, false);
+
+ rtl_ephy_init(tp, e_info_8125b);
+ rtl_hw_start_8125_common(tp);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+static void rtl_hw_config(struct rtl8169_private *tp)
+{
+ static const rtl_generic_fct hw_configs[] = {
+ [RTL_GIGA_MAC_VER_07] = rtl_hw_start_8102e_1,
+ [RTL_GIGA_MAC_VER_08] = rtl_hw_start_8102e_3,
+ [RTL_GIGA_MAC_VER_09] = rtl_hw_start_8102e_2,
+ [RTL_GIGA_MAC_VER_10] = NULL,
+ [RTL_GIGA_MAC_VER_11] = rtl_hw_start_8168b,
+ [RTL_GIGA_MAC_VER_14] = rtl_hw_start_8401,
+ [RTL_GIGA_MAC_VER_17] = rtl_hw_start_8168b,
+ [RTL_GIGA_MAC_VER_18] = rtl_hw_start_8168cp_1,
+ [RTL_GIGA_MAC_VER_19] = rtl_hw_start_8168c_1,
+ [RTL_GIGA_MAC_VER_20] = rtl_hw_start_8168c_2,
+ [RTL_GIGA_MAC_VER_21] = rtl_hw_start_8168c_2,
+ [RTL_GIGA_MAC_VER_22] = rtl_hw_start_8168c_4,
+ [RTL_GIGA_MAC_VER_23] = rtl_hw_start_8168cp_2,
+ [RTL_GIGA_MAC_VER_24] = rtl_hw_start_8168cp_3,
+ [RTL_GIGA_MAC_VER_25] = rtl_hw_start_8168d,
+ [RTL_GIGA_MAC_VER_26] = rtl_hw_start_8168d,
+ [RTL_GIGA_MAC_VER_28] = rtl_hw_start_8168d_4,
+ [RTL_GIGA_MAC_VER_29] = rtl_hw_start_8105e_1,
+ [RTL_GIGA_MAC_VER_30] = rtl_hw_start_8105e_2,
+ [RTL_GIGA_MAC_VER_31] = rtl_hw_start_8168d,
+ [RTL_GIGA_MAC_VER_32] = rtl_hw_start_8168e_1,
+ [RTL_GIGA_MAC_VER_33] = rtl_hw_start_8168e_1,
+ [RTL_GIGA_MAC_VER_34] = rtl_hw_start_8168e_2,
+ [RTL_GIGA_MAC_VER_35] = rtl_hw_start_8168f_1,
+ [RTL_GIGA_MAC_VER_36] = rtl_hw_start_8168f_1,
+ [RTL_GIGA_MAC_VER_37] = rtl_hw_start_8402,
+ [RTL_GIGA_MAC_VER_38] = rtl_hw_start_8411,
+ [RTL_GIGA_MAC_VER_39] = rtl_hw_start_8106,
+ [RTL_GIGA_MAC_VER_40] = rtl_hw_start_8168g_1,
+ [RTL_GIGA_MAC_VER_42] = rtl_hw_start_8168g_2,
+ [RTL_GIGA_MAC_VER_43] = rtl_hw_start_8168g_2,
+ [RTL_GIGA_MAC_VER_44] = rtl_hw_start_8411_2,
+ [RTL_GIGA_MAC_VER_46] = rtl_hw_start_8168h_1,
+ [RTL_GIGA_MAC_VER_48] = rtl_hw_start_8168h_1,
+ [RTL_GIGA_MAC_VER_51] = rtl_hw_start_8168ep_3,
+ [RTL_GIGA_MAC_VER_52] = rtl_hw_start_8117,
+ [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117,
+ [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2,
+ [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b,
+ };
+
+ if (hw_configs[tp->mac_version])
+ hw_configs[tp->mac_version](tp);
+}
+
+static void rtl_hw_start_8125(struct rtl8169_private *tp)
+{
+ int i;
+
+ /* disable interrupt coalescing */
+ for (i = 0xa00; i < 0xb00; i += 4)
+ RTL_W32(tp, i, 0);
+
+ rtl_hw_config(tp);
+}
+
+static void rtl_hw_start_8168(struct rtl8169_private *tp)
+{
+ if (rtl_is_8168evl_up(tp))
+ RTL_W8(tp, MaxTxPacketSize, EarlySize);
+ else
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
+
+ rtl_hw_config(tp);
+
+ /* disable interrupt coalescing */
+ RTL_W16(tp, IntrMitigate, 0x0000);
+}
+
+static void rtl_hw_start_8169(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, EarlyTxThres, NoEarlyTx);
+
+ tp->cp_cmd |= PCIMulRW;
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_02 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_03)
+ tp->cp_cmd |= EnAnaPLL;
+
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
+
+ rtl8169_set_magic_reg(tp);
+
+ /* disable interrupt coalescing */
+ RTL_W16(tp, IntrMitigate, 0x0000);
+}
+
+static void rtl_hw_start(struct rtl8169_private *tp)
+{
+ rtl_unlock_config_regs(tp);
+
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
+
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
+ rtl_hw_start_8169(tp);
+ else if (rtl_is_8125(tp))
+ rtl_hw_start_8125(tp);
+ else
+ rtl_hw_start_8168(tp);
+
+ rtl_enable_exit_l1(tp);
+ rtl_set_rx_max_size(tp);
+ rtl_set_rx_tx_desc_registers(tp);
+ rtl_lock_config_regs(tp);
+
+ /* Initially a 10 us delay. Turned it into a PCI commit. - FR */
+ rtl_pci_commit(tp);
+
+ RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
+ rtl_init_rxcfg(tp);
+ rtl_set_tx_config_registers(tp);
+ rtl_set_rx_mode(tp);
+}
+
+static void rtl8169_cleanup(struct rtl8169_private *tp)
+{
+ rtl_rx_close(tp);
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_28:
+ case RTL_GIGA_MAC_VER_31:
+ rtl_loop_wait_low(tp, &rtl_npq_cond, 20, 2000);
+ break;
+ case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+ rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
+ break;
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
+ rtl_enable_rxdvgate(tp);
+ udelay(2000);
+ break;
+ default:
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+ udelay(100);
+ break;
+ }
+
+ rtl_hw_reset(tp);
+}
+
+static void rtl_read_mac_address(struct rtl8169_private *tp,
+ u8 mac_addr[ETH_ALEN])
+{
+ /* Get MAC address */
+ if (rtl_is_8168evl_up(tp) && tp->mac_version != RTL_GIGA_MAC_VER_34) {
+ u32 value;
+
+ value = rtl_eri_read(tp, 0xe0);
+ put_unaligned_le32(value, mac_addr);
+ value = rtl_eri_read(tp, 0xe4);
+ put_unaligned_le16(value, mac_addr + 4);
+ } else if (rtl_is_8125(tp)) {
+ rtl_read_mac_from_reg(tp, mac_addr, MAC0_BKP);
+ }
+}
+
+DECLARE_RTL_COND(rtl_link_list_ready_cond)
+{
+ return RTL_R8(tp, MCU) & LINK_LIST_RDY;
+}
+
+static void r8168g_wait_ll_share_fifo_ready(struct rtl8169_private *tp)
+{
+ rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42);
+}
+
+static void rtl_hw_init_8168g(struct rtl8169_private *tp)
+{
+ rtl_enable_rxdvgate(tp);
+
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb));
+ mdelay(1);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
+
+ r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0);
+ r8168g_wait_ll_share_fifo_ready(tp);
+
+ r8168_mac_ocp_modify(tp, 0xe8de, 0, BIT(15));
+ r8168g_wait_ll_share_fifo_ready(tp);
+}
+
+static void rtl_hw_init_8125(struct rtl8169_private *tp)
+{
+ rtl_enable_rxdvgate(tp);
+
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb));
+ mdelay(1);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
+
+ r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0);
+ r8168g_wait_ll_share_fifo_ready(tp);
+
+ r8168_mac_ocp_write(tp, 0xc0aa, 0x07d0);
+ r8168_mac_ocp_write(tp, 0xc0a6, 0x0150);
+ r8168_mac_ocp_write(tp, 0xc01e, 0x5555);
+ r8168g_wait_ll_share_fifo_ready(tp);
+}
+
+static void rtl_hw_initialize(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53:
+ rtl8168ep_stop_cmac(tp);
+ fallthrough;
+ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48:
+ rtl_hw_init_8168g(tp);
+ break;
+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
+ rtl_hw_init_8125(tp);
+ break;
+ default:
+ break;
+ }
+}
+
+static int rtl8169_init_dev(struct eth_device *edev)
+{
+ struct rtl8169_private *tp = edev->priv;
+ int ret;
+
+ rtl_request_firmware(tp);
+
+ pci_set_master(tp->pci_dev);
+
+ tp->phydev = get_phy_device(&tp->miibus, 0);
+ if (IS_ERR(tp->phydev))
+ return PTR_ERR(tp->phydev);
+
+ ret = phy_register_device(tp->phydev);
+ if (ret)
+ return ret;
+
+ rtl8169_init_phy(tp);
+
+ return 0;
+}
+
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define PKT_BUF_SIZE 1536
+
+static void rtl8169_init_ring(struct rtl8169_private *tp)
+{
+ struct eth_device *edev = &tp->edev;
+ int i;
+
+ tp->cur_rx = tp->cur_tx = 0;
+
+ tp->TxDescArray = dma_alloc_coherent(NUM_TX_DESC * sizeof(struct TxDesc),
+ &tp->TxPhyAddr);
+ tp->tx_buf = dma_alloc(NUM_TX_DESC * PKT_BUF_SIZE);
+ tp->tx_buf_phys = dma_map_single(edev->parent, tp->tx_buf,
+ NUM_TX_DESC * PKT_BUF_SIZE, DMA_TO_DEVICE);
+
+ tp->RxDescArray = dma_alloc_coherent(NUM_RX_DESC * sizeof(struct RxDesc),
+ &tp->RxPhyAddr);
+ tp->rx_buf = dma_alloc(NUM_RX_DESC * PKT_BUF_SIZE);
+ tp->rx_buf_phys = dma_map_single(edev->parent, tp->rx_buf,
+ NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE);
+
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (i == (NUM_RX_DESC - 1))
+ tp->RxDescArray[i].opts1 =
+ cpu_to_le32(DescOwn | RingEnd | PKT_BUF_SIZE);
+ else
+ tp->RxDescArray[i].opts1 =
+ cpu_to_le32(DescOwn | PKT_BUF_SIZE);
+
+ tp->RxDescArray[i].addr =
+ cpu_to_le64(tp->rx_buf_phys + i * PKT_BUF_SIZE);
+ }
+}
+
+static void r8169_phylink_handler(struct eth_device *edev)
+{
+ struct rtl8169_private *tp = edev->priv;
+
+ rtl_link_chg_patch(tp);
+}
+
+static int rtl8169_eth_open(struct eth_device *edev)
+{
+ struct rtl8169_private *tp = edev->priv;
+ int ret;
+
+ pci_set_master(tp->pci_dev);
+
+ rtl8169_init_ring(tp);
+ rtl_hw_start(tp);
+
+ ret = phy_device_connect(edev, &tp->miibus, 0, r8169_phylink_handler, 0,
+ PHY_INTERFACE_MODE_NA);
+
+ return ret;
+}
+
+static int rtl8169_phy_write(struct mii_bus *bus, int phyaddr, int phyreg, u16 val)
+{
+ struct rtl8169_private *tp = bus->priv;
+
+ if (phyaddr > 0)
+ return -ENODEV;
+
+ rtl_writephy(tp, phyreg, val);
+
+ return 0;
+}
+
+static int rtl8169_phy_read(struct mii_bus *bus, int phyaddr, int phyreg)
+{
+ struct rtl8169_private *tp = bus->priv;
+
+ if (phyaddr > 0)
+ return -ENODEV;
+
+ return rtl_readphy(tp, phyreg);
+}
+
+static void rtl8169_doorbell(struct rtl8169_private *tp)
+{
+ if (rtl_is_8125(tp))
+ RTL_W16(tp, TxPoll_8125, BIT(0));
+ else
+ RTL_W8(tp, TxPoll, NPQ);
+}
+
+static int rtl8169_eth_send(struct eth_device *edev, void *packet,
+ int packet_length)
+{
+ struct rtl8169_private *tp = edev->priv;
+ struct device *dev = &tp->pci_dev->dev;
+ unsigned int entry;
+ u64 start;
+ int ret = 0;
+
+ entry = tp->cur_tx % NUM_TX_DESC;
+
+ if (packet_length < ETH_ZLEN)
+ memset(tp->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN);
+ memcpy(tp->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length);
+ dma_sync_single_for_device(dev, tp->tx_buf_phys + entry *
+ PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE);
+
+ tp->TxDescArray[entry].addr = cpu_to_le64(tp->tx_buf_phys + entry * PKT_BUF_SIZE);
+
+ if (entry != (NUM_TX_DESC - 1)) {
+ tp->TxDescArray[entry].opts1 =
+ cpu_to_le32(DescOwn | FirstFrag | LastFrag |
+ ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN));
+ } else {
+ tp->TxDescArray[entry].opts1 =
+ cpu_to_le32(DescOwn | RingEnd | FirstFrag | LastFrag |
+ ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN));
+ }
+
+ rtl8169_doorbell(tp);
+
+ start = get_time_ns();
+
+ while (le32_to_cpu(tp->TxDescArray[entry].opts1) & DescOwn) {
+ if (is_timeout(start, 100 * MSECOND)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ dma_sync_single_for_cpu(dev, tp->tx_buf_phys + entry * PKT_BUF_SIZE,
+ PKT_BUF_SIZE, DMA_TO_DEVICE);
+
+ tp->cur_tx++;
+
+ return ret;
+}
+
+static int rtl8169_eth_rx(struct eth_device *edev)
+{
+ struct rtl8169_private *tp = edev->priv;
+ struct device *dev = &tp->pci_dev->dev;
+ unsigned int entry, pkt_size = 0;
+ u8 status;
+
+ entry = tp->cur_rx % NUM_RX_DESC;
+
+ if ((le32_to_cpu(tp->RxDescArray[entry].opts1) & DescOwn) == 0) {
+ if (!(le32_to_cpu(tp->RxDescArray[entry].opts1) & RxRES)) {
+ pkt_size = (le32_to_cpu(tp->RxDescArray[entry].opts1) & 0x1fff) - 4;
+
+ dma_sync_single_for_cpu(dev, tp->rx_buf_phys + entry * PKT_BUF_SIZE,
+ pkt_size, DMA_FROM_DEVICE);
+
+ net_receive(edev, tp->rx_buf + entry * PKT_BUF_SIZE,
+ pkt_size);
+
+ dma_sync_single_for_device(dev, tp->rx_buf_phys + entry * PKT_BUF_SIZE,
+ pkt_size, DMA_FROM_DEVICE);
+
+ if (entry == NUM_RX_DESC - 1)
+ tp->RxDescArray[entry].opts1 = cpu_to_le32(DescOwn |
+ RingEnd | PKT_BUF_SIZE);
+ else
+ tp->RxDescArray[entry].opts1 =
+ cpu_to_le32(DescOwn | PKT_BUF_SIZE);
+ tp->RxDescArray[entry].addr = cpu_to_le64(tp->rx_buf_phys +
+ entry * PKT_BUF_SIZE);
+ } else {
+ dev_err(&edev->dev, "rx error\n");
+ }
+
+ tp->cur_rx++;
+
+ return pkt_size;
+
+ } else {
+ status = RTL_R8(tp, IntrStatus);
+ RTL_W8(tp, IntrStatus, status & ~(TxErr | RxErr | SYSErr));
+ udelay(100); /* wait */
+ }
+
+ return 0;
+}
+
+static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *mac_addr)
+{
+ struct rtl8169_private *tp = edev->priv;
+
+ rtl_read_mac_address(tp, mac_addr);
+ if (is_valid_ether_addr(mac_addr))
+ return 0;
+
+ rtl_read_mac_address(tp, mac_addr);
+ if (is_valid_ether_addr(mac_addr))
+ return 0;
+
+ rtl_read_mac_from_reg(tp, mac_addr, MAC0);
+ if (is_valid_ether_addr(mac_addr))
+ return 0;
+
+ return 0;
+}
+
+static int rtl8169_set_ethaddr(struct eth_device *edev, const unsigned char *mac_addr)
+{
+ struct rtl8169_private *tp = edev->priv;
+
+ rtl_rar_set(tp, mac_addr);
+
+ return 0;
+}
+
+static void rtl8169_eth_halt(struct eth_device *edev)
+{
+ struct rtl8169_private *tp = edev->priv;
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8(tp, ChipCmd, 0x00);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16(tp, IntrMask, 0x0000);
+ RTL_W32(tp, RxMissed, 0);
+
+ pci_clear_master(tp->pci_dev);
+ rtl_pci_commit(tp);
+
+ rtl8169_cleanup(tp);
+ rtl_disable_exit_l1(tp);
+
+ dma_unmap_single(edev->parent, tp->tx_buf_phys, NUM_TX_DESC * PKT_BUF_SIZE,
+ DMA_TO_DEVICE);
+ free(tp->tx_buf);
+ dma_free_coherent((void *)tp->TxDescArray, tp->TxPhyAddr,
+ NUM_TX_DESC * sizeof(struct TxDesc));
+
+ dma_unmap_single(edev->parent, tp->rx_buf_phys, NUM_RX_DESC * PKT_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ free(tp->rx_buf);
+ dma_free_coherent((void *)tp->RxDescArray, tp->RxPhyAddr,
+ NUM_RX_DESC * sizeof(struct RxDesc));
+}
+
+static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct eth_device *edev;
+ struct rtl8169_private *tp;
+ int region, ret;
+ u16 xid;
+ enum mac_version chipset;
+
+ /* enable pci device */
+ pci_enable_device(pdev);
+
+ tp = xzalloc(sizeof(*tp));
+
+ edev = &tp->edev;
+ dev->type_data = edev;
+ edev->priv = tp;
+
+ tp->pci_dev = pdev;
+ tp->dev = &pdev->dev;
+
+ tp->miibus.read = rtl8169_phy_read;
+ tp->miibus.write = rtl8169_phy_write;
+ tp->miibus.priv = tp;
+ tp->miibus.parent = dev;
+
+ /* use first MMIO region */
+ region = ffs(pci_select_bars(pdev, IORESOURCE_MEM)) - 1;
+ if (region < 0) {
+ dev_err(&pdev->dev, "no MMIO resource found\n");
+ return -ENODEV;
+ }
+
+ tp->mmio_addr = pci_iomap(pdev, region);
+
+ rtl_hw_reset(tp);
+
+ xid = (RTL_R32(tp, TxConfig) >> 20) & 0xfcf;
+
+ /* Identify chip attached to board */
+ chipset = rtl8169_get_mac_version(xid, 1);
+ if (chipset == RTL_GIGA_MAC_NONE) {
+ dev_err(&pdev->dev, "unknown chip XID %03x\n", xid);
+ return -ENODEV;
+ }
+
+ dev_info(dev, "found %s (base=0x%p)\n",
+ rtl_chip_infos[chipset].name, tp->mmio_addr);
+
+ tp->mac_version = chipset;
+
+ tp->dash_type = rtl_check_dash(tp);
+
+ tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
+
+ rtl_init_rxcfg(tp);
+
+ rtl_hw_initialize(tp);
+
+ tp->fw_name = rtl_chip_infos[chipset].fw_name;
+
+ if (tp->dash_type != RTL_DASH_NONE)
+ rtl8168_driver_start(tp);
+
+ rtl_set_d3_pll_down(tp, tp->dash_type == RTL_DASH_NONE);
+
+ edev->init = rtl8169_init_dev;
+ edev->open = rtl8169_eth_open;
+ edev->send = rtl8169_eth_send;
+ edev->recv = rtl8169_eth_rx;
+ edev->get_ethaddr = rtl8169_get_ethaddr;
+ edev->set_ethaddr = rtl8169_set_ethaddr;
+ edev->halt = rtl8169_eth_halt;
+ edev->parent = dev;
+ tp->ocp_base = OCP_STD_PHY_BASE;
+
+ ret = mdiobus_register(&tp->miibus);
+ if (ret)
+ goto mdio_err;
+
+ ret = eth_register(edev);
+ if (ret)
+ goto eth_err;
+
+ return 0;
+
+mdio_err:
+ eth_unregister(edev);
+
+eth_err:
+ free(tp);
+
+ return ret;
+}
+
+static struct pci_driver rtl8169_eth_driver = {
+ .name = "rtl8169_eth",
+ .id_table = rtl8169_pci_tbl,
+ .probe = rtl8169_probe,
+};
+device_pci_driver(rtl8169_eth_driver);
diff --git a/drivers/net/r8169_phy_config.c b/drivers/net/r8169_phy_config.c
new file mode 100644
index 0000000000..c57c221e13
--- /dev/null
+++ b/drivers/net/r8169_phy_config.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * r8169_phy_config.c: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include <common.h>
+#include <linux/phy.h>
+#include <linux/mdio.h>
+
+#include "r8169.h"
+
+typedef void (*rtl_phy_cfg_fct)(struct rtl8169_private *tp,
+ struct phy_device *phydev);
+
+static void r8168d_modify_extpage(struct phy_device *phydev, int extpage,
+ int reg, u16 mask, u16 val)
+{
+ int oldpage = phy_select_page(phydev, 0x0007);
+
+ phy_write(phydev, 0x1e, extpage);
+ phy_modify(phydev, reg, mask, val);
+
+ phy_restore_page(phydev, oldpage, 0);
+}
+
+static void r8168d_phy_param(struct phy_device *phydev, u16 parm,
+ u16 mask, u16 val)
+{
+ int oldpage = phy_select_page(phydev, 0x0005);
+
+ phy_write(phydev, 0x05, parm);
+ phy_modify(phydev, 0x06, mask, val);
+
+ phy_restore_page(phydev, oldpage, 0);
+}
+
+static void r8168g_phy_param(struct phy_device *phydev, u16 parm,
+ u16 mask, u16 val)
+{
+ int oldpage = phy_select_page(phydev, 0x0a43);
+
+ phy_write(phydev, 0x13, parm);
+ phy_modify(phydev, 0x14, mask, val);
+
+ phy_restore_page(phydev, oldpage, 0);
+}
+
+struct phy_reg {
+ u16 reg;
+ u16 val;
+};
+
+static void __rtl_writephy_batch(struct phy_device *phydev,
+ const struct phy_reg *regs, int len)
+{
+ while (len-- > 0) {
+ phy_write(phydev, regs->reg, regs->val);
+ regs++;
+ }
+}
+
+#define rtl_writephy_batch(p, a) __rtl_writephy_batch(p, a, ARRAY_SIZE(a))
+
+static void rtl8168f_config_eee_phy(struct phy_device *phydev)
+{
+ r8168d_modify_extpage(phydev, 0x0020, 0x15, 0, BIT(8));
+ r8168d_phy_param(phydev, 0x8b85, 0, BIT(13));
+}
+
+static void rtl8168g_config_eee_phy(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0x0a43, 0x11, 0, BIT(4));
+}
+
+static void rtl8168h_config_eee_phy(struct phy_device *phydev)
+{
+ rtl8168g_config_eee_phy(phydev);
+
+ phy_modify_paged(phydev, 0xa4a, 0x11, 0x0000, 0x0200);
+ phy_modify_paged(phydev, 0xa42, 0x14, 0x0000, 0x0080);
+}
+
+static void rtl8125a_config_eee_phy(struct phy_device *phydev)
+{
+ rtl8168h_config_eee_phy(phydev);
+
+ phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000);
+ phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000);
+}
+
+static void rtl8125b_config_eee_phy(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000);
+ phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000);
+ phy_modify_paged(phydev, 0xa42, 0x14, 0x0080, 0x0000);
+ phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000);
+}
+
+static void rtl8169s_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0001 },
+ { 0x06, 0x006e },
+ { 0x08, 0x0708 },
+ { 0x15, 0x4000 },
+ { 0x18, 0x65c7 },
+
+ { 0x1f, 0x0001 },
+ { 0x03, 0x00a1 },
+ { 0x02, 0x0008 },
+ { 0x01, 0x0120 },
+ { 0x00, 0x1000 },
+ { 0x04, 0x0800 },
+ { 0x04, 0x0000 },
+
+ { 0x03, 0xff41 },
+ { 0x02, 0xdf60 },
+ { 0x01, 0x0140 },
+ { 0x00, 0x0077 },
+ { 0x04, 0x7800 },
+ { 0x04, 0x7000 },
+
+ { 0x03, 0x802f },
+ { 0x02, 0x4f02 },
+ { 0x01, 0x0409 },
+ { 0x00, 0xf0f9 },
+ { 0x04, 0x9800 },
+ { 0x04, 0x9000 },
+
+ { 0x03, 0xdf01 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0xff95 },
+ { 0x00, 0xba00 },
+ { 0x04, 0xa800 },
+ { 0x04, 0xa000 },
+
+ { 0x03, 0xff41 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0x0140 },
+ { 0x00, 0x00bb },
+ { 0x04, 0xb800 },
+ { 0x04, 0xb000 },
+
+ { 0x03, 0xdf41 },
+ { 0x02, 0xdc60 },
+ { 0x01, 0x6340 },
+ { 0x00, 0x007d },
+ { 0x04, 0xd800 },
+ { 0x04, 0xd000 },
+
+ { 0x03, 0xdf01 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0x100a },
+ { 0x00, 0xa0ff },
+ { 0x04, 0xf800 },
+ { 0x04, 0xf000 },
+
+ { 0x1f, 0x0000 },
+ { 0x0b, 0x0000 },
+ { 0x00, 0x9200 }
+ };
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+}
+
+static void rtl8169sb_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_write_paged(phydev, 0x0002, 0x01, 0x90d0);
+}
+
+static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0001 },
+ { 0x04, 0x0000 },
+ { 0x03, 0x00a1 },
+ { 0x02, 0x0008 },
+ { 0x01, 0x0120 },
+ { 0x00, 0x1000 },
+ { 0x04, 0x0800 },
+ { 0x04, 0x9000 },
+ { 0x03, 0x802f },
+ { 0x02, 0x4f02 },
+ { 0x01, 0x0409 },
+ { 0x00, 0xf099 },
+ { 0x04, 0x9800 },
+ { 0x04, 0xa000 },
+ { 0x03, 0xdf01 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0xff95 },
+ { 0x00, 0xba00 },
+ { 0x04, 0xa800 },
+ { 0x04, 0xf000 },
+ { 0x03, 0xdf01 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0x101a },
+ { 0x00, 0xa0ff },
+ { 0x04, 0xf800 },
+ { 0x04, 0x0000 },
+ { 0x1f, 0x0000 },
+
+ { 0x1f, 0x0001 },
+ { 0x10, 0xf41b },
+ { 0x14, 0xfb54 },
+ { 0x18, 0xf5c7 },
+ { 0x1f, 0x0000 },
+
+ { 0x1f, 0x0001 },
+ { 0x17, 0x0cc0 },
+ { 0x1f, 0x0000 }
+ };
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+}
+
+static void rtl8169sce_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0001 },
+ { 0x04, 0x0000 },
+ { 0x03, 0x00a1 },
+ { 0x02, 0x0008 },
+ { 0x01, 0x0120 },
+ { 0x00, 0x1000 },
+ { 0x04, 0x0800 },
+ { 0x04, 0x9000 },
+ { 0x03, 0x802f },
+ { 0x02, 0x4f02 },
+ { 0x01, 0x0409 },
+ { 0x00, 0xf099 },
+ { 0x04, 0x9800 },
+ { 0x04, 0xa000 },
+ { 0x03, 0xdf01 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0xff95 },
+ { 0x00, 0xba00 },
+ { 0x04, 0xa800 },
+ { 0x04, 0xf000 },
+ { 0x03, 0xdf01 },
+ { 0x02, 0xdf20 },
+ { 0x01, 0x101a },
+ { 0x00, 0xa0ff },
+ { 0x04, 0xf800 },
+ { 0x04, 0x0000 },
+ { 0x1f, 0x0000 },
+
+ { 0x1f, 0x0001 },
+ { 0x0b, 0x8480 },
+ { 0x1f, 0x0000 },
+
+ { 0x1f, 0x0001 },
+ { 0x18, 0x67c7 },
+ { 0x04, 0x2000 },
+ { 0x03, 0x002f },
+ { 0x02, 0x4360 },
+ { 0x01, 0x0109 },
+ { 0x00, 0x3022 },
+ { 0x04, 0x2800 },
+ { 0x1f, 0x0000 },
+
+ { 0x1f, 0x0001 },
+ { 0x17, 0x0cc0 },
+ { 0x1f, 0x0000 }
+ };
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+}
+
+static void rtl8168bb_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_write(phydev, 0x1f, 0x0001);
+ phy_set_bits(phydev, 0x16, BIT(0));
+ phy_write(phydev, 0x10, 0xf41b);
+ phy_write(phydev, 0x1f, 0x0000);
+}
+
+static void rtl8168bef_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_write_paged(phydev, 0x0001, 0x10, 0xf41b);
+}
+
+static void rtl8168cp_1_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_write(phydev, 0x1d, 0x0f00);
+ phy_write_paged(phydev, 0x0002, 0x0c, 0x1ec8);
+}
+
+static void rtl8168cp_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_set_bits(phydev, 0x14, BIT(5));
+ phy_set_bits(phydev, 0x0d, BIT(5));
+ phy_write_paged(phydev, 0x0001, 0x1d, 0x3d98);
+}
+
+static void rtl8168c_1_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0001 },
+ { 0x12, 0x2300 },
+ { 0x1f, 0x0002 },
+ { 0x00, 0x88d4 },
+ { 0x01, 0x82b1 },
+ { 0x03, 0x7002 },
+ { 0x08, 0x9e30 },
+ { 0x09, 0x01f0 },
+ { 0x0a, 0x5500 },
+ { 0x0c, 0x00c8 },
+ { 0x1f, 0x0003 },
+ { 0x12, 0xc096 },
+ { 0x16, 0x000a },
+ { 0x1f, 0x0000 },
+ { 0x1f, 0x0000 },
+ { 0x09, 0x2000 },
+ { 0x09, 0x0000 }
+ };
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+
+ phy_set_bits(phydev, 0x14, BIT(5));
+ phy_set_bits(phydev, 0x0d, BIT(5));
+}
+
+static void rtl8168c_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0001 },
+ { 0x12, 0x2300 },
+ { 0x03, 0x802f },
+ { 0x02, 0x4f02 },
+ { 0x01, 0x0409 },
+ { 0x00, 0xf099 },
+ { 0x04, 0x9800 },
+ { 0x04, 0x9000 },
+ { 0x1d, 0x3d98 },
+ { 0x1f, 0x0002 },
+ { 0x0c, 0x7eb8 },
+ { 0x06, 0x0761 },
+ { 0x1f, 0x0003 },
+ { 0x16, 0x0f0a },
+ { 0x1f, 0x0000 }
+ };
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+
+ phy_set_bits(phydev, 0x16, BIT(0));
+ phy_set_bits(phydev, 0x14, BIT(5));
+ phy_set_bits(phydev, 0x0d, BIT(5));
+}
+
+static void rtl8168c_3_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0001 },
+ { 0x12, 0x2300 },
+ { 0x1d, 0x3d98 },
+ { 0x1f, 0x0002 },
+ { 0x0c, 0x7eb8 },
+ { 0x06, 0x5461 },
+ { 0x1f, 0x0003 },
+ { 0x16, 0x0f0a },
+ { 0x1f, 0x0000 }
+ };
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+
+ phy_set_bits(phydev, 0x16, BIT(0));
+ phy_set_bits(phydev, 0x14, BIT(5));
+ phy_set_bits(phydev, 0x0d, BIT(5));
+}
+
+static const struct phy_reg rtl8168d_1_phy_reg_init_0[] = {
+ /* Channel Estimation */
+ { 0x1f, 0x0001 },
+ { 0x06, 0x4064 },
+ { 0x07, 0x2863 },
+ { 0x08, 0x059c },
+ { 0x09, 0x26b4 },
+ { 0x0a, 0x6a19 },
+ { 0x0b, 0xdcc8 },
+ { 0x10, 0xf06d },
+ { 0x14, 0x7f68 },
+ { 0x18, 0x7fd9 },
+ { 0x1c, 0xf0ff },
+ { 0x1d, 0x3d9c },
+ { 0x1f, 0x0003 },
+ { 0x12, 0xf49f },
+ { 0x13, 0x070b },
+ { 0x1a, 0x05ad },
+ { 0x14, 0x94c0 },
+
+ /*
+ * Tx Error Issue
+ * Enhance line driver power
+ */
+ { 0x1f, 0x0002 },
+ { 0x06, 0x5561 },
+ { 0x1f, 0x0005 },
+ { 0x05, 0x8332 },
+ { 0x06, 0x5561 },
+
+ /*
+ * Can not link to 1Gbps with bad cable
+ * Decrease SNR threshold form 21.07dB to 19.04dB
+ */
+ { 0x1f, 0x0001 },
+ { 0x17, 0x0cc0 },
+
+ { 0x1f, 0x0000 },
+ { 0x0d, 0xf880 }
+};
+
+static void rtl8168d_apply_firmware_cond(struct rtl8169_private *tp,
+ struct phy_device *phydev,
+ u16 val)
+{
+ u16 reg_val;
+
+ phy_write(phydev, 0x1f, 0x0005);
+ phy_write(phydev, 0x05, 0x001b);
+ reg_val = phy_read(phydev, 0x06);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ if (reg_val != val)
+ dev_warn(&phydev->dev, "chipset not ready for firmware\n");
+ else
+ r8169_apply_firmware(tp);
+}
+
+static void rtl8168d_1_common(struct phy_device *phydev)
+{
+ u16 val;
+
+ phy_write_paged(phydev, 0x0002, 0x05, 0x669a);
+ r8168d_phy_param(phydev, 0x8330, 0xffff, 0x669a);
+ phy_write(phydev, 0x1f, 0x0002);
+
+ val = phy_read(phydev, 0x0d);
+
+ if ((val & 0x00ff) != 0x006c) {
+ static const u16 set[] = {
+ 0x0065, 0x0066, 0x0067, 0x0068,
+ 0x0069, 0x006a, 0x006b, 0x006c
+ };
+ int i;
+
+ val &= 0xff00;
+ for (i = 0; i < ARRAY_SIZE(set); i++)
+ phy_write(phydev, 0x0d, val | set[i]);
+ }
+}
+
+static void rtl8168d_1_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0);
+
+ /*
+ * Rx Error Issue
+ * Fine Tune Switching regulator parameter
+ */
+ phy_write(phydev, 0x1f, 0x0002);
+ phy_modify(phydev, 0x0b, 0x00ef, 0x0010);
+ phy_modify(phydev, 0x0c, 0x5d00, 0xa200);
+
+ if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) {
+ rtl8168d_1_common(phydev);
+ } else {
+ phy_write_paged(phydev, 0x0002, 0x05, 0x6662);
+ r8168d_phy_param(phydev, 0x8330, 0xffff, 0x6662);
+ }
+
+ /* RSET couple improve */
+ phy_write(phydev, 0x1f, 0x0002);
+ phy_set_bits(phydev, 0x0d, 0x0300);
+ phy_set_bits(phydev, 0x0f, 0x0010);
+
+ /* Fine tune PLL performance */
+ phy_write(phydev, 0x1f, 0x0002);
+ phy_modify(phydev, 0x02, 0x0600, 0x0100);
+ phy_clear_bits(phydev, 0x03, 0xe000);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ rtl8168d_apply_firmware_cond(tp, phydev, 0xbf00);
+}
+
+static void rtl8168d_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0);
+
+ if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) {
+ rtl8168d_1_common(phydev);
+ } else {
+ phy_write_paged(phydev, 0x0002, 0x05, 0x2642);
+ r8168d_phy_param(phydev, 0x8330, 0xffff, 0x2642);
+ }
+
+ /* Fine tune PLL performance */
+ phy_write(phydev, 0x1f, 0x0002);
+ phy_modify(phydev, 0x02, 0x0600, 0x0100);
+ phy_clear_bits(phydev, 0x03, 0xe000);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ /* Switching regulator Slew rate */
+ phy_modify_paged(phydev, 0x0002, 0x0f, 0x0000, 0x0017);
+
+ rtl8168d_apply_firmware_cond(tp, phydev, 0xb300);
+}
+
+static void rtl8168d_4_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_write_paged(phydev, 0x0001, 0x17, 0x0cc0);
+ r8168d_modify_extpage(phydev, 0x002d, 0x18, 0xffff, 0x0040);
+ phy_set_bits(phydev, 0x0d, BIT(5));
+}
+
+static void rtl8168e_1_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ /* Channel estimation fine tune */
+ { 0x1f, 0x0001 },
+ { 0x0b, 0x6c20 },
+ { 0x07, 0x2872 },
+ { 0x1c, 0xefff },
+ { 0x1f, 0x0003 },
+ { 0x14, 0x6420 },
+ { 0x1f, 0x0000 },
+ };
+
+ r8169_apply_firmware(tp);
+
+ /* Enable Delay cap */
+ r8168d_phy_param(phydev, 0x8b80, 0xffff, 0xc896);
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+
+ /* Update PFM & 10M TX idle timer */
+ r8168d_modify_extpage(phydev, 0x002f, 0x15, 0xffff, 0x1919);
+
+ r8168d_modify_extpage(phydev, 0x00ac, 0x18, 0xffff, 0x0006);
+
+ /* DCO enable for 10M IDLE Power */
+ r8168d_modify_extpage(phydev, 0x0023, 0x17, 0x0000, 0x0006);
+
+ /* For impedance matching */
+ phy_modify_paged(phydev, 0x0002, 0x08, 0x7f00, 0x8000);
+
+ /* PHY auto speed down */
+ r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0050);
+ phy_set_bits(phydev, 0x14, BIT(15));
+
+ r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001);
+ r8168d_phy_param(phydev, 0x8b85, 0x2000, 0x0000);
+
+ r8168d_modify_extpage(phydev, 0x0020, 0x15, 0x1100, 0x0000);
+ phy_write_paged(phydev, 0x0006, 0x00, 0x5a00);
+
+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0000);
+}
+
+static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+
+ /* Enable Delay cap */
+ r8168d_modify_extpage(phydev, 0x00ac, 0x18, 0xffff, 0x0006);
+
+ /* Channel estimation fine tune */
+ phy_write_paged(phydev, 0x0003, 0x09, 0xa20f);
+
+ /* Green Setting */
+ r8168d_phy_param(phydev, 0x8b5b, 0xffff, 0x9222);
+ r8168d_phy_param(phydev, 0x8b6d, 0xffff, 0x8000);
+ r8168d_phy_param(phydev, 0x8b76, 0xffff, 0x8000);
+
+ /* For 4-corner performance improve */
+ phy_write(phydev, 0x1f, 0x0005);
+ phy_write(phydev, 0x05, 0x8b80);
+ phy_set_bits(phydev, 0x17, 0x0006);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ /* PHY auto speed down */
+ r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0010);
+ phy_set_bits(phydev, 0x14, BIT(15));
+
+ /* improve 10M EEE waveform */
+ r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001);
+
+ /* Improve 2-pair detection performance */
+ r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000);
+
+ rtl8168f_config_eee_phy(phydev);
+
+ /* Green feature */
+ phy_write(phydev, 0x1f, 0x0003);
+ phy_set_bits(phydev, 0x19, BIT(0));
+ phy_set_bits(phydev, 0x10, BIT(10));
+ phy_write(phydev, 0x1f, 0x0000);
+ phy_modify_paged(phydev, 0x0005, 0x01, 0, BIT(8));
+}
+
+static void rtl8168f_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ /* For 4-corner performance improve */
+ r8168d_phy_param(phydev, 0x8b80, 0x0000, 0x0006);
+
+ /* PHY auto speed down */
+ r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0010);
+ phy_set_bits(phydev, 0x14, BIT(15));
+
+ /* Improve 10M EEE waveform */
+ r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001);
+
+ rtl8168f_config_eee_phy(phydev);
+}
+
+static void rtl8168f_1_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+
+ /* Channel estimation fine tune */
+ phy_write_paged(phydev, 0x0003, 0x09, 0xa20f);
+
+ /* Modify green table for giga & fnet */
+ r8168d_phy_param(phydev, 0x8b55, 0xffff, 0x0000);
+ r8168d_phy_param(phydev, 0x8b5e, 0xffff, 0x0000);
+ r8168d_phy_param(phydev, 0x8b67, 0xffff, 0x0000);
+ r8168d_phy_param(phydev, 0x8b70, 0xffff, 0x0000);
+ r8168d_modify_extpage(phydev, 0x0078, 0x17, 0xffff, 0x0000);
+ r8168d_modify_extpage(phydev, 0x0078, 0x19, 0xffff, 0x00fb);
+
+ /* Modify green table for 10M */
+ r8168d_phy_param(phydev, 0x8b79, 0xffff, 0xaa00);
+
+ /* Disable hiimpedance detection (RTCT) */
+ phy_write_paged(phydev, 0x0003, 0x01, 0x328a);
+
+ rtl8168f_hw_phy_config(tp, phydev);
+
+ /* Improve 2-pair detection performance */
+ r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000);
+}
+
+static void rtl8168f_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+
+ rtl8168f_hw_phy_config(tp, phydev);
+}
+
+static void rtl8411_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+
+ rtl8168f_hw_phy_config(tp, phydev);
+
+ /* Improve 2-pair detection performance */
+ r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000);
+
+ /* Channel estimation fine tune */
+ phy_write_paged(phydev, 0x0003, 0x09, 0xa20f);
+
+ /* Modify green table for giga & fnet */
+ r8168d_phy_param(phydev, 0x8b55, 0xffff, 0x0000);
+ r8168d_phy_param(phydev, 0x8b5e, 0xffff, 0x0000);
+ r8168d_phy_param(phydev, 0x8b67, 0xffff, 0x0000);
+ r8168d_phy_param(phydev, 0x8b70, 0xffff, 0x0000);
+ r8168d_modify_extpage(phydev, 0x0078, 0x17, 0xffff, 0x0000);
+ r8168d_modify_extpage(phydev, 0x0078, 0x19, 0xffff, 0x00aa);
+
+ /* Modify green table for 10M */
+ r8168d_phy_param(phydev, 0x8b79, 0xffff, 0xaa00);
+
+ /* Disable hiimpedance detection (RTCT) */
+ phy_write_paged(phydev, 0x0003, 0x01, 0x328a);
+
+ /* Modify green table for giga */
+ r8168d_phy_param(phydev, 0x8b54, 0x0800, 0x0000);
+ r8168d_phy_param(phydev, 0x8b5d, 0x0800, 0x0000);
+ r8168d_phy_param(phydev, 0x8a7c, 0x0100, 0x0000);
+ r8168d_phy_param(phydev, 0x8a7f, 0x0000, 0x0100);
+ r8168d_phy_param(phydev, 0x8a82, 0x0100, 0x0000);
+ r8168d_phy_param(phydev, 0x8a85, 0x0100, 0x0000);
+ r8168d_phy_param(phydev, 0x8a88, 0x0100, 0x0000);
+
+ /* uc same-seed solution */
+ r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x8000);
+
+ /* Green feature */
+ phy_write(phydev, 0x1f, 0x0003);
+ phy_clear_bits(phydev, 0x19, BIT(0));
+ phy_clear_bits(phydev, 0x10, BIT(10));
+ phy_write(phydev, 0x1f, 0x0000);
+}
+
+static void rtl8168g_disable_aldps(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0x0a43, 0x10, BIT(2), 0);
+}
+
+static void rtl8168g_enable_gphy_10m(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(11));
+}
+
+static void rtl8168g_phy_adjust_10m_aldps(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0x0bcc, 0x14, BIT(8), 0);
+ phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(7) | BIT(6));
+ r8168g_phy_param(phydev, 0x8084, 0x6000, 0x0000);
+ phy_modify_paged(phydev, 0x0a43, 0x10, 0x0000, 0x1003);
+}
+
+static void rtl8168g_1_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ int ret;
+
+ r8169_apply_firmware(tp);
+
+ ret = phy_read_paged(phydev, 0x0a46, 0x10);
+ if (ret & BIT(8))
+ phy_modify_paged(phydev, 0x0bcc, 0x12, BIT(15), 0);
+ else
+ phy_modify_paged(phydev, 0x0bcc, 0x12, 0, BIT(15));
+
+ ret = phy_read_paged(phydev, 0x0a46, 0x13);
+ if (ret & BIT(8))
+ phy_modify_paged(phydev, 0x0c41, 0x15, 0, BIT(1));
+ else
+ phy_modify_paged(phydev, 0x0c41, 0x15, BIT(1), 0);
+
+ /* Enable PHY auto speed down */
+ phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(3) | BIT(2));
+
+ rtl8168g_phy_adjust_10m_aldps(phydev);
+
+ /* EEE auto-fallback function */
+ phy_modify_paged(phydev, 0x0a4b, 0x11, 0, BIT(2));
+
+ /* Enable UC LPF tune function */
+ r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000);
+
+ phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14));
+
+ /* Improve SWR Efficiency */
+ phy_write(phydev, 0x1f, 0x0bcd);
+ phy_write(phydev, 0x14, 0x5065);
+ phy_write(phydev, 0x14, 0xd065);
+ phy_write(phydev, 0x1f, 0x0bc8);
+ phy_write(phydev, 0x11, 0x5655);
+ phy_write(phydev, 0x1f, 0x0bcd);
+ phy_write(phydev, 0x14, 0x1065);
+ phy_write(phydev, 0x14, 0x9065);
+ phy_write(phydev, 0x14, 0x1065);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ rtl8168g_disable_aldps(phydev);
+ rtl8168g_config_eee_phy(phydev);
+}
+
+static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+ rtl8168g_config_eee_phy(phydev);
+}
+
+static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ u16 ioffset, rlen;
+ u32 data;
+
+ r8169_apply_firmware(tp);
+
+ /* CHIN EST parameter update */
+ r8168g_phy_param(phydev, 0x808a, 0x003f, 0x000a);
+
+ /* enable R-tune & PGA-retune function */
+ r8168g_phy_param(phydev, 0x0811, 0x0000, 0x0800);
+ phy_modify_paged(phydev, 0x0a42, 0x16, 0x0000, 0x0002);
+
+ rtl8168g_enable_gphy_10m(phydev);
+
+ ioffset = rtl8168h_2_get_adc_bias_ioffset(tp);
+ if (ioffset != 0xffff)
+ phy_write_paged(phydev, 0x0bcf, 0x16, ioffset);
+
+ /* Modify rlen (TX LPF corner frequency) level */
+ data = phy_read_paged(phydev, 0x0bcd, 0x16);
+ data &= 0x000f;
+ rlen = 0;
+ if (data > 3)
+ rlen = data - 3;
+ data = rlen | (rlen << 4) | (rlen << 8) | (rlen << 12);
+ phy_write_paged(phydev, 0x0bcd, 0x17, data);
+
+ /* disable phy pfm mode */
+ phy_modify_paged(phydev, 0x0a44, 0x11, BIT(7), 0);
+
+ /* disable 10m pll off */
+ phy_modify_paged(phydev, 0x0a43, 0x10, BIT(0), 0);
+
+ rtl8168g_disable_aldps(phydev);
+ rtl8168g_config_eee_phy(phydev);
+}
+
+static void rtl8168ep_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ rtl8168g_phy_adjust_10m_aldps(phydev);
+
+ /* Enable UC LPF tune function */
+ r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000);
+
+ /* Set rg_sel_sdm_rate */
+ phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14));
+
+ /* Channel estimation parameters */
+ r8168g_phy_param(phydev, 0x80f3, 0xff00, 0x8b00);
+ r8168g_phy_param(phydev, 0x80f0, 0xff00, 0x3a00);
+ r8168g_phy_param(phydev, 0x80ef, 0xff00, 0x0500);
+ r8168g_phy_param(phydev, 0x80f6, 0xff00, 0x6e00);
+ r8168g_phy_param(phydev, 0x80ec, 0xff00, 0x6800);
+ r8168g_phy_param(phydev, 0x80ed, 0xff00, 0x7c00);
+ r8168g_phy_param(phydev, 0x80f2, 0xff00, 0xf400);
+ r8168g_phy_param(phydev, 0x80f4, 0xff00, 0x8500);
+ r8168g_phy_param(phydev, 0x8110, 0xff00, 0xa800);
+ r8168g_phy_param(phydev, 0x810f, 0xff00, 0x1d00);
+ r8168g_phy_param(phydev, 0x8111, 0xff00, 0xf500);
+ r8168g_phy_param(phydev, 0x8113, 0xff00, 0x6100);
+ r8168g_phy_param(phydev, 0x8115, 0xff00, 0x9200);
+ r8168g_phy_param(phydev, 0x810e, 0xff00, 0x0400);
+ r8168g_phy_param(phydev, 0x810c, 0xff00, 0x7c00);
+ r8168g_phy_param(phydev, 0x810b, 0xff00, 0x5a00);
+ r8168g_phy_param(phydev, 0x80d1, 0xff00, 0xff00);
+ r8168g_phy_param(phydev, 0x80cd, 0xff00, 0x9e00);
+ r8168g_phy_param(phydev, 0x80d3, 0xff00, 0x0e00);
+ r8168g_phy_param(phydev, 0x80d5, 0xff00, 0xca00);
+ r8168g_phy_param(phydev, 0x80d7, 0xff00, 0x8400);
+
+ /* Force PWM-mode */
+ phy_write(phydev, 0x1f, 0x0bcd);
+ phy_write(phydev, 0x14, 0x5065);
+ phy_write(phydev, 0x14, 0xd065);
+ phy_write(phydev, 0x1f, 0x0bc8);
+ phy_write(phydev, 0x12, 0x00ed);
+ phy_write(phydev, 0x1f, 0x0bcd);
+ phy_write(phydev, 0x14, 0x1065);
+ phy_write(phydev, 0x14, 0x9065);
+ phy_write(phydev, 0x14, 0x1065);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ rtl8168g_disable_aldps(phydev);
+ rtl8168g_config_eee_phy(phydev);
+}
+
+static void rtl8117_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ /* CHN EST parameters adjust - fnet */
+ r8168g_phy_param(phydev, 0x808e, 0xff00, 0x4800);
+ r8168g_phy_param(phydev, 0x8090, 0xff00, 0xcc00);
+ r8168g_phy_param(phydev, 0x8092, 0xff00, 0xb000);
+
+ r8168g_phy_param(phydev, 0x8088, 0xff00, 0x6000);
+ r8168g_phy_param(phydev, 0x808b, 0x3f00, 0x0b00);
+ r8168g_phy_param(phydev, 0x808d, 0x1f00, 0x0600);
+ r8168g_phy_param(phydev, 0x808c, 0xff00, 0xb000);
+ r8168g_phy_param(phydev, 0x80a0, 0xff00, 0x2800);
+ r8168g_phy_param(phydev, 0x80a2, 0xff00, 0x5000);
+ r8168g_phy_param(phydev, 0x809b, 0xf800, 0xb000);
+ r8168g_phy_param(phydev, 0x809a, 0xff00, 0x4b00);
+ r8168g_phy_param(phydev, 0x809d, 0x3f00, 0x0800);
+ r8168g_phy_param(phydev, 0x80a1, 0xff00, 0x7000);
+ r8168g_phy_param(phydev, 0x809f, 0x1f00, 0x0300);
+ r8168g_phy_param(phydev, 0x809e, 0xff00, 0x8800);
+ r8168g_phy_param(phydev, 0x80b2, 0xff00, 0x2200);
+ r8168g_phy_param(phydev, 0x80ad, 0xf800, 0x9800);
+ r8168g_phy_param(phydev, 0x80af, 0x3f00, 0x0800);
+ r8168g_phy_param(phydev, 0x80b3, 0xff00, 0x6f00);
+ r8168g_phy_param(phydev, 0x80b1, 0x1f00, 0x0300);
+ r8168g_phy_param(phydev, 0x80b0, 0xff00, 0x9300);
+
+ r8168g_phy_param(phydev, 0x8011, 0x0000, 0x0800);
+
+ rtl8168g_enable_gphy_10m(phydev);
+
+ r8168g_phy_param(phydev, 0x8016, 0x0000, 0x0400);
+
+ rtl8168g_disable_aldps(phydev);
+ rtl8168h_config_eee_phy(phydev);
+}
+
+static void rtl8102e_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0003 },
+ { 0x08, 0x441d },
+ { 0x01, 0x9100 },
+ { 0x1f, 0x0000 }
+ };
+
+ phy_set_bits(phydev, 0x11, BIT(12));
+ phy_set_bits(phydev, 0x19, BIT(13));
+ phy_set_bits(phydev, 0x10, BIT(15));
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+}
+
+static void rtl8401_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_set_bits(phydev, 0x11, BIT(12));
+ phy_modify_paged(phydev, 0x0002, 0x0f, 0x0000, 0x0003);
+}
+
+static void rtl8105e_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ /* Disable ALDPS before ram code */
+ phy_write(phydev, 0x18, 0x0310);
+ mdelay(100);
+
+ r8169_apply_firmware(tp);
+
+ phy_write_paged(phydev, 0x0005, 0x1a, 0x0000);
+ phy_write_paged(phydev, 0x0004, 0x1c, 0x0000);
+ phy_write_paged(phydev, 0x0001, 0x15, 0x7701);
+}
+
+static void rtl8402_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ /* Disable ALDPS before setting firmware */
+ phy_write(phydev, 0x18, 0x0310);
+ mdelay(20);
+
+ r8169_apply_firmware(tp);
+
+ /* EEE setting */
+ phy_write(phydev, 0x1f, 0x0004);
+ phy_write(phydev, 0x10, 0x401f);
+ phy_write(phydev, 0x19, 0x7030);
+ phy_write(phydev, 0x1f, 0x0000);
+}
+
+static void rtl8106e_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ static const struct phy_reg phy_reg_init[] = {
+ { 0x1f, 0x0004 },
+ { 0x10, 0xc07f },
+ { 0x19, 0x7030 },
+ { 0x1f, 0x0000 }
+ };
+
+ /* Disable ALDPS before ram code */
+ phy_write(phydev, 0x18, 0x0310);
+ mdelay(100);
+
+ r8169_apply_firmware(tp);
+
+ rtl_writephy_batch(phydev, phy_reg_init);
+}
+
+static void rtl8125_legacy_force_mode(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0xa5b, 0x12, BIT(15), 0);
+}
+
+static void rtl8125a_2_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ int i;
+
+ phy_modify_paged(phydev, 0xad4, 0x17, 0x0000, 0x0010);
+ phy_modify_paged(phydev, 0xad1, 0x13, 0x03ff, 0x03ff);
+ phy_modify_paged(phydev, 0xad3, 0x11, 0x003f, 0x0006);
+ phy_modify_paged(phydev, 0xac0, 0x14, 0x1100, 0x0000);
+ phy_modify_paged(phydev, 0xacc, 0x10, 0x0003, 0x0002);
+ phy_modify_paged(phydev, 0xad4, 0x10, 0x00e7, 0x0044);
+ phy_modify_paged(phydev, 0xac1, 0x12, 0x0080, 0x0000);
+ phy_modify_paged(phydev, 0xac8, 0x10, 0x0300, 0x0000);
+ phy_modify_paged(phydev, 0xac5, 0x17, 0x0007, 0x0002);
+ phy_write_paged(phydev, 0xad4, 0x16, 0x00a8);
+ phy_write_paged(phydev, 0xac5, 0x16, 0x01ff);
+ phy_modify_paged(phydev, 0xac8, 0x15, 0x00f0, 0x0030);
+
+ phy_write(phydev, 0x1f, 0x0b87);
+ phy_write(phydev, 0x16, 0x80a2);
+ phy_write(phydev, 0x17, 0x0153);
+ phy_write(phydev, 0x16, 0x809c);
+ phy_write(phydev, 0x17, 0x0153);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ phy_write(phydev, 0x1f, 0x0a43);
+ phy_write(phydev, 0x13, 0x81B3);
+ phy_write(phydev, 0x14, 0x0043);
+ phy_write(phydev, 0x14, 0x00A7);
+ phy_write(phydev, 0x14, 0x00D6);
+ phy_write(phydev, 0x14, 0x00EC);
+ phy_write(phydev, 0x14, 0x00F6);
+ phy_write(phydev, 0x14, 0x00FB);
+ phy_write(phydev, 0x14, 0x00FD);
+ phy_write(phydev, 0x14, 0x00FF);
+ phy_write(phydev, 0x14, 0x00BB);
+ phy_write(phydev, 0x14, 0x0058);
+ phy_write(phydev, 0x14, 0x0029);
+ phy_write(phydev, 0x14, 0x0013);
+ phy_write(phydev, 0x14, 0x0009);
+ phy_write(phydev, 0x14, 0x0004);
+ phy_write(phydev, 0x14, 0x0002);
+ for (i = 0; i < 25; i++)
+ phy_write(phydev, 0x14, 0x0000);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ r8168g_phy_param(phydev, 0x8257, 0xffff, 0x020F);
+ r8168g_phy_param(phydev, 0x80ea, 0xffff, 0x7843);
+
+ r8169_apply_firmware(tp);
+
+ phy_modify_paged(phydev, 0xd06, 0x14, 0x0000, 0x2000);
+
+ r8168g_phy_param(phydev, 0x81a2, 0x0000, 0x0100);
+
+ phy_modify_paged(phydev, 0xb54, 0x16, 0xff00, 0xdb00);
+ phy_modify_paged(phydev, 0xa45, 0x12, 0x0001, 0x0000);
+ phy_modify_paged(phydev, 0xa5d, 0x12, 0x0000, 0x0020);
+ phy_modify_paged(phydev, 0xad4, 0x17, 0x0010, 0x0000);
+ phy_modify_paged(phydev, 0xa86, 0x15, 0x0001, 0x0000);
+ rtl8168g_enable_gphy_10m(phydev);
+
+ rtl8125a_config_eee_phy(phydev);
+}
+
+static void rtl8125b_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+
+ phy_modify_paged(phydev, 0xa44, 0x11, 0x0000, 0x0800);
+ phy_modify_paged(phydev, 0xac4, 0x13, 0x00f0, 0x0090);
+ phy_modify_paged(phydev, 0xad3, 0x10, 0x0003, 0x0001);
+
+ phy_write(phydev, 0x1f, 0x0b87);
+ phy_write(phydev, 0x16, 0x80f5);
+ phy_write(phydev, 0x17, 0x760e);
+ phy_write(phydev, 0x16, 0x8107);
+ phy_write(phydev, 0x17, 0x360e);
+ phy_write(phydev, 0x16, 0x8551);
+ phy_modify(phydev, 0x17, 0xff00, 0x0800);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ phy_modify_paged(phydev, 0xbf0, 0x10, 0xe000, 0xa000);
+ phy_modify_paged(phydev, 0xbf4, 0x13, 0x0f00, 0x0300);
+
+ r8168g_phy_param(phydev, 0x8044, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x804a, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x8050, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x8056, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x805c, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x8062, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x8068, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x806e, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x8074, 0xffff, 0x2417);
+ r8168g_phy_param(phydev, 0x807a, 0xffff, 0x2417);
+
+ phy_modify_paged(phydev, 0xa4c, 0x15, 0x0000, 0x0040);
+ phy_modify_paged(phydev, 0xbf8, 0x12, 0xe000, 0xa000);
+
+ rtl8125_legacy_force_mode(phydev);
+ rtl8125b_config_eee_phy(phydev);
+}
+
+void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
+ enum mac_version ver)
+{
+ static const rtl_phy_cfg_fct phy_configs[] = {
+ /* PCI devices. */
+ [RTL_GIGA_MAC_VER_02] = rtl8169s_hw_phy_config,
+ [RTL_GIGA_MAC_VER_03] = rtl8169s_hw_phy_config,
+ [RTL_GIGA_MAC_VER_04] = rtl8169sb_hw_phy_config,
+ [RTL_GIGA_MAC_VER_05] = rtl8169scd_hw_phy_config,
+ [RTL_GIGA_MAC_VER_06] = rtl8169sce_hw_phy_config,
+ /* PCI-E devices. */
+ [RTL_GIGA_MAC_VER_07] = rtl8102e_hw_phy_config,
+ [RTL_GIGA_MAC_VER_08] = rtl8102e_hw_phy_config,
+ [RTL_GIGA_MAC_VER_09] = rtl8102e_hw_phy_config,
+ [RTL_GIGA_MAC_VER_10] = NULL,
+ [RTL_GIGA_MAC_VER_11] = rtl8168bb_hw_phy_config,
+ [RTL_GIGA_MAC_VER_14] = rtl8401_hw_phy_config,
+ [RTL_GIGA_MAC_VER_17] = rtl8168bef_hw_phy_config,
+ [RTL_GIGA_MAC_VER_18] = rtl8168cp_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_19] = rtl8168c_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_20] = rtl8168c_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_21] = rtl8168c_3_hw_phy_config,
+ [RTL_GIGA_MAC_VER_22] = rtl8168c_3_hw_phy_config,
+ [RTL_GIGA_MAC_VER_23] = rtl8168cp_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_24] = rtl8168cp_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_25] = rtl8168d_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_26] = rtl8168d_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_28] = rtl8168d_4_hw_phy_config,
+ [RTL_GIGA_MAC_VER_29] = rtl8105e_hw_phy_config,
+ [RTL_GIGA_MAC_VER_30] = rtl8105e_hw_phy_config,
+ [RTL_GIGA_MAC_VER_31] = NULL,
+ [RTL_GIGA_MAC_VER_32] = rtl8168e_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_33] = rtl8168e_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_34] = rtl8168e_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_35] = rtl8168f_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_36] = rtl8168f_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_37] = rtl8402_hw_phy_config,
+ [RTL_GIGA_MAC_VER_38] = rtl8411_hw_phy_config,
+ [RTL_GIGA_MAC_VER_39] = rtl8106e_hw_phy_config,
+ [RTL_GIGA_MAC_VER_40] = rtl8168g_1_hw_phy_config,
+ [RTL_GIGA_MAC_VER_42] = rtl8168g_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_43] = rtl8168g_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_44] = rtl8168g_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_46] = rtl8168h_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_48] = rtl8168h_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_51] = rtl8168ep_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_52] = rtl8117_hw_phy_config,
+ [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config,
+ [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config,
+ };
+
+ if (phy_configs[ver])
+ phy_configs[ver](tp, phydev);
+}
diff --git a/drivers/net/realtek-dsa/Kconfig b/drivers/net/realtek-dsa/Kconfig
new file mode 100644
index 0000000000..f9404e0265
--- /dev/null
+++ b/drivers/net/realtek-dsa/Kconfig
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig DRIVER_NET_DSA_REALTEK
+ tristate "Realtek Ethernet switch family support"
+ depends on DSA
+ select PHYLIB
+ select REALTEK_PHY
+ help
+ Select to enable support for Realtek Ethernet switch chips.
+
+ Note that at least one interface driver must be enabled for the
+ subdrivers to be loaded. Moreover, an interface driver cannot achieve
+ anything without at least one subdriver enabled.
+
+config NET_DSA_TAG_RTL4_A
+ bool
+ help
+ Selected to enable support for tagging frames for the
+ Realtek switches with 4 byte protocol A tags, sich as found in
+ the Realtek RTL8366RB.
+
+config NET_DSA_TAG_RTL8_4
+ bool
+ help
+ Selected to enable support for tagging frames for Realtek
+ switches with 8 byte protocol 4 tags, such as the Realtek RTL8365MB-VC.
+
+if DRIVER_NET_DSA_REALTEK
+
+
+config NET_DSA_REALTEK_MDIO
+ tristate "Realtek MDIO interface driver"
+ depends on OFDEVICE
+ help
+ Select to enable support for registering switches configured
+ through MDIO.
+
+config NET_DSA_REALTEK_SMI
+ tristate "Realtek SMI interface driver"
+ depends on OFDEVICE
+ help
+ Select to enable support for registering switches connected
+ through SMI.
+
+config NET_DSA_REALTEK_RTL8365MB
+ tristate "Realtek RTL8365MB switch subdriver"
+ imply NET_DSA_REALTEK_SMI
+ imply NET_DSA_REALTEK_MDIO
+ select NET_DSA_TAG_RTL8_4
+ help
+ Select to enable support for Realtek RTL8365MB-VC and RTL8367S.
+
+config NET_DSA_REALTEK_RTL8366RB
+ tristate "Realtek RTL8366RB switch subdriver"
+ imply NET_DSA_REALTEK_SMI
+ imply NET_DSA_REALTEK_MDIO
+ select NET_DSA_TAG_RTL4_A
+ help
+ Select to enable support for Realtek RTL8366RB.
+
+endif
diff --git a/drivers/net/realtek-dsa/Makefile b/drivers/net/realtek-dsa/Makefile
new file mode 100644
index 0000000000..3aafa0a8a7
--- /dev/null
+++ b/drivers/net/realtek-dsa/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o tagger.o
+obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o tagger.o
+obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366rb.o
+obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o
+obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
+obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o
diff --git a/drivers/net/realtek-dsa/dsa_priv.h b/drivers/net/realtek-dsa/dsa_priv.h
new file mode 100644
index 0000000000..4cda518310
--- /dev/null
+++ b/drivers/net/realtek-dsa/dsa_priv.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: (c) 2008-2009 Marvell Semiconductor */
+
+#ifndef __DSA_PRIV_H
+#define __DSA_PRIV_H
+
+#include <net.h>
+#include <linux/string.h>
+
+
+/* Helper for removing DSA header tags from packets in the RX path.
+ * Must not be called before skb_pull(len).
+ *
+ * Before:
+ * packet
+ * |
+ * v
+ * | | | | | | | | | | | | | | | | | | |
+ * +-----------------------+-----------------------+---------------+-------+
+ * | Destination MAC | Source MAC | DSA header | EType |
+ * +-----------------------+-----------------------+---------------+-------+
+ * | |
+ * <----- len ----->
+ * After:
+ *
+ * <----- len ----->
+ * |
+ * >>>>>>> v
+ * >>>>>>> | | | | | | | | | | | | | | |
+ * >>>>>>> +-----------------------+-----------------------+-------+
+ * >>>>>>> | Destination MAC | Source MAC | EType |
+ * +-----------------------+-----------------------+-------+
+ *
+ */
+static inline void dsa_strip_etype_header(void *packet, int len)
+{
+ memmove(packet + len, packet, 2 * ETH_ALEN);
+}
+
+/* Helper for creating space for DSA header tags in TX path packets.
+ *
+ * Before:
+ *
+ * <<<<<<< | | | | | | | | | | | | | | |
+ * ^ <<<<<<< +-----------------------+-----------------------+-------+
+ * | <<<<<<< | Destination MAC | Source MAC | EType |
+ * | +-----------------------+-----------------------+-------+
+ * <----- len ----->
+ * |
+ * |
+ * packet
+ *
+ * After:
+ *
+ * | | | | | | | | | | | | | | | | | | |
+ * +-----------------------+-----------------------+---------------+-------+
+ * | Destination MAC | Source MAC | DSA header | EType |
+ * +-----------------------+-----------------------+---------------+-------+
+ * ^ | |
+ * | <----- len ----->
+ * packet
+ */
+static inline void dsa_alloc_etype_header(void *packet, int len)
+{
+ memmove(packet, packet + len, 2 * ETH_ALEN);
+}
+
+/* On TX, skb->data points to skb_mac_header(skb), which means that EtherType
+ * header taggers start exactly where the EtherType is (the EtherType is
+ * treated as part of the DSA header).
+ */
+static inline void *dsa_etype_header_pos(void *packet)
+{
+ return packet + 2 * ETH_ALEN;
+}
+
+#endif
diff --git a/drivers/net/realtek-dsa/realtek-mdio.c b/drivers/net/realtek-dsa/realtek-mdio.c
new file mode 100644
index 0000000000..4fc2295b1b
--- /dev/null
+++ b/drivers/net/realtek-dsa/realtek-mdio.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Realtek MDIO interface driver
+ *
+ * ASICs we intend to support with this driver:
+ *
+ * RTL8366 - The original version, apparently
+ * RTL8369 - Similar enough to have the same datsheet as RTL8366
+ * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite
+ * different register layout from the other two
+ * RTL8366S - Is this "RTL8366 super"?
+ * RTL8367 - Has an OpenWRT driver as well
+ * RTL8368S - Seems to be an alternative name for RTL8366RB
+ * RTL8370 - Also uses SMI
+ *
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
+ * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ */
+
+#include <of_device.h>
+#include <linux/regmap.h>
+#include <clock.h>
+#include <linux/gpio/consumer.h>
+#include <linux/printk.h>
+#include <linux/mdio.h>
+
+#include "realtek.h"
+
+/* Read/write via mdiobus */
+#define REALTEK_MDIO_CTRL0_REG 31
+#define REALTEK_MDIO_START_REG 29
+#define REALTEK_MDIO_CTRL1_REG 21
+#define REALTEK_MDIO_ADDRESS_REG 23
+#define REALTEK_MDIO_DATA_WRITE_REG 24
+#define REALTEK_MDIO_DATA_READ_REG 25
+
+#define REALTEK_MDIO_START_OP 0xFFFF
+#define REALTEK_MDIO_ADDR_OP 0x000E
+#define REALTEK_MDIO_READ_OP 0x0001
+#define REALTEK_MDIO_WRITE_OP 0x0003
+
+static int realtek_mdio_write(void *ctx, u32 reg, u32 val)
+{
+ struct realtek_priv *priv = ctx;
+ struct mii_bus *bus = priv->bus;
+ int ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG,
+ REALTEK_MDIO_ADDR_OP);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG,
+ reg);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG,
+ val);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG,
+ REALTEK_MDIO_WRITE_OP);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int realtek_mdio_read(void *ctx, u32 reg, u32 *val)
+{
+ struct realtek_priv *priv = ctx;
+ struct mii_bus *bus = priv->bus;
+ int ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG,
+ REALTEK_MDIO_ADDR_OP);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG,
+ reg);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG,
+ REALTEK_MDIO_READ_OP);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int realtek_mdio_mdio_write(struct mii_bus *bus, int addr, int regnum,
+ u16 val)
+{
+ struct realtek_priv *priv = bus->priv;
+
+ return priv->ops->phy_write(priv, addr, regnum, val);
+}
+
+static int realtek_mdio_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct realtek_priv *priv = bus->priv;
+
+ return priv->ops->phy_read(priv, addr, regnum);
+}
+
+static const struct regmap_config realtek_mdio_regmap_config = {
+ .reg_bits = 10, /* A4..A0 R4..R0 */
+ .val_bits = 16,
+ .reg_stride = 1,
+ /* PHY regs are at 0x8000 */
+ .max_register = 0xffff,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_bus realtek_mdio_regmap_bus = {
+ .reg_write = realtek_mdio_write,
+ .reg_read = realtek_mdio_read,
+};
+
+static int realtek_mdio_setup_mdio(struct dsa_switch *ds)
+{
+ struct realtek_priv *priv = ds->priv;
+ struct device_node *np;
+ int ret;
+
+ np = of_get_child_by_name(priv->dev->of_node, "mdio");
+ if (!np) {
+ dev_err(priv->dev, "missing 'mdio' child node\n");
+ return -ENODEV;
+ }
+
+ priv->slave_mii_bus->priv = priv;
+ priv->slave_mii_bus->read = realtek_mdio_mdio_read;
+ priv->slave_mii_bus->write = realtek_mdio_mdio_write;
+ priv->slave_mii_bus->dev.of_node = np;
+ priv->slave_mii_bus->parent = priv->dev;
+
+ ret = mdiobus_register(priv->slave_mii_bus);
+ if (ret) {
+ dev_err(priv->dev, "unable to register MDIO bus %pOF\n", np);
+ goto err_put_node;
+ }
+
+ /* Avoid interleaved MDIO access during PHY status polling */
+ slice_depends_on(mdiobus_slice(priv->slave_mii_bus),
+ mdiobus_slice(priv->bus));
+
+ return 0;
+
+err_put_node:
+ of_node_put(np);
+
+ return ret;
+}
+
+static int realtek_mdio_probe(struct phy_device *mdiodev)
+{
+ struct realtek_priv *priv;
+ struct device *dev = &mdiodev->dev;
+ const struct realtek_variant *var;
+ struct regmap_config rc;
+ struct device_node *np;
+ int ret;
+
+ var = of_device_get_match_data(dev);
+ if (!var)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(*priv) + var->chip_data_sz, GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ rc = realtek_mdio_regmap_config;
+ priv->map = regmap_init(dev, &realtek_mdio_regmap_bus, priv, &rc);
+ if (IS_ERR(priv->map)) {
+ ret = PTR_ERR(priv->map);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ priv->mdio_addr = mdiodev->addr;
+ priv->bus = mdiodev->bus;
+ priv->dev = &mdiodev->dev;
+ priv->chip_data = (void *)priv + sizeof(*priv);
+
+ priv->clk_delay = var->clk_delay;
+ priv->cmd_read = var->cmd_read;
+ priv->cmd_write = var->cmd_write;
+ priv->ops = var->ops;
+
+ priv->setup_interface = realtek_mdio_setup_mdio;
+ priv->write_reg_noack = realtek_mdio_write;
+
+ np = dev->of_node;
+
+ dev->priv = priv;
+
+ /* TODO: if power is software controlled, set up any regulators here */
+ priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");
+
+ priv->reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset))
+ return dev_errp_probe(dev, priv->reset, "failed to get RESET GPIO\n");
+
+ if (priv->reset) {
+ gpiod_set_value(priv->reset, 1);
+ dev_dbg(dev, "asserted RESET\n");
+ mdelay(REALTEK_HW_STOP_DELAY);
+ gpiod_set_value(priv->reset, 0);
+ mdelay(REALTEK_HW_START_DELAY);
+ dev_dbg(dev, "deasserted RESET\n");
+ }
+
+ ret = priv->ops->detect(priv);
+ if (ret) {
+ dev_err(dev, "unable to detect switch\n");
+ return ret;
+ }
+
+ priv->ds = kzalloc(sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+ return -ENOMEM;
+
+ priv->ds->dev = dev;
+ priv->ds->num_ports = priv->num_ports;
+ priv->ds->priv = priv;
+ priv->ds->ops = var->ds_ops;
+
+ ret = realtek_dsa_init_tagger(priv);
+ if (ret)
+ return ret;
+
+ ret = dsa_register_switch(priv->ds);
+ if (ret) {
+ dev_err(priv->dev, "unable to register switch ret = %d\n", ret);
+ return ret;
+ }
+
+ return priv->ops->setup ? priv->ops->setup(priv) : 0;
+}
+
+static void realtek_mdio_remove(struct phy_device *mdiodev)
+{
+ struct realtek_priv *priv = mdiodev->dev.priv;
+
+ /* leave the device reset asserted */
+ gpiod_set_value(priv->reset, 1);
+}
+
+static const struct of_device_id realtek_mdio_of_match[] = {
+#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
+ { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, },
+#endif
+#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
+ { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, },
+#endif
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, realtek_mdio_of_match);
+MODULE_DEVICE_TABLE(of, realtek_mdio_of_match);
+
+static struct phy_driver realtek_mdio_driver = {
+ .drv = {
+ .name = "realtek-mdio",
+ .of_match_table = of_match_ptr(realtek_mdio_of_match),
+ },
+ .probe = realtek_mdio_probe,
+ .remove = realtek_mdio_remove,
+};
+
+device_mdio_driver(realtek_mdio_driver);
+
+MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
+MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/realtek-dsa/realtek-smi.c b/drivers/net/realtek-dsa/realtek-smi.c
new file mode 100644
index 0000000000..da150dbc5d
--- /dev/null
+++ b/drivers/net/realtek-dsa/realtek-smi.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Realtek Simple Management Interface (SMI) driver
+ * It can be discussed how "simple" this interface is.
+ *
+ * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels
+ * but the protocol is not MDIO at all. Instead it is a Realtek
+ * pecularity that need to bit-bang the lines in a special way to
+ * communicate with the switch.
+ *
+ * ASICs we intend to support with this driver:
+ *
+ * RTL8366 - The original version, apparently
+ * RTL8369 - Similar enough to have the same datsheet as RTL8366
+ * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite
+ * different register layout from the other two
+ * RTL8366S - Is this "RTL8366 super"?
+ * RTL8367 - Has an OpenWRT driver as well
+ * RTL8368S - Seems to be an alternative name for RTL8366RB
+ * RTL8370 - Also uses SMI
+ *
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
+ * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <driver.h>
+#include <of.h>
+#include <of_device.h>
+#include <linux/mdio.h>
+#include <linux/printk.h>
+#include <clock.h>
+#include <linux/gpio/consumer.h>
+#include <driver.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/if_bridge.h>
+
+
+#include "realtek.h"
+
+#define REALTEK_SMI_ACK_RETRY_COUNT 5
+
+static inline void realtek_smi_clk_delay(struct realtek_priv *priv)
+{
+ ndelay(priv->clk_delay);
+}
+
+static void realtek_smi_start(struct realtek_priv *priv)
+{
+ /* Set GPIO pins to output mode, with initial state:
+ * SCK = 0, SDA = 1
+ */
+ gpiod_direction_output(priv->mdc, 0);
+ gpiod_direction_output(priv->mdio, 1);
+ realtek_smi_clk_delay(priv);
+
+ /* CLK 1: 0 -> 1, 1 -> 0 */
+ gpiod_set_value(priv->mdc, 1);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 0);
+ realtek_smi_clk_delay(priv);
+
+ /* CLK 2: */
+ gpiod_set_value(priv->mdc, 1);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdio, 0);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 0);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdio, 1);
+}
+
+static void realtek_smi_stop(struct realtek_priv *priv)
+{
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdio, 0);
+ gpiod_set_value(priv->mdc, 1);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdio, 1);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 1);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 0);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 1);
+
+ /* Add a click */
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 0);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 1);
+
+ /* Set GPIO pins to input mode */
+ gpiod_direction_input(priv->mdio);
+ gpiod_direction_input(priv->mdc);
+}
+
+static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len)
+{
+ for (; len > 0; len--) {
+ realtek_smi_clk_delay(priv);
+
+ /* Prepare data */
+ gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1))));
+ realtek_smi_clk_delay(priv);
+
+ /* Clocking */
+ gpiod_set_value(priv->mdc, 1);
+ realtek_smi_clk_delay(priv);
+ gpiod_set_value(priv->mdc, 0);
+ }
+}
+
+static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data)
+{
+ gpiod_direction_input(priv->mdio);
+
+ for (*data = 0; len > 0; len--) {
+ u32 u;
+
+ realtek_smi_clk_delay(priv);
+
+ /* Clocking */
+ gpiod_set_value(priv->mdc, 1);
+ realtek_smi_clk_delay(priv);
+ u = !!gpiod_get_value(priv->mdio);
+ gpiod_set_value(priv->mdc, 0);
+
+ *data |= (u << (len - 1));
+ }
+
+ gpiod_direction_output(priv->mdio, 0);
+}
+
+static int realtek_smi_wait_for_ack(struct realtek_priv *priv)
+{
+ int retry_cnt;
+
+ retry_cnt = 0;
+ do {
+ u32 ack;
+
+ realtek_smi_read_bits(priv, 1, &ack);
+ if (ack == 0)
+ break;
+
+ if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) {
+ dev_err(priv->dev, "ACK timeout\n");
+ return -ETIMEDOUT;
+ }
+ } while (1);
+
+ return 0;
+}
+
+static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data)
+{
+ realtek_smi_write_bits(priv, data, 8);
+ return realtek_smi_wait_for_ack(priv);
+}
+
+static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data)
+{
+ realtek_smi_write_bits(priv, data, 8);
+ return 0;
+}
+
+static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data)
+{
+ u32 t;
+
+ /* Read data */
+ realtek_smi_read_bits(priv, 8, &t);
+ *data = (t & 0xff);
+
+ /* Send an ACK */
+ realtek_smi_write_bits(priv, 0x00, 1);
+
+ return 0;
+}
+
+static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data)
+{
+ u32 t;
+
+ /* Read data */
+ realtek_smi_read_bits(priv, 8, &t);
+ *data = (t & 0xff);
+
+ /* Send an ACK */
+ realtek_smi_write_bits(priv, 0x01, 1);
+
+ return 0;
+}
+
+static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data)
+{
+ unsigned long flags;
+ u8 lo = 0;
+ u8 hi = 0;
+ int ret;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ realtek_smi_start(priv);
+
+ /* Send READ command */
+ ret = realtek_smi_write_byte(priv, priv->cmd_read);
+ if (ret)
+ goto out;
+
+ /* Set ADDR[7:0] */
+ ret = realtek_smi_write_byte(priv, addr & 0xff);
+ if (ret)
+ goto out;
+
+ /* Set ADDR[15:8] */
+ ret = realtek_smi_write_byte(priv, addr >> 8);
+ if (ret)
+ goto out;
+
+ /* Read DATA[7:0] */
+ realtek_smi_read_byte0(priv, &lo);
+ /* Read DATA[15:8] */
+ realtek_smi_read_byte1(priv, &hi);
+
+ *data = ((u32)lo) | (((u32)hi) << 8);
+
+ ret = 0;
+
+ out:
+ realtek_smi_stop(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static int realtek_smi_write_reg(struct realtek_priv *priv,
+ u32 addr, u32 data, bool ack)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ realtek_smi_start(priv);
+
+ /* Send WRITE command */
+ ret = realtek_smi_write_byte(priv, priv->cmd_write);
+ if (ret)
+ goto out;
+
+ /* Set ADDR[7:0] */
+ ret = realtek_smi_write_byte(priv, addr & 0xff);
+ if (ret)
+ goto out;
+
+ /* Set ADDR[15:8] */
+ ret = realtek_smi_write_byte(priv, addr >> 8);
+ if (ret)
+ goto out;
+
+ /* Write DATA[7:0] */
+ ret = realtek_smi_write_byte(priv, data & 0xff);
+ if (ret)
+ goto out;
+
+ /* Write DATA[15:8] */
+ if (ack)
+ ret = realtek_smi_write_byte(priv, data >> 8);
+ else
+ ret = realtek_smi_write_byte_noack(priv, data >> 8);
+ if (ret)
+ goto out;
+
+ ret = 0;
+
+ out:
+ realtek_smi_stop(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+/* There is one single case when we need to use this accessor and that
+ * is when issueing soft reset. Since the device reset as soon as we write
+ * that bit, no ACK will come back for natural reasons.
+ */
+static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val)
+{
+ return realtek_smi_write_reg(ctx, reg, val, false);
+}
+
+/* Regmap accessors */
+
+static int realtek_smi_write(void *ctx, u32 reg, u32 val)
+{
+ struct realtek_priv *priv = ctx;
+
+ return realtek_smi_write_reg(priv, reg, val, true);
+}
+
+static int realtek_smi_read(void *ctx, u32 reg, u32 *val)
+{
+ struct realtek_priv *priv = ctx;
+
+ return realtek_smi_read_reg(priv, reg, val);
+}
+
+static const struct regmap_config realtek_smi_regmap_config = {
+ .reg_bits = 10, /* A4..A0 R4..R0 */
+ .val_bits = 16,
+ .reg_stride = 1,
+ /* PHY regs are at 0x8000 */
+ .max_register = 0xffff,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_bus realtek_smi_regmap_bus = {
+ .reg_read = realtek_smi_read,
+ .reg_write = realtek_smi_write,
+};
+
+static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct realtek_priv *priv = bus->priv;
+
+ return priv->ops->phy_read(priv, addr, regnum);
+}
+
+static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum,
+ u16 val)
+{
+ struct realtek_priv *priv = bus->priv;
+
+ return priv->ops->phy_write(priv, addr, regnum, val);
+}
+
+static int realtek_smi_setup_mdio(struct dsa_switch *ds)
+{
+ struct realtek_priv *priv = ds->priv;
+ struct device_node *mdio_np;
+ int ret;
+
+ mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio");
+ if (!mdio_np) {
+ dev_err(priv->dev, "no MDIO bus node\n");
+ return -ENODEV;
+ }
+
+ priv->slave_mii_bus->priv = priv;
+ priv->slave_mii_bus->read = realtek_smi_mdio_read;
+ priv->slave_mii_bus->write = realtek_smi_mdio_write;
+ priv->slave_mii_bus->dev.of_node = mdio_np;
+ priv->slave_mii_bus->parent = priv->dev;
+ ds->slave_mii_bus = priv->slave_mii_bus;
+
+ ret = mdiobus_register(priv->slave_mii_bus);
+ if (ret) {
+ dev_err(priv->dev, "unable to register MDIO bus %pOF\n",
+ mdio_np);
+ goto err_put_node;
+ }
+
+ return 0;
+
+err_put_node:
+ of_node_put(mdio_np);
+
+ return ret;
+}
+
+static int realtek_smi_probe(struct device *dev)
+{
+ const struct realtek_variant *var;
+ struct realtek_priv *priv;
+ struct regmap_config rc;
+ struct device_node *np;
+ int ret;
+
+ var = of_device_get_match_data(dev);
+ np = dev->of_node;
+
+ priv = kzalloc(sizeof(*priv) + var->chip_data_sz, GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->chip_data = (void *)priv + sizeof(*priv);
+
+ rc = realtek_smi_regmap_config;
+ priv->map = regmap_init(dev, &realtek_smi_regmap_bus, priv, &rc);
+ if (IS_ERR(priv->map)) {
+ ret = PTR_ERR(priv->map);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Link forward and backward */
+ priv->dev = dev;
+ priv->clk_delay = var->clk_delay;
+ priv->cmd_read = var->cmd_read;
+ priv->cmd_write = var->cmd_write;
+ priv->ops = var->ops;
+
+ priv->setup_interface = realtek_smi_setup_mdio;
+ priv->write_reg_noack = realtek_smi_write_reg_noack;
+
+ dev->priv = priv;
+ spin_lock_init(&priv->lock);
+
+ /* TODO: if power is software controlled, set up any regulators here */
+
+ priv->reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset))
+ return dev_errp_probe(dev, priv->reset, "failed to get RESET GPIO\n");
+
+ if (priv->reset) {
+ gpiod_set_value(priv->reset, 1);
+ dev_dbg(dev, "asserted RESET\n");
+ mdelay(REALTEK_HW_STOP_DELAY);
+ gpiod_set_value(priv->reset, 0);
+ mdelay(REALTEK_HW_START_DELAY);
+ dev_dbg(dev, "deasserted RESET\n");
+ }
+
+ /* Fetch MDIO pins */
+ priv->mdc = gpiod_get(dev, "mdc", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->mdc))
+ return dev_errp_probe(dev, priv->mdc, "failed to get MDC GPIO\n");
+
+ priv->mdio = gpiod_get(dev, "mdio", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->mdio))
+ return dev_errp_probe(dev, priv->mdio, "failed to get MDIO GPIO\n");
+
+ priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");
+
+ ret = priv->ops->detect(priv);
+ if (ret) {
+ dev_err(dev, "unable to detect switch\n");
+ return ret;
+ }
+
+ priv->ds = kzalloc(sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+ return -ENOMEM;
+
+ priv->ds->dev = dev;
+ priv->ds->num_ports = priv->num_ports;
+ priv->ds->priv = priv;
+ priv->ds->ops = var->ds_ops;
+
+ ret = realtek_dsa_init_tagger(priv);
+ if (ret)
+ return ret;
+
+ ret = dsa_register_switch(priv->ds);
+ if (ret) {
+ dev_err_probe(dev, ret, "unable to register switch\n");
+ return ret;
+ }
+
+ return priv->ops->setup ? priv->ops->setup(priv) : 0;
+}
+
+static void realtek_smi_remove(struct device *dev)
+{
+ struct realtek_priv *priv = dev->priv;
+
+ /* leave the device reset asserted */
+ gpiod_set_value(priv->reset, 1);
+}
+
+static const struct of_device_id realtek_smi_of_match[] = {
+#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
+ {
+ .compatible = "realtek,rtl8366rb",
+ .data = &rtl8366rb_variant,
+ },
+#endif
+#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
+ {
+ .compatible = "realtek,rtl8365mb",
+ .data = &rtl8365mb_variant,
+ },
+#endif
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, realtek_smi_of_match);
+MODULE_DEVICE_TABLE(of, realtek_smi_of_match);
+
+static struct driver realtek_smi_driver = {
+ .name = "realtek-smi",
+ .of_match_table = of_match_ptr(realtek_smi_of_match),
+ .probe = realtek_smi_probe,
+ .remove = realtek_smi_remove,
+};
+device_platform_driver(realtek_smi_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/realtek-dsa/realtek.h b/drivers/net/realtek-dsa/realtek.h
new file mode 100644
index 0000000000..dbca949462
--- /dev/null
+++ b/drivers/net/realtek-dsa/realtek.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Realtek SMI interface driver defines
+ *
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ */
+
+#ifndef _REALTEK_H
+#define _REALTEK_H
+
+#include <linux/spinlock.h>
+#include <linux/phy.h>
+#include <driver.h>
+#include <gpio.h>
+#include <dsa.h>
+
+#define REALTEK_HW_STOP_DELAY 25 /* msecs */
+#define REALTEK_HW_START_DELAY 100 /* msecs */
+
+struct realtek_ops;
+
+struct realtek_priv {
+ struct device *dev;
+ struct gpio_desc *reset;
+ struct gpio_desc *mdc;
+ struct gpio_desc *mdio;
+ union {
+ struct regmap *map;
+ struct regmap *map_nolock;
+ };
+ struct mii_bus slave_mii_bus[1];
+ struct mii_bus *bus;
+ int mdio_addr;
+
+ unsigned int clk_delay;
+ u8 cmd_read;
+ u8 cmd_write;
+ spinlock_t lock; /* Locks around command writes */
+ struct dsa_switch *ds;
+ bool leds_disabled;
+
+ unsigned int cpu_port;
+ unsigned int num_ports;
+
+ const struct realtek_ops *ops;
+ int (*setup_interface)(struct dsa_switch *ds);
+ int (*write_reg_noack)(void *ctx, u32 addr, u32 data);
+
+ char buf[4096];
+ void *chip_data; /* Per-chip extra variant data */
+};
+
+/*
+ * struct realtek_ops - vtable for the per-SMI-chiptype operations
+ * @detect: detects the chiptype
+ */
+struct realtek_ops {
+ int (*detect)(struct realtek_priv *priv);
+ int (*reset_chip)(struct realtek_priv *priv);
+ int (*setup)(struct realtek_priv *priv);
+ void (*cleanup)(struct realtek_priv *priv);
+ int (*enable_port)(struct realtek_priv *priv, int port, bool enable);
+ int (*phy_read)(struct realtek_priv *priv, int phy, int regnum);
+ int (*phy_write)(struct realtek_priv *priv, int phy, int regnum,
+ u16 val);
+ enum dsa_tag_protocol (*get_tag_protocol)(struct realtek_priv *priv);
+ int (*change_tag_protocol)(struct realtek_priv *priv,
+ enum dsa_tag_protocol proto);
+};
+
+struct realtek_variant {
+ const struct dsa_switch_ops *ds_ops;
+ const struct realtek_ops *ops;
+ unsigned int clk_delay;
+ u8 cmd_read;
+ u8 cmd_write;
+ size_t chip_data_sz;
+};
+
+enum dsa_tag_protocol {
+ DSA_TAG_PROTO_RTL4_A = 17,
+ DSA_TAG_PROTO_RTL8_4 = 24,
+ DSA_TAG_PROTO_RTL8_4T = 25,
+};
+
+struct dsa_device_ops {
+ int (*xmit)(struct dsa_port *dp, int port, void *packet, int length);
+ int (*rcv)(struct dsa_switch *ds, int *portp, void *packet, int length);
+ unsigned int needed_headroom;
+ unsigned int needed_tailroom;
+ const char *name;
+ enum dsa_tag_protocol proto;
+};
+
+extern const struct realtek_variant rtl8366rb_variant;
+extern const struct realtek_variant rtl8365mb_variant;
+
+int realtek_dsa_init_tagger(struct realtek_priv *priv);
+
+extern const struct dsa_device_ops rtl4a_netdev_ops;
+extern const struct dsa_device_ops rtl8_4_netdev_ops;
+extern const struct dsa_device_ops rtl8_4t_netdev_ops;
+
+#endif /* _REALTEK_H */
diff --git a/drivers/net/realtek-dsa/rtl8365mb.c b/drivers/net/realtek-dsa/rtl8365mb.c
new file mode 100644
index 0000000000..5889982358
--- /dev/null
+++ b/drivers/net/realtek-dsa/rtl8365mb.c
@@ -0,0 +1,1255 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Realtek SMI subdriver for the Realtek RTL8365MB-VC ethernet switch.
+ *
+ * Copyright (C) 2021 Alvin Å ipraga <alsi@bang-olufsen.dk>
+ * Copyright (C) 2021 Michael Rasmussen <mir@bang-olufsen.dk>
+ *
+ * The RTL8365MB-VC is a 4+1 port 10/100/1000M switch controller. It includes 4
+ * integrated PHYs for the user facing ports, and an extension interface which
+ * can be connected to the CPU - or another PHY - via either MII, RMII, or
+ * RGMII. The switch is configured via the Realtek Simple Management Interface
+ * (SMI), which uses the MDIO/MDC lines.
+ *
+ * Below is a simplified block diagram of the chip and its relevant interfaces.
+ *
+ * .-----------------------------------.
+ * | |
+ * UTP <---------------> Giga PHY <-> PCS <-> P0 GMAC |
+ * UTP <---------------> Giga PHY <-> PCS <-> P1 GMAC |
+ * UTP <---------------> Giga PHY <-> PCS <-> P2 GMAC |
+ * UTP <---------------> Giga PHY <-> PCS <-> P3 GMAC |
+ * | |
+ * CPU/PHY <-MII/RMII/RGMII---> Extension <---> Extension |
+ * | interface 1 GMAC 1 |
+ * | |
+ * SMI driver/ <-MDC/SCL---> Management ~~~~~~~~~~~~~~ |
+ * EEPROM <-MDIO/SDA--> interface ~REALTEK ~~~~~ |
+ * | ~RTL8365MB ~~~ |
+ * | ~GXXXC TAIWAN~ |
+ * GPIO <--------------> Reset ~~~~~~~~~~~~~~ |
+ * | |
+ * Interrupt <----------> Link UP/DOWN events |
+ * controller | |
+ * '-----------------------------------'
+ *
+ * The driver uses DSA to integrate the 4 user and 1 extension ports into the
+ * kernel. Netdevices are created for the user ports, as are PHY devices for
+ * their integrated PHYs. The device tree firmware should also specify the link
+ * partner of the extension port - either via a fixed-link or other phy-handle.
+ * See the device tree bindings for more detailed information. Note that the
+ * driver has only been tested with a fixed-link, but in principle it should not
+ * matter.
+ *
+ * NOTE: Currently, only the RGMII interface is implemented in this driver.
+ *
+ * The interrupt line is asserted on link UP/DOWN events. The driver creates a
+ * custom irqchip to handle this interrupt and demultiplex the events by reading
+ * the status registers via SMI. Interrupts are then propagated to the relevant
+ * PHY device.
+ *
+ * The EEPROM contains initial register values which the chip will read over I2C
+ * upon hardware reset. It is also possible to omit the EEPROM. In both cases,
+ * the driver will manually reprogram some registers using jam tables to reach
+ * an initial state defined by the vendor driver.
+ *
+ * This Linux driver is written based on an OS-agnostic vendor driver from
+ * Realtek. The reference GPL-licensed sources can be found in the OpenWrt
+ * source tree under the name rtl8367c. The vendor driver claims to support a
+ * number of similar switch controllers from Realtek, but the only hardware we
+ * have is the RTL8365MB-VC. Moreover, there does not seem to be any chip under
+ * the name RTL8367C. Although one wishes that the 'C' stood for some kind of
+ * common hardware revision, there exist examples of chips with the suffix -VC
+ * which are explicitly not supported by the rtl8367c driver and which instead
+ * require the rtl8367d vendor driver. With all this uncertainty, the driver has
+ * been modestly named rtl8365mb. Future implementors may wish to rename things
+ * accordingly.
+ *
+ * In the same family of chips, some carry up to 8 user ports and up to 2
+ * extension ports. Where possible this driver tries to make things generic, but
+ * more work must be done to support these configurations. According to
+ * documentation from Realtek, the family should include the following chips:
+ *
+ * - RTL8363NB
+ * - RTL8363NB-VB
+ * - RTL8363SC
+ * - RTL8363SC-VB
+ * - RTL8364NB
+ * - RTL8364NB-VB
+ * - RTL8365MB-VC
+ * - RTL8366SC
+ * - RTL8367RB-VB
+ * - RTL8367SB
+ * - RTL8367S
+ * - RTL8370MB
+ * - RTL8310SR
+ *
+ * Some of the register logic for these additional chips has been skipped over
+ * while implementing this driver. It is therefore not possible to assume that
+ * things will work out-of-the-box for other chips, and a careful review of the
+ * vendor driver may be needed to expand support. The RTL8365MB-VC seems to be
+ * one of the simpler chips.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/printk.h>
+#include <linux/export.h>
+#include <linux/regmap.h>
+#include <net.h>
+#include <linux/if_bridge.h>
+
+#include "realtek.h"
+
+/* Family-specific data and limits */
+#define RTL8365MB_PHYADDRMAX 7
+#define RTL8365MB_NUM_PHYREGS 32
+#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1)
+#define RTL8365MB_MAX_NUM_PORTS 11
+#define RTL8365MB_MAX_NUM_EXTINTS 3
+#define RTL8365MB_LEARN_LIMIT_MAX 2112
+
+/* Chip identification registers */
+#define RTL8365MB_CHIP_ID_REG 0x1300
+
+#define RTL8365MB_CHIP_VER_REG 0x1301
+
+#define RTL8365MB_MAGIC_REG 0x13C2
+#define RTL8365MB_MAGIC_VALUE 0x0249
+
+/* Chip reset register */
+#define RTL8365MB_CHIP_RESET_REG 0x1322
+#define RTL8365MB_CHIP_RESET_SW_MASK 0x0002
+#define RTL8365MB_CHIP_RESET_HW_MASK 0x0001
+
+/* Interrupt polarity register */
+#define RTL8365MB_INTR_POLARITY_REG 0x1100
+#define RTL8365MB_INTR_POLARITY_MASK 0x0001
+#define RTL8365MB_INTR_POLARITY_HIGH 0
+#define RTL8365MB_INTR_POLARITY_LOW 1
+
+/* Interrupt control/status register - enable/check specific interrupt types */
+#define RTL8365MB_INTR_CTRL_REG 0x1101
+#define RTL8365MB_INTR_STATUS_REG 0x1102
+#define RTL8365MB_INTR_SLIENT_START_2_MASK 0x1000
+#define RTL8365MB_INTR_SLIENT_START_MASK 0x0800
+#define RTL8365MB_INTR_ACL_ACTION_MASK 0x0200
+#define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK 0x0100
+#define RTL8365MB_INTR_INTERRUPT_8051_MASK 0x0080
+#define RTL8365MB_INTR_LOOP_DETECTION_MASK 0x0040
+#define RTL8365MB_INTR_GREEN_TIMER_MASK 0x0020
+#define RTL8365MB_INTR_SPECIAL_CONGEST_MASK 0x0010
+#define RTL8365MB_INTR_SPEED_CHANGE_MASK 0x0008
+#define RTL8365MB_INTR_LEARN_OVER_MASK 0x0004
+#define RTL8365MB_INTR_METER_EXCEEDED_MASK 0x0002
+#define RTL8365MB_INTR_LINK_CHANGE_MASK 0x0001
+#define RTL8365MB_INTR_ALL_MASK \
+ (RTL8365MB_INTR_SLIENT_START_2_MASK | \
+ RTL8365MB_INTR_SLIENT_START_MASK | \
+ RTL8365MB_INTR_ACL_ACTION_MASK | \
+ RTL8365MB_INTR_CABLE_DIAG_FIN_MASK | \
+ RTL8365MB_INTR_INTERRUPT_8051_MASK | \
+ RTL8365MB_INTR_LOOP_DETECTION_MASK | \
+ RTL8365MB_INTR_GREEN_TIMER_MASK | \
+ RTL8365MB_INTR_SPECIAL_CONGEST_MASK | \
+ RTL8365MB_INTR_SPEED_CHANGE_MASK | \
+ RTL8365MB_INTR_LEARN_OVER_MASK | \
+ RTL8365MB_INTR_METER_EXCEEDED_MASK | \
+ RTL8365MB_INTR_LINK_CHANGE_MASK)
+
+/* Per-port interrupt type status registers */
+#define RTL8365MB_PORT_LINKDOWN_IND_REG 0x1106
+#define RTL8365MB_PORT_LINKDOWN_IND_MASK 0x07FF
+
+#define RTL8365MB_PORT_LINKUP_IND_REG 0x1107
+#define RTL8365MB_PORT_LINKUP_IND_MASK 0x07FF
+
+/* PHY indirect access registers */
+#define RTL8365MB_INDIRECT_ACCESS_CTRL_REG 0x1F00
+#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK 0x0002
+#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0
+#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1
+#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK 0x0001
+#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1
+#define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01
+#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02
+#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0)
+#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(7, 5)
+#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK GENMASK(11, 8)
+#define RTL8365MB_PHY_BASE 0x2000
+#define RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG 0x1F03
+#define RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG 0x1F04
+
+/* PHY OCP address prefix register */
+#define RTL8365MB_GPHY_OCP_MSB_0_REG 0x1D15
+#define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK 0x0FC0
+#define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK 0xFC00
+
+/* The PHY OCP addresses of PHY registers 0~31 start here */
+#define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400
+
+/* External interface port mode values - used in DIGITAL_INTERFACE_SELECT */
+#define RTL8365MB_EXT_PORT_MODE_DISABLE 0
+#define RTL8365MB_EXT_PORT_MODE_RGMII 1
+#define RTL8365MB_EXT_PORT_MODE_MII_MAC 2
+#define RTL8365MB_EXT_PORT_MODE_MII_PHY 3
+#define RTL8365MB_EXT_PORT_MODE_TMII_MAC 4
+#define RTL8365MB_EXT_PORT_MODE_TMII_PHY 5
+#define RTL8365MB_EXT_PORT_MODE_GMII 6
+#define RTL8365MB_EXT_PORT_MODE_RMII_MAC 7
+#define RTL8365MB_EXT_PORT_MODE_RMII_PHY 8
+#define RTL8365MB_EXT_PORT_MODE_SGMII 9
+#define RTL8365MB_EXT_PORT_MODE_HSGMII 10
+#define RTL8365MB_EXT_PORT_MODE_1000X_100FX 11
+#define RTL8365MB_EXT_PORT_MODE_1000X 12
+#define RTL8365MB_EXT_PORT_MODE_100FX 13
+
+/* External interface mode configuration registers 0~1 */
+#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */
+#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */
+#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \
+ ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \
+ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \
+ 0x0)
+#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \
+ (0xF << (((_extint) % 2)))
+#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \
+ (((_extint) % 2) * 4)
+
+/* External interface RGMII TX/RX delay configuration registers 0~2 */
+#define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */
+#define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */
+#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */
+#define RTL8365MB_EXT_RGMXF_REG(_extint) \
+ ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \
+ (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \
+ (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \
+ 0x0)
+#define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007
+#define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008
+
+/* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */
+#define RTL8365MB_PORT_SPEED_10M 0
+#define RTL8365MB_PORT_SPEED_100M 1
+#define RTL8365MB_PORT_SPEED_1000M 2
+
+/* External interface force configuration registers 0~2 */
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \
+ ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \
+ (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \
+ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \
+ 0x0)
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK 0x0020
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK 0x0010
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK 0x0004
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK 0x0003
+
+/* CPU port mask register - controls which ports are treated as CPU ports */
+#define RTL8365MB_CPU_PORT_MASK_REG 0x1219
+#define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF
+
+/* CPU control register */
+#define RTL8365MB_CPU_CTRL_REG 0x121A
+#define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK 0x0400
+#define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK 0x0200
+#define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK 0x0080
+#define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK 0x0040
+#define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK 0x0038
+#define RTL8365MB_CPU_CTRL_INSERTMODE_MASK 0x0006
+#define RTL8365MB_CPU_CTRL_EN_MASK 0x0001
+
+/* Maximum packet length register */
+#define RTL8365MB_CFG0_MAX_LEN_REG 0x088C
+#define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF
+
+/* Port learning limit registers */
+#define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE 0x0A20
+#define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \
+ (RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE + (_physport))
+
+/* Port isolation (forwarding mask) registers */
+#define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2
+#define RTL8365MB_PORT_ISOLATION_REG(_physport) \
+ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport))
+#define RTL8365MB_PORT_ISOLATION_MASK 0x07FF
+
+/* MSTP port state registers - indexed by tree instance */
+#define RTL8365MB_MSTI_CTRL_BASE 0x0A00
+#define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \
+ (RTL8365MB_MSTI_CTRL_BASE + ((_msti) << 1) + ((_physport) >> 3))
+#define RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(_physport) ((_physport) << 1)
+#define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \
+ (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport)))
+
+struct rtl8365mb_jam_tbl_entry {
+ u16 reg;
+ u16 val;
+};
+
+/* Lifted from the vendor driver sources */
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_8365mb_vc[] = {
+ { 0x13EB, 0x15BB }, { 0x1303, 0x06D6 }, { 0x1304, 0x0700 },
+ { 0x13E2, 0x003F }, { 0x13F9, 0x0090 }, { 0x121E, 0x03CA },
+ { 0x1233, 0x0352 }, { 0x1237, 0x00A0 }, { 0x123A, 0x0030 },
+ { 0x1239, 0x0084 }, { 0x0301, 0x1000 }, { 0x1349, 0x001F },
+ { 0x18E0, 0x4004 }, { 0x122B, 0x241C }, { 0x1305, 0xC000 },
+ { 0x13F0, 0x0000 },
+};
+
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = {
+ { 0x1200, 0x7FCB }, { 0x0884, 0x0003 }, { 0x06EB, 0x0001 },
+ { 0x03Fa, 0x0007 }, { 0x08C8, 0x00C0 }, { 0x0A30, 0x020E },
+ { 0x0800, 0x0000 }, { 0x0802, 0x0000 }, { 0x09DA, 0x0013 },
+ { 0x1D32, 0x0002 },
+};
+
+enum rtl8365mb_phy_interface_mode {
+ RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0,
+ RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0),
+ RTL8365MB_PHY_INTERFACE_MODE_MII = BIT(1),
+ RTL8365MB_PHY_INTERFACE_MODE_TMII = BIT(2),
+ RTL8365MB_PHY_INTERFACE_MODE_RMII = BIT(3),
+ RTL8365MB_PHY_INTERFACE_MODE_RGMII = BIT(4),
+ RTL8365MB_PHY_INTERFACE_MODE_SGMII = BIT(5),
+ RTL8365MB_PHY_INTERFACE_MODE_HSGMII = BIT(6),
+};
+
+/**
+ * struct rtl8365mb_extint - external interface info
+ * @port: the port with an external interface
+ * @id: the external interface ID, which is either 0, 1, or 2
+ * @supported_interfaces: a bitmask of supported PHY interface modes
+ *
+ * Represents a mapping: port -> { id, supported_interfaces }. To be embedded
+ * in &struct rtl8365mb_chip_info for every port with an external interface.
+ */
+struct rtl8365mb_extint {
+ int port;
+ int id;
+ unsigned int supported_interfaces;
+};
+
+/**
+ * struct rtl8365mb_chip_info - static chip-specific info
+ * @name: human-readable chip name
+ * @chip_id: chip identifier
+ * @chip_ver: chip silicon revision
+ * @extints: available external interfaces
+ * @jam_table: chip-specific initialization jam table
+ * @jam_size: size of the chip's jam table
+ *
+ * These data are specific to a given chip in the family of switches supported
+ * by this driver. When adding support for another chip in the family, a new
+ * chip info should be added to the rtl8365mb_chip_infos array.
+ */
+struct rtl8365mb_chip_info {
+ const char *name;
+ u32 chip_id;
+ u32 chip_ver;
+ const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS];
+ const struct rtl8365mb_jam_tbl_entry *jam_table;
+ size_t jam_size;
+};
+
+/* Chip info for each supported switch in the family */
+#define PHY_INTF(_mode) (RTL8365MB_PHY_INTERFACE_MODE_ ## _mode)
+static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
+ {
+ .name = "RTL8365MB-VC",
+ .chip_id = 0x6367,
+ .chip_ver = 0x0040,
+ .extints = {
+ { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
+ PHY_INTF(RMII) | PHY_INTF(RGMII) },
+ },
+ .jam_table = rtl8365mb_init_jam_8365mb_vc,
+ .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ },
+ {
+ .name = "RTL8367S",
+ .chip_id = 0x6367,
+ .chip_ver = 0x00A0,
+ .extints = {
+ { 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },
+ { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |
+ PHY_INTF(RMII) | PHY_INTF(RGMII) },
+ },
+ .jam_table = rtl8365mb_init_jam_8365mb_vc,
+ .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ },
+ {
+ .name = "RTL8367RB-VB",
+ .chip_id = 0x6367,
+ .chip_ver = 0x0020,
+ .extints = {
+ { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
+ PHY_INTF(RMII) | PHY_INTF(RGMII) },
+ { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |
+ PHY_INTF(RMII) | PHY_INTF(RGMII) },
+ },
+ .jam_table = rtl8365mb_init_jam_8365mb_vc,
+ .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ },
+};
+
+enum rtl8365mb_stp_state {
+ RTL8365MB_STP_STATE_DISABLED = 0,
+ RTL8365MB_STP_STATE_BLOCKING = 1,
+ RTL8365MB_STP_STATE_LEARNING = 2,
+ RTL8365MB_STP_STATE_FORWARDING = 3,
+};
+
+enum rtl8365mb_cpu_insert {
+ RTL8365MB_CPU_INSERT_TO_ALL = 0,
+ RTL8365MB_CPU_INSERT_TO_TRAPPING = 1,
+ RTL8365MB_CPU_INSERT_TO_NONE = 2,
+};
+
+enum rtl8365mb_cpu_position {
+ RTL8365MB_CPU_POS_AFTER_SA = 0,
+ RTL8365MB_CPU_POS_BEFORE_CRC = 1,
+};
+
+enum rtl8365mb_cpu_format {
+ RTL8365MB_CPU_FORMAT_8BYTES = 0,
+ RTL8365MB_CPU_FORMAT_4BYTES = 1,
+};
+
+enum rtl8365mb_cpu_rxlen {
+ RTL8365MB_CPU_RXLEN_72BYTES = 0,
+ RTL8365MB_CPU_RXLEN_64BYTES = 1,
+};
+
+/**
+ * struct rtl8365mb_cpu - CPU port configuration
+ * @mask: port mask of ports that parse should parse CPU tags
+ * @trap_port: forward trapped frames to this port
+ * @insert: CPU tag insertion mode in switch->CPU frames
+ * @position: position of CPU tag in frame
+ * @rx_length: minimum CPU RX length
+ * @format: CPU tag format
+ *
+ * Represents the CPU tagging and CPU port configuration of the switch. These
+ * settings are configurable at runtime.
+ */
+struct rtl8365mb_cpu {
+ u32 mask;
+ u32 trap_port;
+ enum rtl8365mb_cpu_insert insert;
+ enum rtl8365mb_cpu_position position;
+ enum rtl8365mb_cpu_rxlen rx_length;
+ enum rtl8365mb_cpu_format format;
+};
+
+/**
+ * struct rtl8365mb_port - private per-port data
+ * @priv: pointer to parent realtek_priv data
+ * @index: DSA port index, same as dsa_port::index
+ */
+struct rtl8365mb_port {
+ struct realtek_priv *priv;
+ unsigned int index;
+};
+
+/**
+ * struct rtl8365mb - driver private data
+ * @priv: pointer to parent realtek_priv data
+ * @irq: registered IRQ or zero
+ * @chip_info: chip-specific info about the attached switch
+ * @cpu: CPU tagging and CPU port configuration for this chip
+ * @ports: per-port data
+ *
+ * Private data for this driver.
+ */
+struct rtl8365mb {
+ struct realtek_priv *priv;
+ const struct rtl8365mb_chip_info *chip_info;
+ struct rtl8365mb_cpu cpu;
+ struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS];
+};
+
+static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv)
+{
+ u32 val;
+
+ return regmap_read_poll_timeout(priv->map_nolock,
+ RTL8365MB_INDIRECT_ACCESS_STATUS_REG,
+ val, !val, 100);
+}
+
+static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy,
+ u32 ocp_addr)
+{
+ u32 val;
+ int ret;
+
+ /* Set OCP prefix */
+ val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr);
+ ret = regmap_update_bits(
+ priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG,
+ RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK,
+ FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val));
+ if (ret)
+ return ret;
+
+ /* Set PHY register address */
+ val = RTL8365MB_PHY_BASE;
+ val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy);
+ val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK,
+ ocp_addr >> 1);
+ val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK,
+ ocp_addr >> 6);
+ ret = regmap_write(priv->map_nolock,
+ RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy,
+ u32 ocp_addr, u16 *data)
+{
+ u32 val;
+ int ret;
+
+ ret = rtl8365mb_phy_poll_busy(priv);
+ if (ret)
+ goto out;
+
+ ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr);
+ if (ret)
+ goto out;
+
+ /* Execute read operation */
+ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK,
+ RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) |
+ FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK,
+ RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ);
+ ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG,
+ val);
+ if (ret)
+ goto out;
+
+ ret = rtl8365mb_phy_poll_busy(priv);
+ if (ret)
+ goto out;
+
+ /* Get PHY register data */
+ ret = regmap_read(priv->map_nolock,
+ RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val);
+ if (ret)
+ goto out;
+
+ *data = val & 0xFFFF;
+
+out:
+
+ return ret;
+}
+
+static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy,
+ u32 ocp_addr, u16 data)
+{
+ u32 val;
+ int ret;
+
+ ret = rtl8365mb_phy_poll_busy(priv);
+ if (ret)
+ goto out;
+
+ ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr);
+ if (ret)
+ goto out;
+
+ /* Set PHY register data */
+ ret = regmap_write(priv->map_nolock,
+ RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data);
+ if (ret)
+ goto out;
+
+ /* Execute write operation */
+ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK,
+ RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) |
+ FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK,
+ RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE);
+ ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG,
+ val);
+ if (ret)
+ goto out;
+
+ ret = rtl8365mb_phy_poll_busy(priv);
+ if (ret)
+ goto out;
+
+out:
+ return 0;
+}
+
+static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum)
+{
+ u32 ocp_addr;
+ u16 val;
+ int ret;
+
+ if (phy > RTL8365MB_PHYADDRMAX)
+ return -EINVAL;
+
+ if (regnum > RTL8365MB_PHYREGMAX)
+ return -EINVAL;
+
+ ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2;
+
+ ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy,
+ regnum, ocp_addr, ret);
+ return ret;
+ }
+
+ dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n",
+ phy, regnum, ocp_addr, val);
+
+ return val;
+}
+
+static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum,
+ u16 val)
+{
+ u32 ocp_addr;
+ int ret;
+
+ if (phy > RTL8365MB_PHYADDRMAX)
+ return -EINVAL;
+
+ if (regnum > RTL8365MB_PHYREGMAX)
+ return -EINVAL;
+
+ ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2;
+
+ ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy,
+ regnum, ocp_addr, ret);
+ return ret;
+ }
+
+ dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n",
+ phy, regnum, ocp_addr, val);
+
+ return 0;
+}
+
+static const struct rtl8365mb_extint *
+rtl8365mb_get_port_extint(struct realtek_priv *priv, int port)
+{
+ struct rtl8365mb *mb = priv->chip_data;
+ int i;
+
+ for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) {
+ const struct rtl8365mb_extint *extint =
+ &mb->chip_info->extints[i];
+
+ if (extint->port == port)
+ return extint;
+ }
+
+ return NULL;
+}
+
+static enum dsa_tag_protocol
+rtl8365mb_get_tag_protocol(struct realtek_priv *priv)
+{
+ struct rtl8365mb_cpu *cpu;
+ struct rtl8365mb *mb;
+
+ mb = priv->chip_data;
+ cpu = &mb->cpu;
+
+ if (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC)
+ return DSA_TAG_PROTO_RTL8_4T;
+
+ return DSA_TAG_PROTO_RTL8_4;
+}
+
+static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
+ phy_interface_t interface)
+{
+ const struct rtl8365mb_extint *extint =
+ rtl8365mb_get_port_extint(priv, port);
+ struct device_node *dn;
+ struct dsa_port *dp;
+ int tx_delay = 0;
+ int rx_delay = 0;
+ u32 val;
+ int ret;
+
+ if (!extint)
+ return -ENODEV;
+
+ dp = dsa_to_port(priv->ds, port);
+ dn = dp->dev->device_node;
+
+ /* Set the RGMII TX/RX delay
+ *
+ * The Realtek vendor driver indicates the following possible
+ * configuration settings:
+ *
+ * TX delay:
+ * 0 = no delay, 1 = 2 ns delay
+ * RX delay:
+ * 0 = no delay, 7 = maximum delay
+ * Each step is approximately 0.3 ns, so the maximum delay is about
+ * 2.1 ns.
+ *
+ * The vendor driver also states that this must be configured *before*
+ * forcing the external interface into a particular mode, which is done
+ * in the rtl8365mb_phylink_mac_link_{up,down} functions.
+ *
+ * Only configure an RGMII TX (resp. RX) delay if the
+ * tx-internal-delay-ps (resp. rx-internal-delay-ps) OF property is
+ * specified. We ignore the detail of the RGMII interface mode
+ * (RGMII_{RXID, TXID, etc.}), as this is considered to be a PHY-only
+ * property.
+ */
+ if (!of_property_read_u32(dn, "tx-internal-delay-ps", &val)) {
+ val = val / 1000; /* convert to ns */
+
+ if (val == 0 || val == 2)
+ tx_delay = val / 2;
+ else
+ dev_warn(priv->dev,
+ "RGMII TX delay must be 0 or 2 ns\n");
+ }
+
+ if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) {
+ val = DIV_ROUND_CLOSEST(val, 300); /* convert to 0.3 ns step */
+
+ if (val <= 7)
+ rx_delay = val;
+ else
+ dev_warn(priv->dev,
+ "RGMII RX delay must be 0 to 2.1 ns\n");
+ }
+
+ ret = regmap_update_bits(
+ priv->map, RTL8365MB_EXT_RGMXF_REG(extint->id),
+ RTL8365MB_EXT_RGMXF_TXDELAY_MASK |
+ RTL8365MB_EXT_RGMXF_RXDELAY_MASK,
+ FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) |
+ FIELD_PREP(RTL8365MB_EXT_RGMXF_RXDELAY_MASK, rx_delay));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(
+ priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id),
+ RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(extint->id),
+ RTL8365MB_EXT_PORT_MODE_RGMII
+ << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(
+ extint->id));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
+ bool link, int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ const struct rtl8365mb_extint *extint =
+ rtl8365mb_get_port_extint(priv, port);
+ u32 r_tx_pause;
+ u32 r_rx_pause;
+ u32 r_duplex;
+ u32 r_speed;
+ u32 r_link;
+ int val;
+ int ret;
+
+ if (!extint)
+ return -ENODEV;
+
+ if (link) {
+ /* Force the link up with the desired configuration */
+ r_link = 1;
+ r_rx_pause = rx_pause ? 1 : 0;
+ r_tx_pause = tx_pause ? 1 : 0;
+
+ if (speed == SPEED_1000) {
+ r_speed = RTL8365MB_PORT_SPEED_1000M;
+ } else if (speed == SPEED_100) {
+ r_speed = RTL8365MB_PORT_SPEED_100M;
+ } else if (speed == SPEED_10) {
+ r_speed = RTL8365MB_PORT_SPEED_10M;
+ } else {
+ dev_err(priv->dev, "unsupported port speed %d\n",
+ speed);
+ dump_stack();
+ return -EINVAL;
+ }
+
+ if (duplex == DUPLEX_FULL) {
+ r_duplex = 1;
+ } else if (duplex == DUPLEX_HALF) {
+ r_duplex = 0;
+ } else {
+ dev_err(priv->dev, "unsupported duplex mode %d\n",
+ duplex);
+ return -EINVAL;
+ }
+ } else {
+ /* Force the link down and reset any programmed configuration */
+ r_link = 0;
+ r_tx_pause = 0;
+ r_rx_pause = 0;
+ r_speed = 0;
+ r_duplex = 0;
+ }
+
+ val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) |
+ FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK,
+ r_tx_pause) |
+ FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK,
+ r_rx_pause) |
+ FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK, r_link) |
+ FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK,
+ r_duplex) |
+ FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed);
+ ret = regmap_write(priv->map,
+ RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint->id),
+ val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port,
+ u8 state)
+{
+ struct realtek_priv *priv = ds->priv;
+ enum rtl8365mb_stp_state val;
+ int msti = 0;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ val = RTL8365MB_STP_STATE_DISABLED;
+ break;
+ case BR_STATE_FORWARDING:
+ val = RTL8365MB_STP_STATE_FORWARDING;
+ break;
+ default:
+ dev_err(priv->dev, "invalid STP state: %u\n", state);
+ return;
+ }
+
+ regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port),
+ RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port),
+ val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port));
+}
+
+static int rtl8365mb_phylink_mac_config(struct dsa_port *dp, int port,
+ phy_interface_t phy_mode)
+{
+ struct realtek_priv *priv = dp->ds->priv;
+ int ret = 0;
+
+ if (phy_interface_mode_is_rgmii(phy_mode)) {
+ ret = rtl8365mb_ext_config_rgmii(priv, port, phy_mode);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to configure RGMII mode on port %d: %d\n",
+ port, ret);
+ }
+
+ return ret;
+}
+
+static void rtl8365mb_phylink_mac_link_down(struct dsa_port *dp, int port,
+ struct phy_device *phy)
+{
+ struct realtek_priv *priv = dp->ds->priv;
+ int ret;
+
+ rtl8365mb_port_stp_state_set(dp->ds, port, BR_STATE_DISABLED);
+
+ if (phy_interface_mode_is_rgmii(phy->interface)) {
+ ret = rtl8365mb_ext_config_forcemode(priv, port, false,
+ 0, 0, 0, 0);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to reset forced mode on port %d: %d\n",
+ port, ret);
+ }
+}
+
+static int rtl8365mb_phylink_mac_link_up(struct dsa_port *dp, int port,
+ struct phy_device *phy)
+{
+ struct realtek_priv *priv = dp->ds->priv;
+ int ret = 0;
+
+ if (phy_interface_mode_is_rgmii(phy->interface)) {
+ ret = rtl8365mb_ext_config_forcemode(priv, port, true,
+ phy->speed, phy->duplex,
+ phy->pause, phy->pause);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to force mode on port %d: %d\n", port,
+ ret);
+ }
+
+ rtl8365mb_port_stp_state_set(dp->ds, port, BR_STATE_FORWARDING);
+
+ return ret;
+}
+
+static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port,
+ bool enable)
+{
+ /* Enable/disable learning by limiting the number of L2 addresses the
+ * port can learn. Realtek documentation states that a limit of zero
+ * disables learning. When enabling learning, set it to the chip's
+ * maximum.
+ */
+ return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port),
+ enable ? RTL8365MB_LEARN_LIMIT_MAX : 0);
+}
+
+static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port,
+ u32 mask)
+{
+ return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask);
+}
+
+static int rtl8365mb_set_irq_enable(struct realtek_priv *priv, bool enable)
+{
+ return regmap_update_bits(priv->map, RTL8365MB_INTR_CTRL_REG,
+ RTL8365MB_INTR_LINK_CHANGE_MASK,
+ FIELD_PREP(RTL8365MB_INTR_LINK_CHANGE_MASK,
+ enable ? 1 : 0));
+}
+
+static int rtl8365mb_irq_disable(struct realtek_priv *priv)
+{
+ return rtl8365mb_set_irq_enable(priv, false);
+}
+
+static int rtl8365mb_irq_setup(struct realtek_priv *priv)
+{
+ int ret;
+
+ /* Disable the interrupt in case the chip has it enabled on reset */
+ ret = rtl8365mb_irq_disable(priv);
+ if (ret)
+ return ret;
+
+ /* Clear the interrupt status register */
+ return regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG,
+ RTL8365MB_INTR_ALL_MASK);
+}
+
+static int rtl8365mb_cpu_config(struct realtek_priv *priv)
+{
+ struct rtl8365mb *mb = priv->chip_data;
+ struct rtl8365mb_cpu *cpu = &mb->cpu;
+ u32 val;
+ int ret;
+
+ ret = regmap_update_bits(priv->map, RTL8365MB_CPU_PORT_MASK_REG,
+ RTL8365MB_CPU_PORT_MASK_MASK,
+ FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK,
+ cpu->mask));
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(RTL8365MB_CPU_CTRL_EN_MASK, 1) |
+ FIELD_PREP(RTL8365MB_CPU_CTRL_INSERTMODE_MASK, cpu->insert) |
+ FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu->position) |
+ FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu->rx_length) |
+ FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu->format) |
+ FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port & 0x7) |
+ FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK,
+ cpu->trap_port >> 3 & 0x1);
+ ret = regmap_write(priv->map, RTL8365MB_CPU_CTRL_REG, val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8365mb_change_tag_protocol(struct realtek_priv *priv,
+ enum dsa_tag_protocol proto)
+{
+ struct rtl8365mb_cpu *cpu;
+ struct rtl8365mb *mb;
+
+ mb = priv->chip_data;
+ cpu = &mb->cpu;
+
+ switch (proto) {
+ case DSA_TAG_PROTO_RTL8_4:
+ cpu->format = RTL8365MB_CPU_FORMAT_8BYTES;
+ cpu->position = RTL8365MB_CPU_POS_AFTER_SA;
+ break;
+ case DSA_TAG_PROTO_RTL8_4T:
+ cpu->format = RTL8365MB_CPU_FORMAT_8BYTES;
+ cpu->position = RTL8365MB_CPU_POS_BEFORE_CRC;
+ break;
+ /* The switch also supports a 4-byte format, similar to rtl4a but with
+ * the same 0x04 8-bit version and probably 8-bit port source/dest.
+ * There is no public doc about it. Not supported yet and it will probably
+ * never be.
+ */
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ return rtl8365mb_cpu_config(priv);
+}
+
+static int rtl8365mb_switch_init(struct realtek_priv *priv)
+{
+ struct rtl8365mb *mb = priv->chip_data;
+ const struct rtl8365mb_chip_info *ci;
+ int ret;
+ int i;
+
+ ci = mb->chip_info;
+
+ /* Do any chip-specific init jam before getting to the common stuff */
+ if (ci->jam_table) {
+ for (i = 0; i < ci->jam_size; i++) {
+ ret = regmap_write(priv->map, ci->jam_table[i].reg,
+ ci->jam_table[i].val);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* Common init jam */
+ for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) {
+ ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg,
+ rtl8365mb_init_jam_common[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtl8365mb_reset_chip(struct realtek_priv *priv)
+{
+ u32 val;
+
+ priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG,
+ FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1));
+
+ /* Realtek documentation says the chip needs 1 second to reset. Sleep
+ * for 100 ms before accessing any registers to prevent ACK timeouts.
+ */
+ mdelay(100);
+ return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val,
+ !(val & RTL8365MB_CHIP_RESET_HW_MASK),
+ 1e6);
+}
+
+static int rtl8365mb_setup(struct realtek_priv *priv)
+{
+ struct rtl8365mb_cpu *cpu;
+ struct dsa_port *cpu_dp;
+ struct rtl8365mb *mb;
+ int ret;
+ int i;
+
+ mb = priv->chip_data;
+ cpu = &mb->cpu;
+
+ ret = rtl8365mb_reset_chip(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to reset chip: %d\n", ret);
+ goto out_error;
+ }
+
+ /* Configure switch to vendor-defined initial state */
+ ret = rtl8365mb_switch_init(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to initialize switch: %d\n", ret);
+ goto out_error;
+ }
+
+ rtl8365mb_irq_setup(priv);
+
+ /* Configure CPU tagging */
+ dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) {
+ cpu->mask |= BIT(cpu_dp->index);
+
+ if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS)
+ cpu->trap_port = cpu_dp->index;
+ }
+
+ if (cpu->mask == 0) {
+ dev_err(priv->dev, "no CPU port found\n");
+ goto out_teardown_irq;
+ }
+
+ ret = rtl8365mb_cpu_config(priv);
+ if (ret)
+ goto out_teardown_irq;
+
+ /* Configure ports */
+ for (i = 0; i < priv->num_ports; i++) {
+ struct rtl8365mb_port *p = &mb->ports[i];
+
+ /* Forward only to the CPU */
+ ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask);
+ if (ret)
+ goto out_teardown_irq;
+
+ /* Disable learning */
+ ret = rtl8365mb_port_set_learning(priv, i, false);
+ if (ret)
+ goto out_teardown_irq;
+
+ /* Set the initial STP state of all ports to DISABLED, otherwise
+ * ports will still forward frames to the CPU despite being
+ * administratively down by default.
+ */
+ rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED);
+
+ /* Set up per-port private data */
+ p->priv = priv;
+ p->index = i;
+ }
+
+ /* Set maximum packet length to 1536 bytes */
+ ret = regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG,
+ RTL8365MB_CFG0_MAX_LEN_MASK,
+ FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536));
+ if (ret)
+ goto out_teardown_irq;
+
+ if (priv->setup_interface) {
+ ret = priv->setup_interface(priv->ds);
+ if (ret) {
+ dev_err(priv->dev, "could not set up MDIO bus\n");
+ goto out_teardown_irq;
+ }
+ }
+
+ return 0;
+
+out_teardown_irq:
+out_error:
+ return ret;
+}
+
+static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver)
+{
+ int ret;
+
+ /* For some reason we have to write a magic value to an arbitrary
+ * register whenever accessing the chip ID/version registers.
+ */
+ ret = regmap_write(map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(map, RTL8365MB_CHIP_ID_REG, id);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(map, RTL8365MB_CHIP_VER_REG, ver);
+ if (ret)
+ return ret;
+
+ /* Reset magic register */
+ ret = regmap_write(map, RTL8365MB_MAGIC_REG, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8365mb_detect(struct realtek_priv *priv)
+{
+ struct rtl8365mb *mb = priv->chip_data;
+ u32 chip_id;
+ u32 chip_ver;
+ int ret;
+ int i;
+
+ ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver);
+ if (ret) {
+ dev_err(priv->dev, "failed to read chip id and version: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rtl8365mb_chip_infos); i++) {
+ const struct rtl8365mb_chip_info *ci = &rtl8365mb_chip_infos[i];
+
+ if (ci->chip_id == chip_id && ci->chip_ver == chip_ver) {
+ mb->chip_info = ci;
+ break;
+ }
+ }
+
+ if (!mb->chip_info) {
+ dev_err(priv->dev,
+ "unrecognized switch (id=0x%04x, ver=0x%04x)\n", chip_id,
+ chip_ver);
+ return -ENODEV;
+ }
+
+ dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name);
+
+ priv->num_ports = RTL8365MB_MAX_NUM_PORTS;
+ mb->priv = priv;
+ mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS;
+ mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;
+ mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA;
+ mb->cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES;
+ mb->cpu.format = RTL8365MB_CPU_FORMAT_8BYTES;
+
+ return 0;
+}
+
+static const struct dsa_switch_ops rtl8365mb_switch_ops = {
+ .port_pre_enable = rtl8365mb_phylink_mac_config,
+ .port_disable = rtl8365mb_phylink_mac_link_down,
+ .port_enable = rtl8365mb_phylink_mac_link_up,
+};
+
+static const struct realtek_ops rtl8365mb_ops = {
+ .detect = rtl8365mb_detect,
+ .phy_read = rtl8365mb_phy_read,
+ .phy_write = rtl8365mb_phy_write,
+ .setup = rtl8365mb_setup,
+ .get_tag_protocol = rtl8365mb_get_tag_protocol,
+ .change_tag_protocol = rtl8365mb_change_tag_protocol,
+};
+
+const struct realtek_variant rtl8365mb_variant = {
+ .ds_ops = &rtl8365mb_switch_ops,
+ .ops = &rtl8365mb_ops,
+ .clk_delay = 10,
+ .cmd_read = 0xb9,
+ .cmd_write = 0xb8,
+ .chip_data_sz = sizeof(struct rtl8365mb),
+};
+EXPORT_SYMBOL_GPL(rtl8365mb_variant);
+
+MODULE_AUTHOR("Alvin Å ipraga <alsi@bang-olufsen.dk>");
+MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/realtek-dsa/rtl8366rb.c b/drivers/net/realtek-dsa/rtl8366rb.c
new file mode 100644
index 0000000000..35028d319e
--- /dev/null
+++ b/drivers/net/realtek-dsa/rtl8366rb.c
@@ -0,0 +1,1106 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Realtek SMI subdriver for the Realtek RTL8366RB ethernet switch
+ *
+ * This is a sparsely documented chip, the only viable documentation seems
+ * to be a patched up code drop from the vendor that appear in various
+ * GPL source trees.
+ *
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
+ * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
+ */
+
+#include <linux/bitops.h>
+#include <net.h>
+#include <linux/if_bridge.h>
+#include <linux/printk.h>
+#include <linux/export.h>
+#include <linux/regmap.h>
+
+#include "realtek.h"
+
+#define RTL8366RB_PORT_NUM_CPU 5
+#define RTL8366RB_NUM_PORTS 6
+#define RTL8366RB_PHY_NO_MAX 4
+#define RTL8366RB_PHY_ADDR_MAX 31
+
+/* Switch Global Configuration register */
+#define RTL8366RB_SGCR 0x0000
+#define RTL8366RB_SGCR_EN_BC_STORM_CTRL BIT(0)
+#define RTL8366RB_SGCR_MAX_LENGTH(a) ((a) << 4)
+#define RTL8366RB_SGCR_MAX_LENGTH_MASK RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_MAX_LENGTH_1522 RTL8366RB_SGCR_MAX_LENGTH(0x0)
+#define RTL8366RB_SGCR_MAX_LENGTH_1536 RTL8366RB_SGCR_MAX_LENGTH(0x1)
+#define RTL8366RB_SGCR_MAX_LENGTH_1552 RTL8366RB_SGCR_MAX_LENGTH(0x2)
+#define RTL8366RB_SGCR_MAX_LENGTH_16000 RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_EN_VLAN BIT(13)
+#define RTL8366RB_SGCR_EN_VLAN_4KTB BIT(14)
+
+/* Port Enable Control register */
+#define RTL8366RB_PECR 0x0001
+
+/* Switch per-port learning disablement register */
+#define RTL8366RB_PORT_LEARNDIS_CTRL 0x0002
+
+/* Security control, actually aging register */
+#define RTL8366RB_SECURITY_CTRL 0x0003
+
+#define RTL8366RB_SSCR2 0x0004
+#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA BIT(0)
+
+/* Port Mode Control registers */
+#define RTL8366RB_PMC0 0x0005
+#define RTL8366RB_PMC0_SPI BIT(0)
+#define RTL8366RB_PMC0_EN_AUTOLOAD BIT(1)
+#define RTL8366RB_PMC0_PROBE BIT(2)
+#define RTL8366RB_PMC0_DIS_BISR BIT(3)
+#define RTL8366RB_PMC0_ADCTEST BIT(4)
+#define RTL8366RB_PMC0_SRAM_DIAG BIT(5)
+#define RTL8366RB_PMC0_EN_SCAN BIT(6)
+#define RTL8366RB_PMC0_P4_IOMODE_SHIFT 7
+#define RTL8366RB_PMC0_P4_IOMODE_MASK GENMASK(9, 7)
+#define RTL8366RB_PMC0_P5_IOMODE_SHIFT 10
+#define RTL8366RB_PMC0_P5_IOMODE_MASK GENMASK(12, 10)
+#define RTL8366RB_PMC0_SDSMODE_SHIFT 13
+#define RTL8366RB_PMC0_SDSMODE_MASK GENMASK(15, 13)
+#define RTL8366RB_PMC1 0x0006
+
+/* Port Mirror Control Register */
+#define RTL8366RB_PMCR 0x0007
+#define RTL8366RB_PMCR_SOURCE_PORT(a) (a)
+#define RTL8366RB_PMCR_SOURCE_PORT_MASK 0x000f
+#define RTL8366RB_PMCR_MONITOR_PORT(a) ((a) << 4)
+#define RTL8366RB_PMCR_MONITOR_PORT_MASK 0x00f0
+#define RTL8366RB_PMCR_MIRROR_RX BIT(8)
+#define RTL8366RB_PMCR_MIRROR_TX BIT(9)
+#define RTL8366RB_PMCR_MIRROR_SPC BIT(10)
+#define RTL8366RB_PMCR_MIRROR_ISO BIT(11)
+
+/* bits 0..7 = port 0, bits 8..15 = port 1 */
+#define RTL8366RB_PAACR0 0x0010
+/* bits 0..7 = port 2, bits 8..15 = port 3 */
+#define RTL8366RB_PAACR1 0x0011
+/* bits 0..7 = port 4, bits 8..15 = port 5 */
+#define RTL8366RB_PAACR2 0x0012
+#define RTL8366RB_PAACR_SPEED_10M 0
+#define RTL8366RB_PAACR_SPEED_100M 1
+#define RTL8366RB_PAACR_SPEED_1000M 2
+#define RTL8366RB_PAACR_FULL_DUPLEX BIT(2)
+#define RTL8366RB_PAACR_LINK_UP BIT(4)
+#define RTL8366RB_PAACR_TX_PAUSE BIT(5)
+#define RTL8366RB_PAACR_RX_PAUSE BIT(6)
+#define RTL8366RB_PAACR_AN BIT(7)
+
+#define RTL8366RB_PAACR_CPU_PORT (RTL8366RB_PAACR_SPEED_1000M | \
+ RTL8366RB_PAACR_FULL_DUPLEX | \
+ RTL8366RB_PAACR_LINK_UP | \
+ RTL8366RB_PAACR_TX_PAUSE | \
+ RTL8366RB_PAACR_RX_PAUSE)
+
+/* bits 0..7 = port 0, bits 8..15 = port 1 */
+#define RTL8366RB_PSTAT0 0x0014
+/* bits 0..7 = port 2, bits 8..15 = port 3 */
+#define RTL8366RB_PSTAT1 0x0015
+/* bits 0..7 = port 4, bits 8..15 = port 5 */
+#define RTL8366RB_PSTAT2 0x0016
+
+#define RTL8366RB_POWER_SAVING_REG 0x0021
+
+/* Spanning tree status (STP) control, two bits per port per FID */
+#define RTL8366RB_STP_STATE_BASE 0x0050 /* 0x0050..0x0057 */
+#define RTL8366RB_STP_STATE_DISABLED 0x0
+#define RTL8366RB_STP_STATE_BLOCKING 0x1
+#define RTL8366RB_STP_STATE_LEARNING 0x2
+#define RTL8366RB_STP_STATE_FORWARDING 0x3
+#define RTL8366RB_STP_MASK GENMASK(1, 0)
+#define RTL8366RB_STP_STATE(port, state) \
+ ((state) << ((port) * 2))
+#define RTL8366RB_STP_STATE_MASK(port) \
+ RTL8366RB_STP_STATE((port), RTL8366RB_STP_MASK)
+
+/* CPU port control reg */
+#define RTL8368RB_CPU_CTRL_REG 0x0061
+#define RTL8368RB_CPU_PORTS_MSK 0x00FF
+/* Disables inserting custom tag length/type 0x8899 */
+#define RTL8368RB_CPU_NO_TAG BIT(15)
+
+#define RTL8366RB_SMAR0 0x0070 /* bits 0..15 */
+#define RTL8366RB_SMAR1 0x0071 /* bits 16..31 */
+#define RTL8366RB_SMAR2 0x0072 /* bits 32..47 */
+
+#define RTL8366RB_RESET_CTRL_REG 0x0100
+#define RTL8366RB_CHIP_CTRL_RESET_HW BIT(0)
+#define RTL8366RB_CHIP_CTRL_RESET_SW BIT(1)
+
+#define RTL8366RB_CHIP_ID_REG 0x0509
+#define RTL8366RB_CHIP_ID_8366 0x5937
+#define RTL8366RB_CHIP_VERSION_CTRL_REG 0x050A
+#define RTL8366RB_CHIP_VERSION_MASK 0xf
+
+/* PHY registers control */
+#define RTL8366RB_PHY_ACCESS_CTRL_REG 0x8000
+#define RTL8366RB_PHY_CTRL_READ BIT(0)
+#define RTL8366RB_PHY_CTRL_WRITE 0
+#define RTL8366RB_PHY_ACCESS_BUSY_REG 0x8001
+#define RTL8366RB_PHY_INT_BUSY BIT(0)
+#define RTL8366RB_PHY_EXT_BUSY BIT(4)
+#define RTL8366RB_PHY_ACCESS_DATA_REG 0x8002
+#define RTL8366RB_PHY_EXT_CTRL_REG 0x8010
+#define RTL8366RB_PHY_EXT_WRDATA_REG 0x8011
+#define RTL8366RB_PHY_EXT_RDDATA_REG 0x8012
+
+#define RTL8366RB_PHY_REG_MASK 0x1f
+#define RTL8366RB_PHY_PAGE_OFFSET 5
+#define RTL8366RB_PHY_PAGE_MASK (0xf << 5)
+#define RTL8366RB_PHY_NO_OFFSET 9
+#define RTL8366RB_PHY_NO_MASK (0x1f << 9)
+
+/* VLAN Ingress Control Register 1, one bit per port.
+ * bit 0 .. 5 will make the switch drop ingress frames without
+ * VID such as untagged or priority-tagged frames for respective
+ * port.
+ * bit 6 .. 11 will make the switch drop ingress frames carrying
+ * a C-tag with VID != 0 for respective port.
+ */
+#define RTL8366RB_VLAN_INGRESS_CTRL1_REG 0x037E
+#define RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) (BIT((port)) | BIT((port) + 6))
+
+/* VLAN Ingress Control Register 2, one bit per port.
+ * bit0 .. bit5 will make the switch drop all ingress frames with
+ * a VLAN classification that does not include the port is in its
+ * member set.
+ */
+#define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f
+
+/* LED control registers */
+#define RTL8366RB_LED_BLINKRATE_REG 0x0430
+#define RTL8366RB_LED_BLINKRATE_MASK 0x0007
+#define RTL8366RB_LED_BLINKRATE_28MS 0x0000
+#define RTL8366RB_LED_BLINKRATE_56MS 0x0001
+#define RTL8366RB_LED_BLINKRATE_84MS 0x0002
+#define RTL8366RB_LED_BLINKRATE_111MS 0x0003
+#define RTL8366RB_LED_BLINKRATE_222MS 0x0004
+#define RTL8366RB_LED_BLINKRATE_446MS 0x0005
+
+#define RTL8366RB_LED_CTRL_REG 0x0431
+#define RTL8366RB_LED_OFF 0x0
+#define RTL8366RB_LED_DUP_COL 0x1
+#define RTL8366RB_LED_LINK_ACT 0x2
+#define RTL8366RB_LED_SPD1000 0x3
+#define RTL8366RB_LED_SPD100 0x4
+#define RTL8366RB_LED_SPD10 0x5
+#define RTL8366RB_LED_SPD1000_ACT 0x6
+#define RTL8366RB_LED_SPD100_ACT 0x7
+#define RTL8366RB_LED_SPD10_ACT 0x8
+#define RTL8366RB_LED_SPD100_10_ACT 0x9
+#define RTL8366RB_LED_FIBER 0xa
+#define RTL8366RB_LED_AN_FAULT 0xb
+#define RTL8366RB_LED_LINK_RX 0xc
+#define RTL8366RB_LED_LINK_TX 0xd
+#define RTL8366RB_LED_MASTER 0xe
+#define RTL8366RB_LED_FORCE 0xf
+#define RTL8366RB_LED_0_1_CTRL_REG 0x0432
+#define RTL8366RB_LED_1_OFFSET 6
+#define RTL8366RB_LED_2_3_CTRL_REG 0x0433
+#define RTL8366RB_LED_3_OFFSET 6
+
+#define RTL8366RB_PORT_VLAN_CTRL_BASE 0x0063
+#define RTL8366RB_PORT_VLAN_CTRL_REG(_p) \
+ (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366RB_PORT_VLAN_CTRL_MASK 0xf
+#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p) (4 * ((_p) % 4))
+
+#define RTL8366RB_VLAN_TABLE_READ_BASE 0x018C
+#define RTL8366RB_VLAN_TABLE_WRITE_BASE 0x0185
+
+#define RTL8366RB_TABLE_ACCESS_CTRL_REG 0x0180
+#define RTL8366RB_TABLE_VLAN_READ_CTRL 0x0E01
+#define RTL8366RB_TABLE_VLAN_WRITE_CTRL 0x0F01
+
+#define RTL8366RB_VLAN_MC_BASE(_x) (0x0020 + (_x) * 3)
+
+#define RTL8366RB_PORT_LINK_STATUS_BASE 0x0014
+#define RTL8366RB_PORT_STATUS_SPEED_MASK 0x0003
+#define RTL8366RB_PORT_STATUS_DUPLEX_MASK 0x0004
+#define RTL8366RB_PORT_STATUS_LINK_MASK 0x0010
+#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK 0x0020
+#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK 0x0040
+#define RTL8366RB_PORT_STATUS_AN_MASK 0x0080
+
+#define RTL8366RB_NUM_VLANS 16
+#define RTL8366RB_NUM_LEDGROUPS 4
+#define RTL8366RB_NUM_VIDS 4096
+#define RTL8366RB_PRIORITYMAX 7
+#define RTL8366RB_NUM_FIDS 8
+#define RTL8366RB_FIDMAX 7
+
+#define RTL8366RB_PORT_1 BIT(0) /* In userspace port 0 */
+#define RTL8366RB_PORT_2 BIT(1) /* In userspace port 1 */
+#define RTL8366RB_PORT_3 BIT(2) /* In userspace port 2 */
+#define RTL8366RB_PORT_4 BIT(3) /* In userspace port 3 */
+#define RTL8366RB_PORT_5 BIT(4) /* In userspace port 4 */
+
+#define RTL8366RB_PORT_CPU BIT(5) /* CPU port */
+
+#define RTL8366RB_PORT_ALL (RTL8366RB_PORT_1 | \
+ RTL8366RB_PORT_2 | \
+ RTL8366RB_PORT_3 | \
+ RTL8366RB_PORT_4 | \
+ RTL8366RB_PORT_5 | \
+ RTL8366RB_PORT_CPU)
+
+#define RTL8366RB_PORT_ALL_BUT_CPU (RTL8366RB_PORT_1 | \
+ RTL8366RB_PORT_2 | \
+ RTL8366RB_PORT_3 | \
+ RTL8366RB_PORT_4 | \
+ RTL8366RB_PORT_5)
+
+#define RTL8366RB_PORT_ALL_EXTERNAL (RTL8366RB_PORT_1 | \
+ RTL8366RB_PORT_2 | \
+ RTL8366RB_PORT_3 | \
+ RTL8366RB_PORT_4)
+
+#define RTL8366RB_PORT_ALL_INTERNAL RTL8366RB_PORT_CPU
+
+/* First configuration word per member config, VID and prio */
+#define RTL8366RB_VLAN_VID_MASK 0xfff
+#define RTL8366RB_VLAN_PRIORITY_SHIFT 12
+#define RTL8366RB_VLAN_PRIORITY_MASK 0x7
+/* Second configuration word per member config, member and untagged */
+#define RTL8366RB_VLAN_UNTAG_SHIFT 8
+#define RTL8366RB_VLAN_UNTAG_MASK 0xff
+#define RTL8366RB_VLAN_MEMBER_MASK 0xff
+/* Third config word per member config, STAG currently unused */
+#define RTL8366RB_VLAN_STAG_MBR_MASK 0xff
+#define RTL8366RB_VLAN_STAG_MBR_SHIFT 8
+#define RTL8366RB_VLAN_STAG_IDX_MASK 0x7
+#define RTL8366RB_VLAN_STAG_IDX_SHIFT 5
+#define RTL8366RB_VLAN_FID_MASK 0x7
+
+/* Port ingress bandwidth control */
+#define RTL8366RB_IB_BASE 0x0200
+#define RTL8366RB_IB_REG(pnum) (RTL8366RB_IB_BASE + (pnum))
+#define RTL8366RB_IB_BDTH_MASK 0x3fff
+#define RTL8366RB_IB_PREIFG BIT(14)
+
+/* Port egress bandwidth control */
+#define RTL8366RB_EB_BASE 0x02d1
+#define RTL8366RB_EB_REG(pnum) (RTL8366RB_EB_BASE + (pnum))
+#define RTL8366RB_EB_BDTH_MASK 0x3fff
+#define RTL8366RB_EB_PREIFG_REG 0x02f8
+#define RTL8366RB_EB_PREIFG BIT(9)
+
+#define RTL8366RB_BDTH_SW_MAX 1048512 /* 1048576? */
+#define RTL8366RB_BDTH_UNIT 64
+#define RTL8366RB_BDTH_REG_DEFAULT 16383
+
+/* QOS */
+#define RTL8366RB_QOS BIT(15)
+/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */
+#define RTL8366RB_QOS_DEFAULT_PREIFG 1
+
+/* Interrupt handling */
+#define RTL8366RB_INTERRUPT_CONTROL_REG 0x0440
+#define RTL8366RB_INTERRUPT_POLARITY BIT(0)
+#define RTL8366RB_P4_RGMII_LED BIT(2)
+#define RTL8366RB_INTERRUPT_MASK_REG 0x0441
+#define RTL8366RB_INTERRUPT_LINK_CHGALL GENMASK(11, 0)
+#define RTL8366RB_INTERRUPT_ACLEXCEED BIT(8)
+#define RTL8366RB_INTERRUPT_STORMEXCEED BIT(9)
+#define RTL8366RB_INTERRUPT_P4_FIBER BIT(12)
+#define RTL8366RB_INTERRUPT_P4_UTP BIT(13)
+#define RTL8366RB_INTERRUPT_VALID (RTL8366RB_INTERRUPT_LINK_CHGALL | \
+ RTL8366RB_INTERRUPT_ACLEXCEED | \
+ RTL8366RB_INTERRUPT_STORMEXCEED | \
+ RTL8366RB_INTERRUPT_P4_FIBER | \
+ RTL8366RB_INTERRUPT_P4_UTP)
+#define RTL8366RB_INTERRUPT_STATUS_REG 0x0442
+#define RTL8366RB_NUM_INTERRUPT 14 /* 0..13 */
+
+/* Port isolation registers */
+#define RTL8366RB_PORT_ISO_BASE 0x0F08
+#define RTL8366RB_PORT_ISO(pnum) (RTL8366RB_PORT_ISO_BASE + (pnum))
+#define RTL8366RB_PORT_ISO_EN BIT(0)
+#define RTL8366RB_PORT_ISO_PORTS_MASK GENMASK(7, 1)
+#define RTL8366RB_PORT_ISO_PORTS(pmask) ((pmask) << 1)
+
+/* bits 0..5 enable force when cleared */
+#define RTL8366RB_MAC_FORCE_CTRL_REG 0x0F11
+
+#define RTL8366RB_OAM_PARSER_REG 0x0F14
+#define RTL8366RB_OAM_MULTIPLEXER_REG 0x0F15
+
+#define RTL8366RB_GREEN_FEATURE_REG 0x0F51
+#define RTL8366RB_GREEN_FEATURE_MSK 0x0007
+#define RTL8366RB_GREEN_FEATURE_TX BIT(0)
+#define RTL8366RB_GREEN_FEATURE_RX BIT(2)
+
+static void rtl8366rb_mask_irqs(struct realtek_priv *priv)
+{
+ int ret;
+
+ ret = regmap_write(priv->map, RTL8366RB_INTERRUPT_MASK_REG, 0);
+ if (ret)
+ dev_err(priv->dev, "could not mask IRQ\n");
+}
+
+static int rtl8366rb_irq_setup(struct realtek_priv *priv)
+{
+ int ret;
+ u32 val;
+
+ rtl8366rb_mask_irqs(priv);
+
+ /* This clears the IRQ status register */
+ ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG,
+ &val);
+ if (ret)
+ dev_err(priv->dev, "can't read interrupt status\n");
+
+ return ret;
+}
+
+static int rtl8366rb_set_addr(struct realtek_priv *priv)
+{
+ u8 addr[ETH_ALEN];
+ u16 val;
+ int ret;
+
+ random_ether_addr(addr);
+
+ dev_info(priv->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ val = addr[0] << 8 | addr[1];
+ ret = regmap_write(priv->map, RTL8366RB_SMAR0, val);
+ if (ret)
+ return ret;
+ val = addr[2] << 8 | addr[3];
+ ret = regmap_write(priv->map, RTL8366RB_SMAR1, val);
+ if (ret)
+ return ret;
+ val = addr[4] << 8 | addr[5];
+ ret = regmap_write(priv->map, RTL8366RB_SMAR2, val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Found in a vendor driver */
+
+/* Struct for handling the jam tables' entries */
+struct rtl8366rb_jam_tbl_entry {
+ u16 reg;
+ u16 val;
+};
+
+/* For the "version 0" early silicon, appear in most source releases */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_0[] = {
+ {0x000B, 0x0001}, {0x03A6, 0x0100}, {0x03A7, 0x0001}, {0x02D1, 0x3FFF},
+ {0x02D2, 0x3FFF}, {0x02D3, 0x3FFF}, {0x02D4, 0x3FFF}, {0x02D5, 0x3FFF},
+ {0x02D6, 0x3FFF}, {0x02D7, 0x3FFF}, {0x02D8, 0x3FFF}, {0x022B, 0x0688},
+ {0x022C, 0x0FAC}, {0x03D0, 0x4688}, {0x03D1, 0x01F5}, {0x0000, 0x0830},
+ {0x02F9, 0x0200}, {0x02F7, 0x7FFF}, {0x02F8, 0x03FF}, {0x0080, 0x03E8},
+ {0x0081, 0x00CE}, {0x0082, 0x00DA}, {0x0083, 0x0230}, {0xBE0F, 0x2000},
+ {0x0231, 0x422A}, {0x0232, 0x422A}, {0x0233, 0x422A}, {0x0234, 0x422A},
+ {0x0235, 0x422A}, {0x0236, 0x422A}, {0x0237, 0x422A}, {0x0238, 0x422A},
+ {0x0239, 0x422A}, {0x023A, 0x422A}, {0x023B, 0x422A}, {0x023C, 0x422A},
+ {0x023D, 0x422A}, {0x023E, 0x422A}, {0x023F, 0x422A}, {0x0240, 0x422A},
+ {0x0241, 0x422A}, {0x0242, 0x422A}, {0x0243, 0x422A}, {0x0244, 0x422A},
+ {0x0245, 0x422A}, {0x0246, 0x422A}, {0x0247, 0x422A}, {0x0248, 0x422A},
+ {0x0249, 0x0146}, {0x024A, 0x0146}, {0x024B, 0x0146}, {0xBE03, 0xC961},
+ {0x024D, 0x0146}, {0x024E, 0x0146}, {0x024F, 0x0146}, {0x0250, 0x0146},
+ {0xBE64, 0x0226}, {0x0252, 0x0146}, {0x0253, 0x0146}, {0x024C, 0x0146},
+ {0x0251, 0x0146}, {0x0254, 0x0146}, {0xBE62, 0x3FD0}, {0x0084, 0x0320},
+ {0x0255, 0x0146}, {0x0256, 0x0146}, {0x0257, 0x0146}, {0x0258, 0x0146},
+ {0x0259, 0x0146}, {0x025A, 0x0146}, {0x025B, 0x0146}, {0x025C, 0x0146},
+ {0x025D, 0x0146}, {0x025E, 0x0146}, {0x025F, 0x0146}, {0x0260, 0x0146},
+ {0x0261, 0xA23F}, {0x0262, 0x0294}, {0x0263, 0xA23F}, {0x0264, 0x0294},
+ {0x0265, 0xA23F}, {0x0266, 0x0294}, {0x0267, 0xA23F}, {0x0268, 0x0294},
+ {0x0269, 0xA23F}, {0x026A, 0x0294}, {0x026B, 0xA23F}, {0x026C, 0x0294},
+ {0x026D, 0xA23F}, {0x026E, 0x0294}, {0x026F, 0xA23F}, {0x0270, 0x0294},
+ {0x02F5, 0x0048}, {0xBE09, 0x0E00}, {0xBE1E, 0x0FA0}, {0xBE14, 0x8448},
+ {0xBE15, 0x1007}, {0xBE4A, 0xA284}, {0xC454, 0x3F0B}, {0xC474, 0x3F0B},
+ {0xBE48, 0x3672}, {0xBE4B, 0x17A7}, {0xBE4C, 0x0B15}, {0xBE52, 0x0EDD},
+ {0xBE49, 0x8C00}, {0xBE5B, 0x785C}, {0xBE5C, 0x785C}, {0xBE5D, 0x785C},
+ {0xBE61, 0x368A}, {0xBE63, 0x9B84}, {0xC456, 0xCC13}, {0xC476, 0xCC13},
+ {0xBE65, 0x307D}, {0xBE6D, 0x0005}, {0xBE6E, 0xE120}, {0xBE2E, 0x7BAF},
+};
+
+/* This v1 init sequence is from Belkin F5D8235 U-Boot release */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_1[] = {
+ {0x0000, 0x0830}, {0x0001, 0x8000}, {0x0400, 0x8130}, {0xBE78, 0x3C3C},
+ {0x0431, 0x5432}, {0xBE37, 0x0CE4}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0},
+ {0xC44C, 0x1585}, {0xC44C, 0x1185}, {0xC44C, 0x1585}, {0xC46C, 0x1585},
+ {0xC46C, 0x1185}, {0xC46C, 0x1585}, {0xC451, 0x2135}, {0xC471, 0x2135},
+ {0xBE10, 0x8140}, {0xBE15, 0x0007}, {0xBE6E, 0xE120}, {0xBE69, 0xD20F},
+ {0xBE6B, 0x0320}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF20},
+ {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, {0xBE24, 0x0000},
+ {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, {0xBE21, 0x0140},
+ {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, {0xBE2E, 0x7B7A},
+ {0xBE36, 0x0CE4}, {0x02F5, 0x0048}, {0xBE77, 0x2940}, {0x000A, 0x83E0},
+ {0xBE79, 0x3C3C}, {0xBE00, 0x1340},
+};
+
+/* This v2 init sequence is from Belkin F5D8235 U-Boot release */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_2[] = {
+ {0x0450, 0x0000}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432},
+ {0xC44F, 0x6250}, {0xC46F, 0x6250}, {0xC456, 0x0C14}, {0xC476, 0x0C14},
+ {0xC44C, 0x1C85}, {0xC44C, 0x1885}, {0xC44C, 0x1C85}, {0xC46C, 0x1C85},
+ {0xC46C, 0x1885}, {0xC46C, 0x1C85}, {0xC44C, 0x0885}, {0xC44C, 0x0881},
+ {0xC44C, 0x0885}, {0xC46C, 0x0885}, {0xC46C, 0x0881}, {0xC46C, 0x0885},
+ {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001},
+ {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6E, 0x0320},
+ {0xBE77, 0x2940}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120},
+ {0x8000, 0x0001}, {0xBE15, 0x1007}, {0x8000, 0x0000}, {0xBE15, 0x1007},
+ {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, {0xBE10, 0x8140},
+ {0xBE00, 0x1340}, {0x0F51, 0x0010},
+};
+
+/* Appears in a DDWRT code dump */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_3[] = {
+ {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432},
+ {0x0F51, 0x0017}, {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0},
+ {0xC456, 0x0C14}, {0xC476, 0x0C14}, {0xC454, 0x3F8B}, {0xC474, 0x3F8B},
+ {0xC450, 0x2071}, {0xC470, 0x2071}, {0xC451, 0x226B}, {0xC471, 0x226B},
+ {0xC452, 0xA293}, {0xC472, 0xA293}, {0xC44C, 0x1585}, {0xC44C, 0x1185},
+ {0xC44C, 0x1585}, {0xC46C, 0x1585}, {0xC46C, 0x1185}, {0xC46C, 0x1585},
+ {0xC44C, 0x0185}, {0xC44C, 0x0181}, {0xC44C, 0x0185}, {0xC46C, 0x0185},
+ {0xC46C, 0x0181}, {0xC46C, 0x0185}, {0xBE24, 0xB000}, {0xBE23, 0xFF51},
+ {0xBE22, 0xDF20}, {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800},
+ {0xBE24, 0x0000}, {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60},
+ {0xBE21, 0x0140}, {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000},
+ {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001},
+ {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6B, 0x0320},
+ {0xBE77, 0x2800}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120},
+ {0x8000, 0x0001}, {0xBE10, 0x8140}, {0x8000, 0x0000}, {0xBE10, 0x8140},
+ {0xBE15, 0x1007}, {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160},
+ {0xBE10, 0x8140}, {0xBE00, 0x1340}, {0x0450, 0x0000}, {0x0401, 0x0000},
+};
+
+/* Belkin F5D8235 v1, "belkin,f5d8235-v1" */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_f5d8235[] = {
+ {0x0242, 0x02BF}, {0x0245, 0x02BF}, {0x0248, 0x02BF}, {0x024B, 0x02BF},
+ {0x024E, 0x02BF}, {0x0251, 0x02BF}, {0x0254, 0x0A3F}, {0x0256, 0x0A3F},
+ {0x0258, 0x0A3F}, {0x025A, 0x0A3F}, {0x025C, 0x0A3F}, {0x025E, 0x0A3F},
+ {0x0263, 0x007C}, {0x0100, 0x0004}, {0xBE5B, 0x3500}, {0x800E, 0x200F},
+ {0xBE1D, 0x0F00}, {0x8001, 0x5011}, {0x800A, 0xA2F4}, {0x800B, 0x17A3},
+ {0xBE4B, 0x17A3}, {0xBE41, 0x5011}, {0xBE17, 0x2100}, {0x8000, 0x8304},
+ {0xBE40, 0x8304}, {0xBE4A, 0xA2F4}, {0x800C, 0xA8D5}, {0x8014, 0x5500},
+ {0x8015, 0x0004}, {0xBE4C, 0xA8D5}, {0xBE59, 0x0008}, {0xBE09, 0x0E00},
+ {0xBE36, 0x1036}, {0xBE37, 0x1036}, {0x800D, 0x00FF}, {0xBE4D, 0x00FF},
+};
+
+/* DGN3500, "netgear,dgn3500", "netgear,dgn3500b" */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_dgn3500[] = {
+ {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0F51, 0x0017},
+ {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, {0x0450, 0x0000},
+ {0x0401, 0x0000}, {0x0431, 0x0960},
+};
+
+/* This jam table activates "green ethernet", which means low power mode
+ * and is claimed to detect the cable length and not use more power than
+ * necessary, and the ports should enter power saving mode 10 seconds after
+ * a cable is disconnected. Seems to always be the same.
+ */
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = {
+ {0xBE78, 0x323C}, {0xBE77, 0x5000}, {0xBE2E, 0x7BA7},
+ {0xBE59, 0x3459}, {0xBE5A, 0x745A}, {0xBE5B, 0x785C},
+ {0xBE5C, 0x785C}, {0xBE6E, 0xE120}, {0xBE79, 0x323C},
+};
+
+/* Function that jams the tables in the proper registers */
+static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table,
+ int jam_size, struct realtek_priv *priv,
+ bool write_dbg)
+{
+ u32 val;
+ int ret;
+ int i;
+
+ for (i = 0; i < jam_size; i++) {
+ if ((jam_table[i].reg & 0xBE00) == 0xBE00) {
+ ret = regmap_read(priv->map,
+ RTL8366RB_PHY_ACCESS_BUSY_REG,
+ &val);
+ if (ret)
+ return ret;
+ if (!(val & RTL8366RB_PHY_INT_BUSY)) {
+ ret = regmap_write(priv->map,
+ RTL8366RB_PHY_ACCESS_CTRL_REG,
+ RTL8366RB_PHY_CTRL_WRITE);
+ if (ret)
+ return ret;
+ }
+ }
+ if (write_dbg)
+ dev_dbg(priv->dev, "jam %04x into register %04x\n",
+ jam_table[i].val,
+ jam_table[i].reg);
+ ret = regmap_write(priv->map,
+ jam_table[i].reg,
+ jam_table[i].val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int rtl8366rb_setup(struct realtek_priv *priv)
+{
+ const struct rtl8366rb_jam_tbl_entry *jam_table;
+ u32 chip_ver = 0;
+ u32 chip_id = 0;
+ int jam_size;
+ u32 val;
+ int ret;
+ int i;
+
+ ret = regmap_read(priv->map, RTL8366RB_CHIP_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(priv->dev, "unable to read chip id\n");
+ return ret;
+ }
+
+ switch (chip_id) {
+ case RTL8366RB_CHIP_ID_8366:
+ break;
+ default:
+ dev_err(priv->dev, "unknown chip id (%04x)\n", chip_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(priv->map, RTL8366RB_CHIP_VERSION_CTRL_REG,
+ &chip_ver);
+ if (ret) {
+ dev_err(priv->dev, "unable to read chip version\n");
+ return ret;
+ }
+
+ dev_info(priv->dev, "RTL%04x ver %u chip found\n",
+ chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK);
+
+ /* Do the init dance using the right jam table */
+ switch (chip_ver) {
+ case 0:
+ jam_table = rtl8366rb_init_jam_ver_0;
+ jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_0);
+ break;
+ case 1:
+ jam_table = rtl8366rb_init_jam_ver_1;
+ jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_1);
+ break;
+ case 2:
+ jam_table = rtl8366rb_init_jam_ver_2;
+ jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_2);
+ break;
+ default:
+ jam_table = rtl8366rb_init_jam_ver_3;
+ jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_3);
+ break;
+ }
+
+ /* Special jam tables for special routers
+ * TODO: are these necessary? Maintainers, please test
+ * without them, using just the off-the-shelf tables.
+ */
+ if (of_machine_is_compatible("belkin,f5d8235-v1")) {
+ jam_table = rtl8366rb_init_jam_f5d8235;
+ jam_size = ARRAY_SIZE(rtl8366rb_init_jam_f5d8235);
+ }
+ if (of_machine_is_compatible("netgear,dgn3500") ||
+ of_machine_is_compatible("netgear,dgn3500b")) {
+ jam_table = rtl8366rb_init_jam_dgn3500;
+ jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500);
+ }
+
+ ret = rtl8366rb_jam_table(jam_table, jam_size, priv, true);
+ if (ret)
+ return ret;
+
+ /* Isolate all user ports so they can only send packets to itself and the CPU port */
+ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
+ ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(i),
+ RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) |
+ RTL8366RB_PORT_ISO_EN);
+ if (ret)
+ return ret;
+ }
+ /* CPU port can send packets to all ports */
+ ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU),
+ RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(priv->ds)) |
+ RTL8366RB_PORT_ISO_EN);
+ if (ret)
+ return ret;
+
+ /* Set up the "green ethernet" feature */
+ ret = rtl8366rb_jam_table(rtl8366rb_green_jam,
+ ARRAY_SIZE(rtl8366rb_green_jam), priv, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->map,
+ RTL8366RB_GREEN_FEATURE_REG,
+ (chip_ver == 1) ? 0x0007 : 0x0003);
+ if (ret)
+ return ret;
+
+ /* Vendor driver sets 0x240 in registers 0xc and 0xd (undocumented) */
+ ret = regmap_write(priv->map, 0x0c, 0x240);
+ if (ret)
+ return ret;
+ ret = regmap_write(priv->map, 0x0d, 0x240);
+ if (ret)
+ return ret;
+
+ /* Set some random MAC address */
+ ret = rtl8366rb_set_addr(priv);
+ if (ret)
+ return ret;
+
+ /* Enable CPU port with custom DSA tag 8899.
+ *
+ * If you set RTL8368RB_CPU_NO_TAG (bit 15) in this registers
+ * the custom tag is turned off.
+ */
+ ret = regmap_update_bits(priv->map, RTL8368RB_CPU_CTRL_REG,
+ 0xFFFF,
+ BIT(priv->cpu_port));
+ if (ret)
+ return ret;
+
+ /* Make sure we default-enable the fixed CPU port */
+ ret = regmap_update_bits(priv->map, RTL8366RB_PECR,
+ BIT(priv->cpu_port),
+ 0);
+ if (ret)
+ return ret;
+
+ /* Set maximum packet length to 1536 bytes */
+ ret = regmap_update_bits(priv->map, RTL8366RB_SGCR,
+ RTL8366RB_SGCR_MAX_LENGTH_MASK,
+ RTL8366RB_SGCR_MAX_LENGTH_1536);
+ if (ret)
+ return ret;
+
+ /* Disable learning for all ports */
+ ret = regmap_write(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL,
+ RTL8366RB_PORT_ALL);
+ if (ret)
+ return ret;
+
+ /* Enable auto ageing for all ports */
+ ret = regmap_write(priv->map, RTL8366RB_SECURITY_CTRL, 0);
+ if (ret)
+ return ret;
+
+ /* Port 4 setup: this enables Port 4, usually the WAN port,
+ * common PHY IO mode is apparently mode 0, and this is not what
+ * the port is initialized to. There is no explanation of the
+ * IO modes in the Realtek source code, if your WAN port is
+ * connected to something exotic such as fiber, then this might
+ * be worth experimenting with.
+ */
+ ret = regmap_update_bits(priv->map, RTL8366RB_PMC0,
+ RTL8366RB_PMC0_P4_IOMODE_MASK,
+ 0 << RTL8366RB_PMC0_P4_IOMODE_SHIFT);
+ if (ret)
+ return ret;
+
+ /* Accept all packets by default, we enable filtering on-demand */
+ ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG,
+ 0);
+ if (ret)
+ return ret;
+ ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG,
+ 0);
+ if (ret)
+ return ret;
+
+ /* Don't drop packets whose DA has not been learned */
+ ret = regmap_update_bits(priv->map, RTL8366RB_SSCR2,
+ RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0);
+ if (ret)
+ return ret;
+
+ /* Set blinking, TODO: make this configurable */
+ ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG,
+ RTL8366RB_LED_BLINKRATE_MASK,
+ RTL8366RB_LED_BLINKRATE_56MS);
+ if (ret)
+ return ret;
+
+ /* Set up LED activity:
+ * Each port has 4 LEDs, we configure all ports to the same
+ * behaviour (no individual config) but we can set up each
+ * LED separately.
+ */
+ if (priv->leds_disabled) {
+ /* Turn everything off */
+ regmap_update_bits(priv->map,
+ RTL8366RB_LED_0_1_CTRL_REG,
+ 0x0FFF, 0);
+ regmap_update_bits(priv->map,
+ RTL8366RB_LED_2_3_CTRL_REG,
+ 0x0FFF, 0);
+ regmap_update_bits(priv->map,
+ RTL8366RB_INTERRUPT_CONTROL_REG,
+ RTL8366RB_P4_RGMII_LED,
+ 0);
+ val = RTL8366RB_LED_OFF;
+ } else {
+ /* TODO: make this configurable per LED */
+ val = RTL8366RB_LED_FORCE;
+ }
+ for (i = 0; i < 4; i++) {
+ ret = regmap_update_bits(priv->map,
+ RTL8366RB_LED_CTRL_REG,
+ 0xf << (i * 4),
+ val << (i * 4));
+ if (ret)
+ return ret;
+ }
+
+ // TODO: Untested: We'll assume POR defaults to suffice for our usecase
+ // rtl8366_reset_vlan(priv);
+
+ rtl8366rb_irq_setup(priv);
+
+ if (priv->setup_interface) {
+ ret = priv->setup_interface(priv->ds);
+ if (ret) {
+ dev_err(priv->dev, "could not set up MDIO bus\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static enum dsa_tag_protocol rtl8366_get_tag_protocol(struct realtek_priv *priv)
+{
+ /* This switch uses the 4 byte protocol A Realtek DSA tag */
+ return DSA_TAG_PROTO_RTL4_A;
+}
+
+static void
+rtl8366rb_mac_link_up(struct dsa_switch *ds, int port)
+{
+ struct realtek_priv *priv = ds->priv;
+ int ret;
+
+ if (port != priv->cpu_port)
+ return;
+
+ dev_dbg(priv->dev, "MAC link up on CPU port (%d)\n", port);
+
+ /* Force the fixed CPU port into 1Gbit mode, no autonegotiation */
+ ret = regmap_update_bits(priv->map, RTL8366RB_MAC_FORCE_CTRL_REG,
+ BIT(port), BIT(port));
+ if (ret) {
+ dev_err(priv->dev, "failed to force 1Gbit on CPU port\n");
+ return;
+ }
+
+ ret = regmap_update_bits(priv->map, RTL8366RB_PAACR2,
+ 0xFF00U,
+ RTL8366RB_PAACR_CPU_PORT << 8);
+ if (ret) {
+ dev_err(priv->dev, "failed to set PAACR on CPU port\n");
+ return;
+ }
+
+ /* Enable the CPU port */
+ ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port),
+ 0);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable the CPU port\n");
+ return;
+ }
+}
+
+static void
+rtl8366rb_mac_link_down(struct dsa_switch *ds, int port)
+{
+ struct realtek_priv *priv = ds->priv;
+ int ret;
+
+ if (port != priv->cpu_port)
+ return;
+
+ dev_dbg(priv->dev, "MAC link down on CPU port (%d)\n", port);
+
+ /* Disable the CPU port */
+ ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port),
+ BIT(port));
+ if (ret) {
+ dev_err(priv->dev, "failed to disable the CPU port\n");
+ return;
+ }
+}
+
+static void rb8366rb_set_port_led(struct realtek_priv *priv,
+ int port, bool enable)
+{
+ u16 val = enable ? 0x3f : 0;
+ int ret;
+
+ if (priv->leds_disabled)
+ return;
+
+ switch (port) {
+ case 0:
+ ret = regmap_update_bits(priv->map,
+ RTL8366RB_LED_0_1_CTRL_REG,
+ 0x3F, val);
+ break;
+ case 1:
+ ret = regmap_update_bits(priv->map,
+ RTL8366RB_LED_0_1_CTRL_REG,
+ 0x3F << RTL8366RB_LED_1_OFFSET,
+ val << RTL8366RB_LED_1_OFFSET);
+ break;
+ case 2:
+ ret = regmap_update_bits(priv->map,
+ RTL8366RB_LED_2_3_CTRL_REG,
+ 0x3F, val);
+ break;
+ case 3:
+ ret = regmap_update_bits(priv->map,
+ RTL8366RB_LED_2_3_CTRL_REG,
+ 0x3F << RTL8366RB_LED_3_OFFSET,
+ val << RTL8366RB_LED_3_OFFSET);
+ break;
+ case 4:
+ ret = regmap_update_bits(priv->map,
+ RTL8366RB_INTERRUPT_CONTROL_REG,
+ RTL8366RB_P4_RGMII_LED,
+ enable ? RTL8366RB_P4_RGMII_LED : 0);
+ break;
+ default:
+ dev_err(priv->dev, "no LED for port %d\n", port);
+ return;
+ }
+ if (ret)
+ dev_err(priv->dev, "error updating LED on port %d\n", port);
+}
+
+static void
+rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct realtek_priv *priv = ds->priv;
+ u32 val;
+ int i;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ val = RTL8366RB_STP_STATE_DISABLED;
+ break;
+ case BR_STATE_FORWARDING:
+ val = RTL8366RB_STP_STATE_FORWARDING;
+ break;
+ default:
+ dev_err(priv->dev, "unknown bridge state requested\n");
+ return;
+ }
+
+ /* Set the same status for the port on all the FIDs */
+ for (i = 0; i < RTL8366RB_NUM_FIDS; i++) {
+ regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i,
+ RTL8366RB_STP_STATE_MASK(port),
+ RTL8366RB_STP_STATE(port, val));
+ }
+}
+
+static int
+rtl8366rb_port_enable(struct dsa_port *dp, int port,
+ struct phy_device *phy)
+{
+ struct realtek_priv *priv = dp->ds->priv;
+ int ret;
+
+ rtl8366rb_mac_link_up(dp->ds, port);
+
+ dev_dbg(priv->dev, "enable port %d\n", port);
+ ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port),
+ 0);
+ if (ret)
+ return ret;
+
+ rb8366rb_set_port_led(priv, port, true);
+
+ rtl8366rb_port_stp_state_set(dp->ds, port, BR_STATE_FORWARDING);
+
+ return 0;
+}
+
+static void
+rtl8366rb_port_disable(struct dsa_port *dp, int port,
+ struct phy_device *phy)
+{
+ struct realtek_priv *priv = dp->ds->priv;
+ int ret;
+
+ rtl8366rb_port_stp_state_set(dp->ds, port, BR_STATE_DISABLED);
+
+ dev_dbg(priv->dev, "disable port %d\n", port);
+ ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port),
+ BIT(port));
+ if (ret)
+ return;
+
+ rb8366rb_set_port_led(priv, port, false);
+
+ rtl8366rb_mac_link_down(dp->ds, port);
+}
+
+static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum)
+{
+ u32 val;
+ u32 reg;
+ int ret;
+
+ if (phy > RTL8366RB_PHY_NO_MAX)
+ return -EINVAL;
+
+ ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
+ RTL8366RB_PHY_CTRL_READ);
+ if (ret)
+ goto out;
+
+ reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum;
+
+ ret = regmap_write(priv->map_nolock, reg, 0);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to write PHY%d reg %04x @ %04x, ret %d\n",
+ phy, regnum, reg, ret);
+ goto out;
+ }
+
+ ret = regmap_read(priv->map_nolock, RTL8366RB_PHY_ACCESS_DATA_REG,
+ &val);
+ if (ret)
+ goto out;
+
+ ret = val;
+
+ dev_dbg(priv->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n",
+ phy, regnum, reg, val);
+
+out:
+ return ret;
+}
+
+static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum,
+ u16 val)
+{
+ u32 reg;
+ int ret;
+
+ if (phy > RTL8366RB_PHY_NO_MAX)
+ return -EINVAL;
+
+ ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
+ RTL8366RB_PHY_CTRL_WRITE);
+ if (ret)
+ goto out;
+
+ reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum;
+
+ dev_dbg(priv->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n",
+ phy, regnum, reg, val);
+
+ ret = regmap_write(priv->map_nolock, reg, val);
+ if (ret)
+ goto out;
+
+out:
+ return ret;
+}
+
+static int rtl8366rb_reset_chip(struct realtek_priv *priv)
+{
+ int timeout = 10;
+ u32 val;
+ int ret;
+
+ priv->write_reg_noack(priv, RTL8366RB_RESET_CTRL_REG,
+ RTL8366RB_CHIP_CTRL_RESET_HW);
+ do {
+ udelay(20000);
+ ret = regmap_read(priv->map, RTL8366RB_RESET_CTRL_REG, &val);
+ if (ret)
+ return ret;
+
+ if (!(val & RTL8366RB_CHIP_CTRL_RESET_HW))
+ break;
+ } while (--timeout);
+
+ if (!timeout) {
+ dev_err(priv->dev, "timeout waiting for the switch to reset\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int rtl8366rb_detect(struct realtek_priv *priv)
+{
+ struct device *dev = priv->dev;
+ int ret;
+ u32 val;
+
+ /* Detect device */
+ ret = regmap_read(priv->map, 0x5c, &val);
+ if (ret) {
+ dev_err(dev, "can't get chip ID (%d)\n", ret);
+ return ret;
+ }
+
+ switch (val) {
+ case 0x6027:
+ dev_info(dev, "found an RTL8366S switch\n");
+ dev_err(dev, "this switch is not yet supported, submit patches!\n");
+ return -ENODEV;
+ case 0x5937:
+ dev_info(dev, "found an RTL8366RB switch\n");
+ priv->cpu_port = RTL8366RB_PORT_NUM_CPU;
+ priv->num_ports = RTL8366RB_NUM_PORTS;
+ break;
+ default:
+ dev_info(dev, "found an Unknown Realtek switch (id=0x%04x)\n",
+ val);
+ break;
+ }
+
+ return rtl8366rb_reset_chip(priv);
+}
+
+static const struct dsa_switch_ops rtl8366rb_switch_ops = {
+ .port_enable = rtl8366rb_port_enable,
+ .port_disable = rtl8366rb_port_disable,
+};
+
+static const struct realtek_ops rtl8366rb_ops = {
+ .detect = rtl8366rb_detect,
+ .phy_read = rtl8366rb_phy_read,
+ .phy_write = rtl8366rb_phy_write,
+ .setup = rtl8366rb_setup,
+ .get_tag_protocol = rtl8366_get_tag_protocol,
+};
+
+const struct realtek_variant rtl8366rb_variant = {
+ .ds_ops = &rtl8366rb_switch_ops,
+ .ops = &rtl8366rb_ops,
+ .clk_delay = 10,
+ .cmd_read = 0xa9,
+ .cmd_write = 0xa8,
+};
+EXPORT_SYMBOL_GPL(rtl8366rb_variant);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/realtek-dsa/tag_rtl4_a.c b/drivers/net/realtek-dsa/tag_rtl4_a.c
new file mode 100644
index 0000000000..30c6a712d9
--- /dev/null
+++ b/drivers/net/realtek-dsa/tag_rtl4_a.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Handler for Realtek 4 byte DSA switch tags
+ * Currently only supports protocol "A" found in RTL8366RB
+ * Copyright (c) 2020 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This "proprietary tag" header looks like so:
+ *
+ * -------------------------------------------------
+ * | MAC DA | MAC SA | 0x8899 | 2 bytes tag | Type |
+ * -------------------------------------------------
+ *
+ * The 2 bytes tag form a 16 bit big endian word. The exact
+ * meaning has been guessed from packet dumps from ingress
+ * frames.
+ */
+
+#include <net.h>
+#include <linux/printk.h>
+
+#include "realtek.h"
+#include "dsa_priv.h"
+
+#define RTL4_A_HDR_LEN 4
+#define RTL4_A_ETHERTYPE 0x8899
+#define RTL4_A_PROTOCOL_SHIFT 12
+/*
+ * 0x1 = Realtek Remote Control protocol (RRCP)
+ * 0x2/0x3 seems to be used for loopback testing
+ * 0x9 = RTL8306 DSA protocol
+ * 0xa = RTL8366RB DSA protocol
+ */
+#define RTL4_A_PROTOCOL_RTL8366RB 0xa
+
+static int rtl4a_tag_xmit(struct dsa_port *dp, int port, void *packet, int length)
+{
+ struct device *dev = dp->ds->dev;
+ __be16 *p;
+ u8 *tag;
+ u16 out;
+
+ /* DSA core already pads out to at least 60 bytes */
+
+ dev_dbg(dev, "add realtek tag to package to port %d\n", port);
+
+ dsa_alloc_etype_header(packet, RTL4_A_HDR_LEN);
+ tag = dsa_etype_header_pos(packet);
+
+ /* Set Ethertype */
+ p = (__be16 *)tag;
+ *p = htons(RTL4_A_ETHERTYPE);
+
+ out = (RTL4_A_PROTOCOL_RTL8366RB << RTL4_A_PROTOCOL_SHIFT);
+ /* The lower bits indicate the port number */
+ out |= BIT(port);
+
+ p = (__be16 *)(tag + 2);
+ *p = htons(out);
+
+ return 0;
+}
+
+static int rtl4a_tag_rcv(struct dsa_switch *ds, int *port, void *packet, int length)
+{
+ struct device *dev = ds->dev;
+ u16 protport;
+ __be16 *p;
+ u16 etype;
+ u8 *tag;
+ u8 prot;
+
+ tag = packet + 2 * ETH_ALEN;
+ p = (__be16 *)tag;
+ etype = ntohs(*p);
+ if (etype != RTL4_A_ETHERTYPE) {
+ /* Not custom, just pass through */
+ dev_dbg(dev, "non-realtek ethertype 0x%04x\n", etype);
+ return -EINVAL;
+ }
+ p = (__be16 *)(tag + 2);
+ protport = ntohs(*p);
+ /* The 4 upper bits are the protocol */
+ prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f;
+ if (prot != RTL4_A_PROTOCOL_RTL8366RB) {
+ dev_err(dev, "unknown realtek protocol 0x%01x\n", prot);
+ return -EPROTO;
+ }
+ *port = protport & 0xff;
+
+ dsa_strip_etype_header(packet, RTL4_A_HDR_LEN);
+
+ return 0;
+}
+
+const struct dsa_device_ops rtl4a_netdev_ops = {
+ .name = "rtl4a",
+ .proto = DSA_TAG_PROTO_RTL4_A,
+ .xmit = rtl4a_tag_xmit,
+ .rcv = rtl4a_tag_rcv,
+ .needed_headroom = RTL4_A_HDR_LEN,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A);
diff --git a/drivers/net/realtek-dsa/tag_rtl8_4.c b/drivers/net/realtek-dsa/tag_rtl8_4.c
new file mode 100644
index 0000000000..80e977a65d
--- /dev/null
+++ b/drivers/net/realtek-dsa/tag_rtl8_4.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Handler for Realtek 8 byte switch tags
+ *
+ * Copyright (C) 2021 Alvin Å ipraga <alsi@bang-olufsen.dk>
+ *
+ * NOTE: Currently only supports protocol "4" found in the RTL8365MB, hence
+ * named tag_rtl8_4.
+ *
+ * This tag has the following format:
+ *
+ * 0 7|8 15
+ * |-----------------------------------+-----------------------------------|---
+ * | (16-bit) | ^
+ * | Realtek EtherType [0x8899] | |
+ * |-----------------------------------+-----------------------------------| 8
+ * | (8-bit) | (8-bit) |
+ * | Protocol [0x04] | REASON | b
+ * |-----------------------------------+-----------------------------------| y
+ * | (1) | (1) | (2) | (1) | (3) | (1) | (1) | (1) | (5) | t
+ * | FID_EN | X | FID | PRI_EN | PRI | KEEP | X | LEARN_DIS | X | e
+ * |-----------------------------------+-----------------------------------| s
+ * | (1) | (15-bit) | |
+ * | ALLOW | TX/RX | v
+ * |-----------------------------------+-----------------------------------|---
+ *
+ * With the following field descriptions:
+ *
+ * field | description
+ * ------------+-------------
+ * Realtek | 0x8899: indicates that this is a proprietary Realtek tag;
+ * EtherType | note that Realtek uses the same EtherType for
+ * | other incompatible tag formats (e.g. tag_rtl4_a.c)
+ * Protocol | 0x04: indicates that this tag conforms to this format
+ * X | reserved
+ * ------------+-------------
+ * REASON | reason for forwarding packet to CPU
+ * | 0: packet was forwarded or flooded to CPU
+ * | 80: packet was trapped to CPU
+ * FID_EN | 1: packet has an FID
+ * | 0: no FID
+ * FID | FID of packet (if FID_EN=1)
+ * PRI_EN | 1: force priority of packet
+ * | 0: don't force priority
+ * PRI | priority of packet (if PRI_EN=1)
+ * KEEP | preserve packet VLAN tag format
+ * LEARN_DIS | don't learn the source MAC address of the packet
+ * ALLOW | 1: treat TX/RX field as an allowance port mask, meaning the
+ * | packet may only be forwarded to ports specified in the
+ * | mask
+ * | 0: no allowance port mask, TX/RX field is the forwarding
+ * | port mask
+ * TX/RX | TX (switch->CPU): port number the packet was received on
+ * | RX (CPU->switch): forwarding port mask (if ALLOW=0)
+ * | allowance port mask (if ALLOW=1)
+ *
+ * The tag can be positioned before Ethertype, using tag "rtl8_4":
+ *
+ * +--------+--------+------------+------+-----
+ * | MAC DA | MAC SA | 8 byte tag | Type | ...
+ * +--------+--------+------------+------+-----
+ *
+ * The tag can also appear between the end of the payload and before the CRC,
+ * using tag "rtl8_4t":
+ *
+ * +--------+--------+------+-----+---------+------------+-----+
+ * | MAC DA | MAC SA | TYPE | ... | payload | 8-byte tag | CRC |
+ * +--------+--------+------+-----+---------+------------+-----+
+ *
+ * The added bytes after the payload will break most checksums, either in
+ * software or hardware. We don't care for checksums in barebox, so this
+ * is just ignored.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/printk.h>
+#include <net.h>
+
+#include "realtek.h"
+#include "dsa_priv.h"
+
+/* Protocols supported:
+ *
+ * 0x04 = RTL8365MB DSA protocol
+ */
+
+#define ETH_P_REALTEK 0x8899
+
+#define RTL8_4_TAG_LEN 8
+
+#define RTL8_4_PROTOCOL GENMASK(15, 8)
+#define RTL8_4_PROTOCOL_RTL8365MB 0x04
+#define RTL8_4_REASON GENMASK(7, 0)
+#define RTL8_4_REASON_FORWARD 0
+#define RTL8_4_REASON_TRAP 80
+
+#define RTL8_4_LEARN_DIS BIT(5)
+
+#define RTL8_4_TX GENMASK(3, 0)
+#define RTL8_4_RX GENMASK(10, 0)
+
+static void rtl8_4_write_tag(int port, void *tag)
+{
+ __be16 tag16[RTL8_4_TAG_LEN / 2];
+
+ /* Set Realtek EtherType */
+ tag16[0] = htons(ETH_P_REALTEK);
+
+ /* Set Protocol; zero REASON */
+ tag16[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB));
+
+ /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */
+ tag16[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1));
+
+ /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */
+ tag16[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(port)));
+
+ memcpy(tag, tag16, RTL8_4_TAG_LEN);
+}
+
+static int rtl8_4_tag_xmit(struct dsa_port *dp, int port, void *packet, int length)
+{
+ dsa_alloc_etype_header(packet, RTL8_4_TAG_LEN);
+
+ rtl8_4_write_tag(port, dsa_etype_header_pos(packet));
+
+ return 0;
+}
+
+static int rtl8_4t_tag_xmit(struct dsa_port *dp, int port, void *packet, int length)
+{
+ rtl8_4_write_tag(port, packet + length - dp->ds->needed_tx_tailroom);
+
+ return 0;
+}
+
+static int rtl8_4_read_tag(int *port, struct device *dev, void *tag)
+{
+ __be16 tag16[RTL8_4_TAG_LEN / 2];
+ u16 etype;
+ u8 proto;
+
+ memcpy(tag16, tag, RTL8_4_TAG_LEN);
+
+ /* Parse Realtek EtherType */
+ etype = ntohs(tag16[0]);
+ if (unlikely(etype != ETH_P_REALTEK)) {
+ dev_warn(dev, "non-realtek ethertype 0x%04x\n", etype);
+ return -EPROTO;
+ }
+
+ /* Parse Protocol */
+ proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag16[1]));
+ if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) {
+ dev_warn(dev, "unknown realtek protocol 0x%02x\n", proto);
+ return -EPROTO;
+ }
+
+ /* Parse TX (switch->CPU) */
+ *port = FIELD_GET(RTL8_4_TX, ntohs(tag16[3]));
+
+ return 0;
+}
+
+static int rtl8_4_tag_rcv(struct dsa_switch *ds, int *port, void *packet, int length)
+{
+ int ret;
+
+ ret = rtl8_4_read_tag(port, ds->dev, dsa_etype_header_pos(packet));
+ if (unlikely(ret))
+ return ret;
+
+ dsa_strip_etype_header(packet, RTL8_4_TAG_LEN);
+
+ return 0;
+}
+
+static int rtl8_4t_tag_rcv(struct dsa_switch *ds, int *port, void *packet, int length)
+{
+ return rtl8_4_read_tag(port, ds->dev, packet + length - ds->needed_rx_tailroom);
+}
+
+/* Ethertype version */
+const struct dsa_device_ops rtl8_4_netdev_ops = {
+ .name = "rtl8_4",
+ .proto = DSA_TAG_PROTO_RTL8_4,
+ .xmit = rtl8_4_tag_xmit,
+ .rcv = rtl8_4_tag_rcv,
+ .needed_headroom = RTL8_4_TAG_LEN,
+};
+
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4);
+
+/* Tail version */
+const struct dsa_device_ops rtl8_4t_netdev_ops = {
+ .name = "rtl8_4t",
+ .proto = DSA_TAG_PROTO_RTL8_4T,
+ .xmit = rtl8_4t_tag_xmit,
+ .rcv = rtl8_4t_tag_rcv,
+ .needed_tailroom = RTL8_4_TAG_LEN,
+};
+
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/realtek-dsa/tagger.c b/drivers/net/realtek-dsa/tagger.c
new file mode 100644
index 0000000000..3a41f3b3c1
--- /dev/null
+++ b/drivers/net/realtek-dsa/tagger.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "realtek.h"
+
+int realtek_dsa_init_tagger(struct realtek_priv *priv)
+{
+ const struct dsa_device_ops *tagger_ops = NULL;
+ struct dsa_switch_ops *ops;
+
+ /* TODO: Tagging can be configured per port in Linux. barebox DSA core
+ * will need some refactoring to do that. For now we just use the
+ * Linux default and leave ->change_tag_protocol unused and
+ * dsa-tag-protocol OF properties unheeded.
+ */
+ switch (priv->ops->get_tag_protocol(priv)) {
+ case DSA_TAG_PROTO_RTL4_A:
+ if (IS_ENABLED(CONFIG_NET_DSA_TAG_RTL4_A))
+ tagger_ops = &rtl4a_netdev_ops;
+ break;
+ case DSA_TAG_PROTO_RTL8_4:
+ if (IS_ENABLED(CONFIG_NET_DSA_TAG_RTL8_4))
+ tagger_ops = &rtl8_4_netdev_ops;
+ break;
+ case DSA_TAG_PROTO_RTL8_4T:
+ if (IS_ENABLED(CONFIG_NET_DSA_TAG_RTL8_4))
+ tagger_ops = &rtl8_4t_netdev_ops;
+ break;
+ default:
+ break;
+ }
+
+ if (!tagger_ops)
+ return -EINVAL;
+
+ ops = memdup(priv->ds->ops, sizeof(*priv->ds->ops));
+ ops->xmit = tagger_ops->xmit;
+ ops->rcv = tagger_ops->rcv;
+ priv->ds->ops = ops;
+ priv->ds->needed_headroom = tagger_ops->needed_headroom;
+ priv->ds->needed_rx_tailroom = tagger_ops->needed_tailroom;
+ priv->ds->needed_tx_tailroom = tagger_ops->needed_tailroom;
+
+ return 0;
+}
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
index c133a542dc..5c91c10fea 100644
--- a/drivers/net/rtl8139.c
+++ b/drivers/net/rtl8139.c
@@ -10,8 +10,6 @@
#include <linux/phy.h>
#include <linux/pci.h>
-#include <asm/dma-mapping.h>
-
#define RTL8139_DEBUG
#undef RTL8139_DEBUG
@@ -375,7 +373,6 @@ static int rtl8139_init_dev(struct eth_device *edev)
struct rtl8139_priv *priv = edev->priv;
rtl8139_chip_reset(priv);
- pci_set_master(priv->pci_dev);
return 0;
}
@@ -392,6 +389,8 @@ static int rtl8139_eth_open(struct eth_device *edev)
rtl8139_init_ring(priv);
rtl8139_hw_start(priv);
+ pci_set_master(priv->pci_dev);
+
ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0,
PHY_INTERFACE_MODE_NA);
@@ -410,6 +409,11 @@ static void rtl8139_eth_halt(struct eth_device *edev)
pci_clear_master(priv->pci_dev);
+ dma_free_coherent((void *)priv->tx_bufs, priv->tx_bufs_dma,
+ TX_BUF_TOT_LEN);
+ dma_free_coherent((void *)priv->rx_ring, priv->rx_ring_dma,
+ RX_BUF_TOT_LEN);
+
/* Green! Put the chip in low-power mode. */
RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
}
@@ -531,7 +535,7 @@ static int rtl8139_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct eth_device *edev;
struct rtl8139_priv *priv;
int ret;
- struct device_d *dev = &pdev->dev;
+ struct device *dev = &pdev->dev;
/* enable pci device */
pci_enable_device(pdev);
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c
deleted file mode 100644
index e923e179bf..0000000000
--- a/drivers/net/rtl8169.c
+++ /dev/null
@@ -1,561 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
- */
-
-#include <common.h>
-#include <dma.h>
-#include <init.h>
-#include <net.h>
-#include <malloc.h>
-#include <linux/pci.h>
-
-#define NUM_TX_DESC 1
-#define NUM_RX_DESC 4
-#define PKT_BUF_SIZE 1536
-#define ETH_ZLEN 60
-
-struct rtl8169_chip_info {
- const char *name;
- u8 version;
- u32 RxConfigMask;
-};
-
-#define BD_STAT_OWN 0x80000000
-#define BD_STAT_EOR 0x40000000
-#define BD_STAT_FS 0x20000000
-#define BD_STAT_LS 0x10000000
-#define BD_STAT_RX_RES 0x00200000
-struct bufdesc {
- u32 status;
- u32 vlan_tag;
- u32 buf_addr;
- u32 buf_Haddr;
-};
-
-struct rtl8169_priv {
- struct eth_device edev;
- void __iomem *base;
- struct pci_dev *pci_dev;
- int chipset;
-
- volatile struct bufdesc *tx_desc;
- dma_addr_t tx_desc_phys;
- void *tx_buf;
- dma_addr_t tx_buf_phys;
- unsigned int cur_tx;
-
- volatile struct bufdesc *rx_desc;
- dma_addr_t rx_desc_phys;
- void *rx_buf;
- dma_addr_t rx_buf_phys;
- unsigned int cur_rx;
-
- struct mii_bus miibus;
-};
-
-#define MAC0 0x00
-#define MAR0 0x08
-#define TxDescStartAddrLow 0x20
-#define TxDescStartAddrHigh 0x24
-#define TxHDescStartAddrLow 0x28
-#define TxHDescStartAddrHigh 0x2c
-#define FLASH 0x30
-#define ERSR 0x36
-#define ChipCmd 0x37
-#define CmdReset 0x10
-#define CmdRxEnb 0x08
-#define CmdTxEnb 0x04
-#define RxBufEmpty 0x01
-#define TxPoll 0x38
-#define IntrMask 0x3c
-#define IntrStatus 0x3e
-#define SYSErr 0x8000
-#define PCSTimeout 0x4000
-#define SWInt 0x0100
-#define TxDescUnavail 0x80
-#define RxFIFOOver 0x40
-#define RxUnderrun 0x20
-#define RxOverflow 0x10
-#define TxErr 0x08
-#define TxOK 0x04
-#define RxErr 0x02
-#define RxOK 0x01
-#define TxConfig 0x40
-#define TxInterFrameGapShift 24
-#define TxDMAShift 8
-#define RxConfig 0x44
-#define AcceptErr 0x20
-#define AcceptRunt 0x10
-#define AcceptBroadcast 0x08
-#define AcceptMulticast 0x04
-#define AcceptMyPhys 0x02
-#define AcceptAllPhys 0x01
-#define RxCfgFIFOShift 13
-#define RxCfgDMAShift 8
-#define RxMissed 0x4c
-#define Cfg9346 0x50
-#define Cfg9346_Lock 0x00
-#define Cfg9346_Unlock 0xc0
-#define Config0 0x51
-#define Config1 0x52
-#define Config2 0x53
-#define Config3 0x54
-#define Config4 0x55
-#define Config5 0x56
-#define MultiIntr 0x5c
-#define PHYAR 0x60
-#define TBICSR 0x64
-#define TBI_ANAR 0x68
-#define TBI_LPAR 0x6a
-#define PHYstatus 0x6c
-#define RxMaxSize 0xda
-#define CPlusCmd 0xe0
-#define RxDescStartAddrLow 0xe4
-#define RxDescStartAddrHigh 0xe8
-#define EarlyTxThres 0xec
-#define FuncEvent 0xf0
-#define FuncEventMask 0xf4
-#define FuncPresetState 0xf8
-#define FuncForceEvent 0xfc
-
-/* write MMIO register */
-#define RTL_W8(priv, reg, val) writeb(val, ((char *)(priv->base) + reg))
-#define RTL_W16(priv, reg, val) writew(val, ((char *)(priv->base) + reg))
-#define RTL_W32(priv, reg, val) writel(val, ((char *)(priv->base) + reg))
-
-/* read MMIO register */
-#define RTL_R8(priv, reg) readb(((char *)(priv->base) + reg))
-#define RTL_R16(priv, reg) readw(((char *)(priv->base) + reg))
-#define RTL_R32(priv, reg) readl(((char *)(priv->base) + reg))
-
-static const u32 rtl8169_rx_config =
- (7 << RxCfgFIFOShift) | (6 << RxCfgDMAShift);
-
-static void rtl8169_chip_reset(struct rtl8169_priv *priv)
-{
- int i;
-
- /* Soft reset the chip. */
- RTL_W8(priv, ChipCmd, CmdReset);
-
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--) {
- if ((RTL_R8(priv, ChipCmd) & CmdReset) == 0)
- break;
- udelay(10);
- }
-}
-
-static struct rtl8169_chip_info chip_info[] = {
- {"RTL-8169", 0x00, 0xff7e1880},
- {"RTL-8169", 0x04, 0xff7e1880},
- {"RTL-8169", 0x00, 0xff7e1880},
- {"RTL-8169s/8110s", 0x02, 0xff7e1880},
- {"RTL-8169s/8110s", 0x04, 0xff7e1880},
- {"RTL-8169sb/8110sb", 0x10, 0xff7e1880},
- {"RTL-8169sc/8110sc", 0x18, 0xff7e1880},
- {"RTL-8168b/8111sb", 0x30, 0xff7e1880},
- {"RTL-8168b/8111sb", 0x38, 0xff7e1880},
- {"RTL-8168d/8111d", 0x28, 0xff7e1880},
- {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880},
- {"RTL-8168/8111g", 0x4c, 0xff7e1880,},
- {"RTL-8101e", 0x34, 0xff7e1880},
- {"RTL-8100e", 0x32, 0xff7e1880},
-};
-
-static void rtl8169_chip_identify(struct rtl8169_priv *priv)
-{
- u32 val;
- int i;
-
- val = RTL_R32(priv, TxConfig);
- val = ((val & 0x7c000000) + ((val & 0x00800000) << 2)) >> 24;
-
- for (i = ARRAY_SIZE(chip_info) - 1; i >= 0; i--){
- if (val == chip_info[i].version) {
- priv->chipset = i;
- dev_dbg(&priv->pci_dev->dev, "found %s chipset\n",
- chip_info[i].name);
- return;
- }
- }
-
- dev_dbg(&priv->pci_dev->dev,
- "no matching chip version found, assuming RTL-8169\n");
- priv->chipset = 0;
-}
-
-static int rtl8169_init_dev(struct eth_device *edev)
-{
- struct rtl8169_priv *priv = edev->priv;
-
- rtl8169_chip_reset(priv);
- rtl8169_chip_identify(priv);
- pci_set_master(priv->pci_dev);
-
- return 0;
-}
-
-static void __set_rx_mode(struct rtl8169_priv *priv)
-{
- u32 mc_filter[2], val;
-
- /* IFF_ALLMULTI */
- /* Too many to filter perfectly -- accept all multicasts. */
- mc_filter[1] = mc_filter[0] = 0xffffffff;
-
- val = AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
- rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
- chip_info[priv->chipset].RxConfigMask);
-
- RTL_W32(priv, RxConfig, val);
- RTL_W32(priv, MAR0 + 0, mc_filter[0]);
- RTL_W32(priv, MAR0 + 4, mc_filter[1]);
-}
-
-static void rtl8169_init_ring(struct rtl8169_priv *priv)
-{
- int i;
-
- priv->cur_rx = priv->cur_tx = 0;
-
- priv->tx_desc = dma_alloc_coherent(NUM_TX_DESC * sizeof(struct bufdesc),
- &priv->tx_desc_phys);
- priv->tx_buf = malloc(NUM_TX_DESC * PKT_BUF_SIZE);
- priv->tx_buf_phys = dma_map_single(&priv->edev.dev, priv->tx_buf,
- NUM_TX_DESC * PKT_BUF_SIZE, DMA_TO_DEVICE);
-
- priv->rx_desc = dma_alloc_coherent(NUM_RX_DESC * sizeof(struct bufdesc),
- &priv->rx_desc_phys);
- priv->rx_buf = malloc(NUM_RX_DESC * PKT_BUF_SIZE);
- priv->rx_buf_phys = dma_map_single(&priv->edev.dev, priv->rx_buf,
- NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE);
-
- for (i = 0; i < NUM_RX_DESC; i++) {
- if (i == (NUM_RX_DESC - 1))
- priv->rx_desc[i].status =
- cpu_to_le32(BD_STAT_OWN | BD_STAT_EOR | PKT_BUF_SIZE);
- else
- priv->rx_desc[i].status =
- cpu_to_le32(BD_STAT_OWN | PKT_BUF_SIZE);
-
- priv->rx_desc[i].buf_addr =
- cpu_to_le32(priv->rx_buf_phys + i * PKT_BUF_SIZE);
- }
-}
-
-static void rtl8169_hw_start(struct rtl8169_priv *priv)
-{
- u32 val;
-
- RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
-
- /* RTL-8169sb/8110sb or previous version */
- if (priv->chipset <= 5)
- RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);
-
- RTL_W8(priv, EarlyTxThres, 0x3f);
-
- /* For gigabit rtl8169 */
- RTL_W16(priv, RxMaxSize, 0x800);
-
- /* Set Rx Config register */
- val = rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
- chip_info[priv->chipset].RxConfigMask);
- RTL_W32(priv, RxConfig, val);
-
- /* Set DMA burst size and Interframe Gap Time */
- RTL_W32(priv, TxConfig, (6 << TxDMAShift) | (3 << TxInterFrameGapShift));
-
- RTL_W32(priv, TxDescStartAddrLow, priv->tx_desc_phys);
- RTL_W32(priv, TxDescStartAddrHigh, 0);
- RTL_W32(priv, RxDescStartAddrLow, priv->rx_desc_phys);
- RTL_W32(priv, RxDescStartAddrHigh, 0);
-
- /* RTL-8169sc/8110sc or later version */
- if (priv->chipset > 5)
- RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);
-
- RTL_W8(priv, Cfg9346, Cfg9346_Lock);
- udelay(10);
-
- RTL_W32(priv, RxMissed, 0);
-
- __set_rx_mode(priv);
-
- /* no early-rx interrupts */
- RTL_W16(priv, MultiIntr, RTL_R16(priv, MultiIntr) & 0xf000);
-}
-
-static int rtl8169_eth_open(struct eth_device *edev)
-{
- struct rtl8169_priv *priv = edev->priv;
- int ret;
-
- pci_set_master(priv->pci_dev);
-
- rtl8169_init_ring(priv);
- rtl8169_hw_start(priv);
-
- ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0,
- PHY_INTERFACE_MODE_NA);
-
- return ret;
-}
-
-static int rtl8169_phy_write(struct mii_bus *bus, int phy_addr,
- int reg, u16 val)
-{
- struct rtl8169_priv *priv = bus->priv;
- int i;
-
- if (phy_addr != 0)
- return -1;
-
- RTL_W32(priv, PHYAR, 0x80000000 | (reg & 0xff) << 16 | val);
- mdelay(1);
-
- for (i = 2000; i > 0; i--) {
- if (!(RTL_R32(priv, PHYAR) & 0x80000000)) {
- return 0;
- } else {
- udelay(100);
- }
- }
-
- return -1;
-}
-
-static int rtl8169_phy_read(struct mii_bus *bus, int phy_addr, int reg)
-{
- struct rtl8169_priv *priv = bus->priv;
- int i, val = 0xffff;
-
- RTL_W32(priv, PHYAR, 0x0 | (reg & 0xff) << 16);
- mdelay(10);
-
- if (phy_addr != 0)
- return val;
-
- for (i = 2000; i > 0; i--) {
- if (RTL_R32(priv, PHYAR) & 0x80000000) {
- val = (int) (RTL_R32(priv, PHYAR) & 0xffff);
- break;
- } else {
- udelay(100);
- }
- }
- return val;
-}
-
-static int rtl8169_eth_send(struct eth_device *edev, void *packet,
- int packet_length)
-{
- struct rtl8169_priv *priv = edev->priv;
- unsigned int entry;
-
- entry = priv->cur_tx % NUM_TX_DESC;
-
- if (packet_length < ETH_ZLEN)
- memset(priv->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN);
- memcpy(priv->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length);
- dma_sync_single_for_device(priv->tx_buf_phys + entry *
- PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE);
-
- priv->tx_desc[entry].buf_Haddr = 0;
- priv->tx_desc[entry].buf_addr =
- cpu_to_le32(priv->tx_buf_phys + entry * PKT_BUF_SIZE);
-
- if (entry != (NUM_TX_DESC - 1)) {
- priv->tx_desc[entry].status =
- cpu_to_le32(BD_STAT_OWN | BD_STAT_FS | BD_STAT_LS |
- ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN));
- } else {
- priv->tx_desc[entry].status =
- cpu_to_le32(BD_STAT_OWN | BD_STAT_EOR | BD_STAT_FS | BD_STAT_LS |
- ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN));
- }
-
- RTL_W8(priv, TxPoll, 0x40);
-
- while (le32_to_cpu(priv->tx_desc[entry].status) & BD_STAT_OWN)
- ;
-
- dma_sync_single_for_cpu(priv->tx_buf_phys + entry * PKT_BUF_SIZE,
- PKT_BUF_SIZE, DMA_TO_DEVICE);
-
- priv->cur_tx++;
-
- return 0;
-}
-
-static int rtl8169_eth_rx(struct eth_device *edev)
-{
- struct rtl8169_priv *priv = edev->priv;
- unsigned int entry, pkt_size = 0;
- u8 status;
-
- entry = priv->cur_rx % NUM_RX_DESC;
-
- if ((le32_to_cpu(priv->rx_desc[entry].status) & BD_STAT_OWN) == 0) {
- if (!(le32_to_cpu(priv->rx_desc[entry].status) & BD_STAT_RX_RES)) {
- pkt_size = (le32_to_cpu(priv->rx_desc[entry].status) & 0x1fff) - 4;
-
- dma_sync_single_for_cpu(priv->rx_buf_phys + entry * PKT_BUF_SIZE,
- pkt_size, DMA_FROM_DEVICE);
-
- net_receive(edev, priv->rx_buf + entry * PKT_BUF_SIZE,
- pkt_size);
-
- dma_sync_single_for_device(priv->rx_buf_phys + entry * PKT_BUF_SIZE,
- pkt_size, DMA_FROM_DEVICE);
-
- if (entry == NUM_RX_DESC - 1)
- priv->rx_desc[entry].status = cpu_to_le32(BD_STAT_OWN |
- BD_STAT_EOR | PKT_BUF_SIZE);
- else
- priv->rx_desc[entry].status =
- cpu_to_le32(BD_STAT_OWN | PKT_BUF_SIZE);
- priv->rx_desc[entry].buf_addr =
- cpu_to_le32(priv->rx_buf_phys +
- entry * PKT_BUF_SIZE);
- } else {
- dev_err(&edev->dev, "rx error\n");
- }
-
- priv->cur_rx++;
-
- return pkt_size;
-
- } else {
- status = RTL_R8(priv, IntrStatus);
- RTL_W8(priv, IntrStatus, status & ~(TxErr | RxErr | SYSErr));
- udelay(100); /* wait */
- }
-
- return 0;
-}
-
-static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *m)
-{
- struct rtl8169_priv *priv = edev->priv;
- int i;
-
- for (i = 0; i < 6; i++) {
- m[i] = RTL_R8(priv, MAC0 + i);
- }
-
- return 0;
-}
-
-static int rtl8169_set_ethaddr(struct eth_device *edev, const unsigned char *mac_addr)
-{
- struct rtl8169_priv *priv = edev->priv;
- int i;
-
- RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
-
- for (i = 0; i < 6; i++) {
- RTL_W8(priv, (MAC0 + i), mac_addr[i]);
- RTL_R8(priv, mac_addr[i]);
- }
-
- RTL_W8(priv, Cfg9346, Cfg9346_Lock);
-
- return 0;
-}
-
-static void rtl8169_eth_halt(struct eth_device *edev)
-{
- struct rtl8169_priv *priv = edev->priv;
-
- /* Stop the chip's Tx and Rx DMA processes. */
- RTL_W8(priv, ChipCmd, 0x00);
-
- /* Disable interrupts by clearing the interrupt mask. */
- RTL_W16(priv, IntrMask, 0x0000);
- RTL_W32(priv, RxMissed, 0);
-
- pci_clear_master(priv->pci_dev);
-
- dma_unmap_single(&edev->dev, priv->tx_buf_phys, NUM_TX_DESC * PKT_BUF_SIZE,
- DMA_TO_DEVICE);
- free(priv->tx_buf);
- dma_free_coherent((void *)priv->tx_desc, priv->tx_desc_phys,
- NUM_TX_DESC * sizeof(struct bufdesc));
-
- dma_unmap_single(&edev->dev, priv->rx_buf_phys, NUM_RX_DESC * PKT_BUF_SIZE,
- DMA_FROM_DEVICE);
- free(priv->rx_buf);
- dma_free_coherent((void *)priv->rx_desc, priv->rx_desc_phys,
- NUM_RX_DESC * sizeof(struct bufdesc));
-}
-
-static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct device_d *dev = &pdev->dev;
- struct eth_device *edev;
- struct rtl8169_priv *priv;
- int ret;
-
- /* enable pci device */
- pci_enable_device(pdev);
-
- priv = xzalloc(sizeof(struct rtl8169_priv));
-
- edev = &priv->edev;
- dev->type_data = edev;
- edev->priv = priv;
-
- priv->pci_dev = pdev;
-
- priv->miibus.read = rtl8169_phy_read;
- priv->miibus.write = rtl8169_phy_write;
- priv->miibus.priv = priv;
- priv->miibus.parent = &edev->dev;
-
- priv->base = pci_iomap(pdev, pdev->device == 0x8168 ? 2 : 1);
-
- dev_dbg(dev, "rtl%04x (rev %02x) (base=%p)\n",
- pdev->device, pdev->revision, priv->base);
-
- edev->init = rtl8169_init_dev;
- edev->open = rtl8169_eth_open;
- edev->send = rtl8169_eth_send;
- edev->recv = rtl8169_eth_rx;
- edev->get_ethaddr = rtl8169_get_ethaddr;
- edev->set_ethaddr = rtl8169_set_ethaddr;
- edev->halt = rtl8169_eth_halt;
- edev->parent = dev;
- ret = eth_register(edev);
- if (ret)
- goto eth_err;
-
- ret = mdiobus_register(&priv->miibus);
- if (ret)
- goto mdio_err;
-
- return 0;
-
-mdio_err:
- eth_unregister(edev);
-
-eth_err:
- free(priv);
-
- return ret;
-}
-static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = {
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), },
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), },
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), },
- { /* sentinel */ }
-};
-
-static struct pci_driver rtl8169_eth_driver = {
- .name = "rtl8169_eth",
- .id_table = rtl8169_pci_tbl,
- .probe = rtl8169_probe,
-};
-device_pci_driver(rtl8169_eth_driver);
diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c
index e1161df31f..d88a5e2fcf 100644
--- a/drivers/net/sja1105.c
+++ b/drivers/net/sja1105.c
@@ -10,7 +10,7 @@
#include <common.h>
#include <dsa.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#include <linux/bitrev.h>
#include <linux/if_vlan.h>
#include <net.h>
@@ -297,7 +297,7 @@ struct sja1105_private {
u16 pvid[SJA1105_MAX_NUM_PORTS];
struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS];
const struct sja1105_dcfg *dcfg;
- struct device_d *dev;
+ struct device *dev;
struct dsa_switch ds;
struct spi_device *spidev;
size_t max_xfer_len;
@@ -1432,7 +1432,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
.top = {0x1FF, 0, 0, 0, 0, 0, 0},
.base = {0x0, 0, 0, 0, 0, 0, 0, 0},
.enabled = {1, 0, 0, 0, 0, 0, 0, 0},
- /* Will be overridden in sja1105_port_enable. */
+ /* Will be overridden in sja1105_adjust_link. */
.speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO],
.egress = true,
.ingress = true,
@@ -1963,7 +1963,7 @@ static int sja1105_rgmii_clocking_setup(struct sja1105_private *priv, int port,
enum sja1105_mii_role role)
{
struct sja1105_mac_config_entry *mac;
- struct device_d *dev = priv->dev;
+ struct device *dev = priv->dev;
u64 speed;
int rc = -EINVAL;
@@ -2660,7 +2660,7 @@ static int sja1105_static_config_reload(struct sja1105_private *priv)
static int sja1105_port_set_mode(struct dsa_port *dp, int port,
phy_interface_t phy_mode)
{
- struct device_d *dev = dp->ds->dev;
+ struct device *dev = dp->ds->dev;
struct sja1105_private *priv = dev_get_priv(dev);
struct sja1105_xmii_params_entry *mii;
@@ -2716,7 +2716,7 @@ unsupported:
static int sja1105_port_pre_enable(struct dsa_port *dp, int port,
phy_interface_t phy_mode)
{
- struct device_d *dev = dp->ds->dev;
+ struct device *dev = dp->ds->dev;
struct sja1105_private *priv = dev_get_priv(dev);
int ret;
@@ -2727,14 +2727,16 @@ static int sja1105_port_pre_enable(struct dsa_port *dp, int port,
return sja1105_static_config_reload(priv);
}
-static int sja1105_port_enable(struct dsa_port *dp, int port,
- struct phy_device *phy)
+static void sja1105_adjust_link(struct eth_device *edev)
{
- struct device_d *dev = dp->ds->dev;
+ struct dsa_port *dp = edev->priv;
+ struct device *dev = dp->ds->dev;
struct sja1105_private *priv = dev_get_priv(dev);
+ struct phy_device *phy = dp->edev.phydev;
phy_interface_t phy_mode = phy->interface;
struct sja1105_xmii_params_entry *mii;
struct sja1105_mac_config_entry *mac;
+ int port = dp->index;
int ret;
mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries;
@@ -2742,7 +2744,7 @@ static int sja1105_port_enable(struct dsa_port *dp, int port,
ret = sja1105_port_set_mode(dp, port, phy_mode);
if (ret)
- return ret;
+ goto error;
/* Let the PHY handle the RGMII delays, if present. */
if (phy->phy_id == 0) {
@@ -2758,7 +2760,7 @@ static int sja1105_port_enable(struct dsa_port *dp, int port,
priv->rgmii_tx_delay[port]) &&
!priv->dcfg->setup_rgmii_delay) {
dev_err(priv->dev, "Chip does not support internal RGMII delays\n");
- return -EINVAL;
+ return;
}
}
@@ -2776,12 +2778,19 @@ static int sja1105_port_enable(struct dsa_port *dp, int port,
mac[port].speed =
priv->dcfg->port_speed[SJA1105_SPEED_10MBPS];
} else {
- dev_err(priv->dev, "Invalid PHY speed %d on port %d\n",
- phy->speed, port);
- return -EINVAL;
+ mac[port].speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO];
+ return;
}
- return sja1105_static_config_reload(priv);
+ ret = sja1105_static_config_reload(priv);
+ if (ret)
+ goto error;
+
+ return;
+
+error:
+ dev_err(priv->dev, "Failed to adjust link on port %d, error %pe\n",
+ port, ERR_PTR(ret));
}
static int sja1105_xmit(struct dsa_port *dp, int port, void *packet, int length)
@@ -2814,9 +2823,9 @@ static int sja1105_rcv(struct dsa_switch *ds, int *port, void *packet,
return 0;
}
-static const struct dsa_ops sja1105_dsa_ops = {
+static const struct dsa_switch_ops sja1105_dsa_ops = {
.port_pre_enable = sja1105_port_pre_enable,
- .port_enable = sja1105_port_enable,
+ .adjust_link = sja1105_adjust_link,
.xmit = sja1105_xmit,
.rcv = sja1105_rcv,
};
@@ -2871,26 +2880,27 @@ static int sja1105_check_device_id(struct sja1105_private *priv)
}
/* Configure the optional reset pin and bring up switch */
-static int sja1105_hw_reset(struct device_d *dev, unsigned int pulse_len,
+static int sja1105_hw_reset(struct device *dev, unsigned int pulse_len,
unsigned int startup_delay)
{
- int gpio;
-
- gpio = gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (gpio < 0)
- return 0;
-
- gpiod_set_value(gpio, 1);
- /* Wait for minimum reset pulse length */
- mdelay(pulse_len);
- gpiod_set_value(gpio, 0);
- /* Wait until chip is ready after reset */
- mdelay(startup_delay);
+ struct gpio_desc *gpio;
+
+ gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio)) {
+ dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n");
+ } else if (gpio) {
+ gpiod_set_value(gpio, 1);
+ /* Wait for minimum reset pulse length */
+ mdelay(pulse_len);
+ gpiod_set_value(gpio, 0);
+ /* Wait until chip is ready after reset */
+ mdelay(startup_delay);
+ }
return 0;
}
-static int sja1105_probe(struct device_d *dev)
+static int sja1105_probe(struct device *dev)
{
struct spi_device *spi = dev->type_data;
const struct sja1105_dcfg *dcfg;
@@ -2977,8 +2987,9 @@ static const struct of_device_id sja1105_ids[] = {
{ .compatible = "nxp,sja1110d", .data = &sja1110d_dcfg },
{ }
};
+MODULE_DEVICE_TABLE(of, sja1105_ids);
-static struct driver_d sja1105_driver = {
+static struct driver sja1105_driver = {
.name = "sja1105",
.probe = sja1105_probe,
.of_compatible = DRV_OF_COMPAT(sja1105_ids),
diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c
index d503535c25..4bbb2a3dee 100644
--- a/drivers/net/smc91111.c
+++ b/drivers/net/smc91111.c
@@ -440,12 +440,12 @@ struct smc91c111_priv {
struct mii_bus miibus;
struct accessors a;
void __iomem *base;
- int qemu_fixup;
unsigned shift;
int version;
int revision;
unsigned int control_setup;
unsigned int config_setup;
+ void *rx_buf;
};
#if (SMC_DEBUG > 2 )
@@ -1047,7 +1047,8 @@ static int smc91c111_eth_open(struct eth_device *edev)
if (ret)
return ret;
- if (priv->qemu_fixup && edev->phydev->phy_id == 0x00000000) {
+ if (of_machine_is_compatible("arm,versatile-pb") ||
+ of_machine_is_compatible("arm,versatile-ab")) {
struct phy_device *dev = edev->phydev;
dev->speed = SPEED_100;
@@ -1302,14 +1303,14 @@ static int smc91c111_eth_rx(struct eth_device *edev)
to send the DWORDs or the bytes first, or some
mixture. A mixture might improve already slow PIO
performance */
- SMC_insl(priv, SMC91111_DATA_REG , NetRxPackets[0],
+ SMC_insl(priv, SMC91111_DATA_REG , priv->rx_buf,
packet_length >> 2);
/* read the left over bytes */
if (packet_length & 3) {
int i;
unsigned char *tail =
- (unsigned char *)(NetRxPackets[0] +
+ (unsigned char *)(priv->rx_buf +
(packet_length & ~3));
unsigned long leftover = SMC_inl(priv,
SMC91111_DATA_REG);
@@ -1320,7 +1321,7 @@ static int smc91c111_eth_rx(struct eth_device *edev)
#if SMC_DEBUG > 2
printf("Receiving Packet\n");
- print_packet( NetRxPackets[0], packet_length );
+ print_packet(priv->rx_buf, packet_length );
#endif
} else {
/* error ... */
@@ -1343,7 +1344,7 @@ static int smc91c111_eth_rx(struct eth_device *edev)
if (!is_error) {
/* Pass the packet up to the protocol layers. */
- net_receive(edev, NetRxPackets[0], packet_length);
+ net_receive(edev, priv->rx_buf, packet_length);
return 0;
}
@@ -1433,7 +1434,7 @@ static int smc91c111_init_dev(struct eth_device *edev)
return 0;
}
-static int smc91c111_probe(struct device_d *dev)
+static int smc91c111_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
@@ -1445,11 +1446,11 @@ static int smc91c111_probe(struct device_d *dev)
priv = edev->priv;
priv->a = access_via_32bit;
+ priv->rx_buf = xmalloc(PKTSIZE);
if (dev->platform_data) {
struct smc91c111_pdata *pdata = dev->platform_data;
- priv->qemu_fixup = pdata->qemu_fixup;
priv->shift = pdata->addr_shift;
if (pdata->bus_width == 16)
priv->a = access_via_16bit;
@@ -1487,8 +1488,18 @@ static int smc91c111_probe(struct device_d *dev)
return 0;
}
-static struct driver_d smc91c111_driver = {
- .name = "smc91c111",
- .probe = smc91c111_probe,
+static __maybe_unused struct of_device_id smc91c111_dt_ids[] = {
+ {
+ .compatible = "smsc,lan91c111",
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, smc91c111_dt_ids);
+MODULE_DEVICE_TABLE(of, smc91c111_dt_ids);
+
+static struct driver smc91c111_driver = {
+ .of_compatible = DRV_OF_COMPAT(smc91c111_dt_ids),
+ .name = "smc91c111",
+ .probe = smc91c111_probe,
};
device_platform_driver(smc91c111_driver);
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
index 1edc16ce44..767d51761b 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/smc911x.c
@@ -32,6 +32,8 @@ struct smc911x_priv {
unsigned int using_extphy;
unsigned int phy_mask;
+ void *rx_buf;
+
u32 (*reg_read)(struct smc911x_priv *priv, u32 reg);
void (*reg_write)(struct smc911x_priv *priv, u32 reg, u32 val);
};
@@ -447,7 +449,7 @@ static void smc911x_eth_halt(struct eth_device *edev)
static int smc911x_eth_rx(struct eth_device *edev)
{
struct smc911x_priv *priv = (struct smc911x_priv *)edev->priv;
- u32 *data = (u32 *)NetRxPackets[0];
+ u32 *data = priv->rx_buf;
u32 pktlen, tmplen;
u32 status;
@@ -465,7 +467,7 @@ static int smc911x_eth_rx(struct eth_device *edev)
dev_err(&edev->dev, "dropped bad packet. Status: 0x%08x\n",
status);
else
- net_receive(edev, NetRxPackets[0], pktlen);
+ net_receive(edev, priv->rx_buf, pktlen);
}
return 0;
@@ -479,7 +481,7 @@ static int smc911x_init_dev(struct eth_device *edev)
return 0;
}
-static int smc911x_probe(struct device_d *dev)
+static int smc911x_probe(struct device *dev)
{
struct resource *iores;
struct eth_device *edev;
@@ -503,18 +505,18 @@ static int smc911x_probe(struct device_d *dev)
priv->shift = pdata->shift;
priv->flags = pdata->flags;
priv->phy_mask = pdata->phy_mask;
- } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) {
- ret = of_property_read_u32(dev->device_node, "reg-io-width", &val);
+ } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) {
+ ret = of_property_read_u32(dev->of_node, "reg-io-width", &val);
if (ret)
return ret;
is_32bit = (val == 4);
- of_property_read_u32(dev->device_node, "reg-shift", &priv->shift);
+ of_property_read_u32(dev->of_node, "reg-shift", &priv->shift);
- if (of_property_read_bool(dev->device_node, "smsc,force-internal-phy"))
+ if (of_property_read_bool(dev->of_node, "smsc,force-internal-phy"))
priv->flags |= SMC911X_FORCE_INTERNAL_PHY;
- if (of_property_read_bool(dev->device_node, "smsc,force-external-phy"))
+ if (of_property_read_bool(dev->of_node, "smsc,force-external-phy"))
priv->flags |= SMC911X_FORCE_EXTERNAL_PHY;
}
@@ -608,6 +610,8 @@ static int smc911x_probe(struct device_d *dev)
dev_info(dev, "LAN911x identified, idrev: 0x%08X, generation: %d\n",
val, priv->generation);
+ priv->rx_buf = xmalloc(PKTSIZE);
+
edev = &priv->edev;
edev->priv = priv;
@@ -638,8 +642,9 @@ static const struct of_device_id smsc911x_dt_ids[] = {
{ .compatible = "smsc,lan9115", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, smsc911x_dt_ids);
-static struct driver_d smc911x_driver = {
+static struct driver smc911x_driver = {
.name = "smc911x",
.probe = smc911x_probe,
.of_compatible = DRV_OF_COMPAT(smsc911x_dt_ids),
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 1fbfa085b1..18f83c6a2c 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -15,6 +15,7 @@
struct tap_priv {
int fd;
char *name;
+ char *rx_buf;
};
static int tap_eth_send(struct eth_device *edev, void *packet, int length)
@@ -30,10 +31,10 @@ static int tap_eth_rx(struct eth_device *edev)
struct tap_priv *priv = edev->priv;
int length;
- length = linux_read_nonblock(priv->fd, NetRxPackets[0], PKTSIZE);
+ length = linux_read_nonblock(priv->fd, priv->rx_buf, PKTSIZE);
if (length > 0)
- net_receive(edev, NetRxPackets[0], length);
+ net_receive(edev, priv->rx_buf, length);
return 0;
}
@@ -58,7 +59,7 @@ static int tap_set_ethaddr(struct eth_device *edev, const unsigned char *adr)
return 0;
}
-static int tap_probe(struct device_d *dev)
+static int tap_probe(struct device *dev)
{
struct eth_device *edev;
struct tap_priv *priv;
@@ -73,6 +74,8 @@ static int tap_probe(struct device_d *dev)
goto out;
}
+ priv->rx_buf = xmalloc(PKTSIZE);
+
edev = xzalloc(sizeof(struct eth_device));
edev->priv = priv;
edev->parent = dev;
@@ -94,7 +97,7 @@ out:
return ret;
}
-static struct driver_d tap_driver = {
+static struct driver tap_driver = {
.name = "tap",
.probe = tap_probe,
};
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 870f24ca96..6dc6a24aee 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -1,12 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig NET_USB
depends on USB_HOST
+ select PHYLIB
bool "USB network support"
if NET_USB
config NET_USB_ASIX
- select PHYLIB
bool "Asix compatible"
config USB_NET_AX88179_178A
@@ -21,7 +21,6 @@ config USB_NET_AX88179_178A
* Sitcomm LN-032
config NET_USB_SMSC95XX
- select PHYLIB
bool "SMSC95xx"
config NET_USB_RTL8152
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index 28a3534bcf..8ee0bc8534 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -3,8 +3,8 @@
#include <init.h>
#include <net.h>
#include <linux/phy.h>
-#include <usb/usb.h>
-#include <usb/usbnet.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usbnet.h>
#include <errno.h>
#include <malloc.h>
#include <asm/byteorder.h>
@@ -175,7 +175,7 @@ static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
dev_dbg(&dev->edev.dev, "asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n",
cmd, value, index, size);
- buf = malloc(size);
+ buf = dma_alloc(size);
if (!buf)
goto out;
@@ -193,7 +193,7 @@ static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
memcpy(data, buf, size);
else if (err >= 0)
err = -EINVAL;
- free(buf);
+ dma_free(buf);
out:
return err;
@@ -209,7 +209,7 @@ static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
cmd, value, index, size);
if (data) {
- buf = malloc(size);
+ buf = dma_alloc(size);
if (!buf)
goto out;
memcpy(buf, data, size);
@@ -225,7 +225,7 @@ static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
buf,
size,
USB_CTRL_SET_TIMEOUT);
- free(buf);
+ dma_free(buf);
out:
return err;
@@ -427,6 +427,25 @@ static int asix_set_ethaddr(struct eth_device *edev, const unsigned char *adr)
return 0;
}
+static int asix_set_promisc(struct eth_device *edev, bool enable)
+{
+ struct usbnet *dev = container_of(edev, struct usbnet, edev);
+ u16 rx_ctl;
+ int ret;
+
+ rx_ctl = asix_read_rx_ctl(dev);
+
+ if (enable)
+ rx_ctl |= AX_RX_CTL_PRO;
+ else
+ rx_ctl &= ~AX_RX_CTL_PRO;
+
+ if ((ret = asix_write_rx_ctl(dev, rx_ctl)) < 0)
+ return ret;
+
+ return 0;
+}
+
static int ax88172_get_ethaddr(struct eth_device *edev, unsigned char *adr)
{
struct usbnet *udev = container_of(edev, struct usbnet, edev);
@@ -664,6 +683,7 @@ static int ax88772_bind(struct usbnet *dev)
dev->edev.get_ethaddr = asix_get_ethaddr;
dev->edev.set_ethaddr = asix_set_ethaddr;
+ dev->edev.set_promisc = asix_set_promisc;
asix_init_mii(dev);
if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index fddb187144..067b0c2f9e 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -8,8 +8,8 @@
#include <init.h>
#include <net.h>
#include <linux/phy.h>
-#include <usb/usb.h>
-#include <usb/usbnet.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usbnet.h>
#include <errno.h>
#include <malloc.h>
#include <poller.h>
@@ -469,7 +469,7 @@ static int ax88179_rx_fixup(struct usbnet *dev, void *buf, int len)
__func__, frame_pos, pkt_len);
net_receive(&dev->edev, buf + frame_pos, pkt_len);
-
+
pkt_hdr++;
frame_pos += ((pkt_len + 7) & 0xfff8) - 2;
}
@@ -724,6 +724,10 @@ static const struct usb_device_id products[] = {
USB_DEVICE(0x0df6, 0x0072),
.driver_info = &sitecom_info,
}, {
+ /* Sitecom USB 3.0 to Gigabit Adapter */
+ USB_DEVICE(0x0df6, 0x0056),
+ .driver_info = &sitecom_info,
+}, {
/* Samsung USB Ethernet Adapter */
USB_DEVICE(0x04e8, 0xa100),
.driver_info = &samsung_info,
@@ -735,6 +739,10 @@ static const struct usb_device_id products[] = {
/* Belkin B2B128 USB 3.0 Hub + Gigabit Ethernet Adapter */
USB_DEVICE(0x050d, 0x0128),
.driver_info = &belkin_info,
+}, {
+ /* Belkin Gigabit Ethernet Adapter */
+ USB_DEVICE(0x050d, 0x5055),
+ .driver_info = &belkin_info,
},
{ },
};
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 0928887784..2511c524cd 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -8,8 +8,8 @@
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/phy.h>
-#include <usb/usb.h>
-#include <usb/usbnet.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usbnet.h>
#include "r8152.h"
#define R8152_TX_BURST_SIZE 512
diff --git a/drivers/net/usb/r8152_fw.c b/drivers/net/usb/r8152_fw.c
index ee5f7c48e4..a6d61ac0d6 100644
--- a/drivers/net/usb/r8152_fw.c
+++ b/drivers/net/usb/r8152_fw.c
@@ -2,8 +2,8 @@
/* Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. */
#include <common.h>
-#include <usb/usb.h>
-#include <usb/usbnet.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usbnet.h>
#include "r8152.h"
static const u8 r8152b_pla_patch_a[] = {
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 3c5bd1e4ee..00267f282c 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -5,8 +5,9 @@
#include <command.h>
#include <init.h>
#include <net.h>
-#include <usb/usb.h>
-#include <usb/usbnet.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usbnet.h>
+#include <asm/unaligned.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <errno.h>
@@ -23,8 +24,6 @@
#define MAX_SINGLE_PACKET_SIZE (2048)
#define LAN95XX_EEPROM_MAGIC (0x9500)
#define EEPROM_MAC_OFFSET (0x01)
-#define DEFAULT_TX_CSUM_ENABLE (1)
-#define DEFAULT_RX_CSUM_ENABLE (1)
#define SMSC95XX_INTERNAL_PHY_ID (1)
#define SMSC95XX_TX_OVERHEAD (8)
#define SMSC95XX_TX_OVERHEAD_CSUM (12)
@@ -45,25 +44,25 @@
struct smsc95xx_priv {
u32 mac_cr;
- int use_tx_csum;
- int use_rx_csum;
+ __le32 *iobuf;
};
static int turbo_mode = 0;
static int smsc95xx_read_reg(struct usbnet *dev, u32 index, u32 *data)
{
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
int ret;
ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
USB_VENDOR_REQUEST_READ_REGISTER,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 00, index, data, 4, USB_CTRL_GET_TIMEOUT);
+ 00, index, pdata->iobuf, 4, USB_CTRL_GET_TIMEOUT);
if (ret < 0)
netdev_warn(dev->net, "Failed to read register index 0x%08x\n", index);
-
- le32_to_cpus(data);
+ else
+ *data = le32_to_cpup(pdata->iobuf);
debug("%s: 0x%08x 0x%08x\n", __func__, index, *data);
@@ -72,14 +71,15 @@ static int smsc95xx_read_reg(struct usbnet *dev, u32 index, u32 *data)
static int smsc95xx_write_reg(struct usbnet *dev, u32 index, u32 data)
{
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
int ret;
- cpu_to_le32s(&data);
+ *pdata->iobuf = cpu_to_le32(data);
ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
USB_VENDOR_REQUEST_WRITE_REGISTER,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 00, index, &data, 4, USB_CTRL_SET_TIMEOUT);
+ 00, index, pdata->iobuf, 4, USB_CTRL_SET_TIMEOUT);
if (ret < 0)
netdev_warn(dev->net, "Failed to write register index 0x%08x\n", index);
@@ -319,10 +319,9 @@ static void smsc95xx_set_multicast(struct usbnet *dev)
smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr);
}
-/* Enable or disable Tx & Rx checksum offload engines */
-static int smsc95xx_set_csums(struct usbnet *dev)
+/* Disable Tx & Rx IP checksum offload engines */
+static int smsc95xx_disable_csums(struct usbnet *dev)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
u32 read_buf;
int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
if (ret < 0) {
@@ -330,15 +329,8 @@ static int smsc95xx_set_csums(struct usbnet *dev)
return ret;
}
- if (pdata->use_tx_csum)
- read_buf |= Tx_COE_EN_;
- else
- read_buf &= ~Tx_COE_EN_;
-
- if (pdata->use_rx_csum)
- read_buf |= Rx_COE_EN_;
- else
- read_buf &= ~Rx_COE_EN_;
+ read_buf &= ~Tx_COE_EN_;
+ read_buf &= ~Rx_COE_EN_;
ret = smsc95xx_write_reg(dev, COE_CR, read_buf);
if (ret < 0) {
@@ -669,7 +661,13 @@ static int smsc95xx_reset(struct usbnet *dev)
return ret;
}
- ret = smsc95xx_set_csums(dev);
+ /*
+ * barebox network stack doesn't care for hardware checksum offloading,
+ * so this enabling them doesn't help and indeed introduces breakage:
+ * The driver will be unaware of the two byte COE trailer and thus packet
+ * sizes reported will be 2 bytes more than what was actually transmitted.
+ */
+ ret = smsc95xx_disable_csums(dev);
if (ret < 0) {
netdev_warn(dev->net, "Failed to set csum offload: %d\n", ret);
return ret;
@@ -723,8 +721,7 @@ static int smsc95xx_bind(struct usbnet *dev)
return -ENOMEM;
}
- pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE;
- pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
+ pdata->iobuf = dma_alloc(4);
/* Init all registers */
ret = smsc95xx_reset(dev);
@@ -757,8 +754,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, void *buf, int len)
unsigned char *packet;
u16 size;
- memcpy(&header, buf, sizeof(header));
- le32_to_cpus(&header);
+ header = get_unaligned_le32(buf);
buf += 4 + NET_IP_ALIGN;
len -= 4 + NET_IP_ALIGN;
packet = buf;
@@ -767,6 +763,12 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, void *buf, int len)
size = (u16)((header & RX_STS_FL_) >> 16);
align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4;
+ if (unlikely(size > len)) {
+ netif_dbg(dev, rx_err, dev->net,
+ "size err header=0x%08x\n", header);
+ return 0;
+ }
+
if (header & RX_STS_ES_) {
netif_dbg(dev, rx_err, dev->net,
"Error header=0x%08x\n", header);
@@ -784,14 +786,17 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, void *buf, int len)
return 1;
}
- net_receive(&dev->edev, packet, len - 4);
+ net_receive(&dev->edev, packet, size - 4);
}
len -= size;
+ buf += size;
/* padding bytes before the next frame starts */
- if (len)
+ if (len) {
len -= align_count;
+ buf += align_count;
+ }
}
if (len < 0) {
@@ -801,28 +806,18 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, void *buf, int len)
return 1;
}
-#if 0
-static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)
-{
- int len = skb->data - skb->head;
- u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len);
- u16 low_16 = (u16)(skb->csum_start - len);
- return (high_16 << 16) | low_16;
-}
-#endif
+
static int smsc95xx_tx_fixup(struct usbnet *dev,
void *buf, int len,
void *nbuf, int *nlen)
{
u32 tx_cmd_a, tx_cmd_b;
- tx_cmd_a = (u32)(len) | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
- cpu_to_le32s(&tx_cmd_a);
- memcpy(nbuf, &tx_cmd_a, 4);
+ tx_cmd_b = (u32)len;
+ tx_cmd_a = tx_cmd_b | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
- tx_cmd_b = (u32)(len);
- cpu_to_le32s(&tx_cmd_b);
- memcpy(nbuf + 4, &tx_cmd_b, 4);
+ put_unaligned_le32(tx_cmd_a, nbuf);
+ put_unaligned_le32(tx_cmd_b, nbuf + 4);
memcpy(nbuf + 8, buf, len);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 4818079523..3c3da3171b 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
-#include <usb/usb.h>
-#include <usb/usbnet.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usbnet.h>
#include <asm/byteorder.h>
#include <errno.h>
#include <malloc.h>
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 4966fc199d..33a592caeb 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -361,6 +361,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
ns->blk.dev = ctrl->dev;
ns->blk.ops = &nvme_block_device_ops;
+ ns->blk.type = BLK_TYPE_NVME;
ns->blk.cdev.name = strdup(disk_name);
__nvme_revalidate_disk(&ns->blk, id);
@@ -601,7 +602,7 @@ EXPORT_SYMBOL_GPL(nvme_init_identify);
* earliest initialization so that we have the initialized structured around
* during probing.
*/
-int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device_d *dev,
+int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
const struct nvme_ctrl_ops *ops)
{
static int instance = 0;
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 80c988632f..90121bd7a3 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -30,7 +30,7 @@ struct nvme_request {
struct nvme_ctrl {
const struct nvme_ctrl_ops *ops;
- struct device_d *dev;
+ struct device *dev;
int instance;
u32 ctrl_config;
@@ -104,7 +104,7 @@ static inline void nvme_end_request(struct nvme_request *rq, __le16 status,
int nvme_disable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl);
-int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device_d *dev,
+int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
const struct nvme_ctrl_ops *ops);
void nvme_start_ctrl(struct nvme_ctrl *ctrl);
int nvme_init_identify(struct nvme_ctrl *ctrl);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index e513deff25..68280fe4a8 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -46,7 +46,7 @@ struct nvme_queue {
struct nvme_dev {
struct nvme_queue queues[NVME_QID_NUM];
u32 __iomem *dbs;
- struct device_d *dev;
+ struct device *dev;
unsigned online_queues;
unsigned max_qid;
int q_depth;
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 2e60734156..255198b2ad 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -44,6 +44,13 @@ config IMX_OCOTP_WRITE
mw -l -d /dev/imx-ocotp 0x8C 0x00001234
mw -l -d /dev/imx-ocotp 0x88 0x56789ABC
+config IMX_OCOTP_ELE
+ tristate "i.MX9 On Chip OTP controller"
+ depends on ARCH_IMX93
+ depends on OFDEVICE
+ help
+ This adds support for the i.MX9 On-Chip OTP controller.
+
config RAVE_SP_EEPROM
tristate "Rave SP EEPROM Support"
depends on RAVE_SP_CORE
@@ -58,6 +65,13 @@ config EEPROM_93XX46
supports both read and write commands and also the command to
erase the whole EEPROM.
+config NVMEM_ROCKCHIP_OTP
+ tristate "Rockchip OTP controller support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ This is a simple driver to dump specified values of Rockchip SoC
+ from otp, such as cpu-leakage, id etc.
+
config STM32_BSEC
tristate "STM32 Boot and security and OTP control"
depends on ARCH_STM32MP
@@ -76,6 +90,12 @@ config STM32_BSEC_WRITE
mw -l -d /dev/stm32-bsec 0x000000e4+4 0x78563412
mw -l -d /dev/stm32-bsec 0x000000e8+4 0x0000bc9a
+config STM32_BSEC_OPTEE_TA
+ def_bool STM32_BSEC && OPTEE
+ help
+ Say y here to enable the accesses to STM32MP SoC OTPs by the OP-TEE
+ trusted application STM32MP BSEC.
+
config KVX_OTP_NV
tristate "kalray KVX OTP Non volatile regs Support"
depends on KVX
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index 62a1f925dd..31db05e5a7 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -23,8 +23,11 @@ nvmem_eeprom_93xx46-y := eeprom_93xx46.o
obj-$(CONFIG_STM32_BSEC) += nvmem_bsec.o
nvmem_bsec-y := bsec.o
+nvmem_bsec-$(CONFIG_STM32_BSEC_OPTEE_TA) += stm32-bsec-optee-ta.o
obj-$(CONFIG_KVX_OTP_NV) += nvmem-kvx-otp-nv.o
nvmem-kvx-otp-nv-y := kvx-otp-nv.o
+obj-$(CONFIG_NVMEM_ROCKCHIP_OTP)+= rockchip-otp.o
obj-$(CONFIG_STARFIVE_OTP) += starfive-otp.o
+obj-$(CONFIG_IMX_OCOTP_ELE) += imx-ocotp-ele.o
diff --git a/drivers/nvmem/bsec.c b/drivers/nvmem/bsec.c
index 86b943a45d..22e30c6c2e 100644
--- a/drivers/nvmem/bsec.c
+++ b/drivers/nvmem/bsec.c
@@ -13,29 +13,33 @@
#include <net.h>
#include <io.h>
#include <of.h>
-#include <regmap.h>
-#include <mach/bsec.h>
+#include <linux/regmap.h>
+#include <mach/stm32mp/bsec.h>
#include <machine_id.h>
#include <linux/nvmem-provider.h>
+#include "stm32-bsec-optee-ta.h"
+
#define BSEC_OTP_SERIAL 13
struct bsec_priv {
- struct device_d dev;
- u32 svc_id;
+ struct device dev;
struct regmap_config map_config;
int permanent_write_enable;
+ u8 lower;
+ struct tee_context *ctx;
};
struct stm32_bsec_data {
- unsigned long svc_id;
- int num_regs;
+ size_t size;
+ u8 lower;
+ bool ta;
};
-static int bsec_smc(struct bsec_priv *priv, enum bsec_op op, u32 field,
+static int bsec_smc(enum bsec_op op, u32 field,
unsigned data2, unsigned *val)
{
- enum bsec_smc ret = stm32mp_smc(priv->svc_id, op, field / 4, data2, val);
+ enum bsec_smc ret = stm32mp_smc(STM32_SMC_BSEC, op, field / 4, data2, val);
switch(ret)
{
case BSEC_SMC_OK:
@@ -58,7 +62,7 @@ static int bsec_smc(struct bsec_priv *priv, enum bsec_op op, u32 field,
static int stm32_bsec_read_shadow(void *ctx, unsigned reg, unsigned *val)
{
- return bsec_smc(ctx, BSEC_SMC_READ_SHADOW, reg, 0, val);
+ return bsec_smc(BSEC_SMC_READ_SHADOW, reg, 0, val);
}
static int stm32_bsec_reg_write(void *ctx, unsigned reg, unsigned val)
@@ -66,9 +70,9 @@ static int stm32_bsec_reg_write(void *ctx, unsigned reg, unsigned val)
struct bsec_priv *priv = ctx;
if (priv->permanent_write_enable)
- return bsec_smc(ctx, BSEC_SMC_PROG_OTP, reg, val, NULL);
+ return bsec_smc(BSEC_SMC_PROG_OTP, reg, val, NULL);
else
- return bsec_smc(ctx, BSEC_SMC_WRITE_SHADOW, reg, val, NULL);
+ return bsec_smc(BSEC_SMC_WRITE_SHADOW, reg, val, NULL);
}
static struct regmap_bus stm32_bsec_regmap_bus = {
@@ -82,7 +86,7 @@ static void stm32_bsec_set_unique_machine_id(struct regmap *map)
int ret;
ret = regmap_bulk_read(map, BSEC_OTP_SERIAL * 4,
- unique_id, sizeof(unique_id));
+ unique_id, sizeof(unique_id) / 4);
if (ret)
return;
@@ -94,13 +98,17 @@ static int stm32_bsec_read_mac(struct bsec_priv *priv, int offset, u8 *mac)
u32 val[2];
int ret;
- /* Some TF-A does not copy all of OTP into shadow registers, so make
- * sure we read the _real_ OTP bits here.
- */
- ret = bsec_smc(priv, BSEC_SMC_READ_OTP, offset * 4, 0, &val[0]);
- if (ret)
- return ret;
- ret = bsec_smc(priv, BSEC_SMC_READ_OTP, offset * 4 + 4, 0, &val[1]);
+ if (priv->ctx) {
+ ret = stm32_bsec_optee_ta_read(priv->ctx, offset * 4, val, sizeof(val));
+ } else {
+ /* Some TF-A does not copy all of OTP into shadow registers, so make
+ * sure we read the _real_ OTP bits here.
+ */
+ ret = bsec_smc(BSEC_SMC_READ_OTP, offset * 4, 0, &val[0]);
+ if (!ret)
+ ret = bsec_smc(BSEC_SMC_READ_OTP, offset * 4 + 4, 0, &val[1]);
+ }
+
if (ret)
return ret;
@@ -108,10 +116,10 @@ static int stm32_bsec_read_mac(struct bsec_priv *priv, int offset, u8 *mac)
return 0;
}
-static void stm32_bsec_init_dt(struct bsec_priv *priv, struct device_d *dev,
+static void stm32_bsec_init_dt(struct bsec_priv *priv, struct device *dev,
struct regmap *map)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
struct device_node *rnode;
u32 phandle, offset;
char mac[ETH_ALEN];
@@ -141,8 +149,56 @@ static void stm32_bsec_init_dt(struct bsec_priv *priv, struct device_d *dev,
of_eth_register_ethaddr(rnode, mac);
}
-static int stm32_bsec_probe(struct device_d *dev)
+static int stm32_bsec_pta_read(void *context, unsigned int offset, unsigned int *val)
+{
+ struct bsec_priv *priv = context;
+
+ return stm32_bsec_optee_ta_read(priv->ctx, offset, val, sizeof(val));
+}
+
+static int stm32_bsec_pta_write(void *context, unsigned int offset, unsigned int val)
+{
+ struct bsec_priv *priv = context;
+
+ if (!priv->permanent_write_enable)
+ return -EACCES;
+
+ return stm32_bsec_optee_ta_write(priv->ctx, priv->lower, offset, &val, sizeof(val));
+}
+
+static struct regmap_bus stm32_bsec_optee_regmap_bus = {
+ .reg_write = stm32_bsec_pta_write,
+ .reg_read = stm32_bsec_pta_read,
+};
+
+static bool stm32_bsec_smc_check(void)
+{
+ u32 val;
+ int ret;
+
+ /* check that the OP-TEE support the BSEC SMC (legacy mode) */
+ ret = bsec_smc(BSEC_SMC_READ_SHADOW, 0, 0, &val);
+
+ return !ret;
+}
+
+static bool optee_presence_check(void)
+{
+ struct device_node *np;
+ bool tee_detected = false;
+
+ /* check that the OP-TEE node is present and available. */
+ np = of_find_compatible_node(NULL, NULL, "linaro,optee-tz");
+ if (np && of_device_is_available(np))
+ tee_detected = true;
+ of_node_put(np);
+
+ return tee_detected;
+}
+
+static int stm32_bsec_probe(struct device *dev)
{
+ const struct regmap_bus *regmap_bus;
struct regmap *map;
struct bsec_priv *priv;
int ret = 0;
@@ -155,8 +211,6 @@ static int stm32_bsec_probe(struct device_d *dev)
priv = xzalloc(sizeof(*priv));
- priv->svc_id = data->svc_id;
-
dev_set_name(&priv->dev, "bsec");
priv->dev.parent = dev;
register_device(&priv->dev);
@@ -164,9 +218,28 @@ static int stm32_bsec_probe(struct device_d *dev)
priv->map_config.reg_bits = 32;
priv->map_config.val_bits = 32;
priv->map_config.reg_stride = 4;
- priv->map_config.max_register = data->num_regs;
+ priv->map_config.max_register = data->size - priv->map_config.reg_stride;
+
+ priv->lower = data->lower;
+
+ if (data->ta || optee_presence_check()) {
+ ret = stm32_bsec_optee_ta_open(&priv->ctx);
+ if (ret) {
+ /* wait for OP-TEE client driver to be up and ready */
+ if (ret == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ /* BSEC PTA is required or SMC not supported */
+ if (data->ta || !stm32_bsec_smc_check())
+ return ret;
+ }
+ }
- map = regmap_init(dev, &stm32_bsec_regmap_bus, priv, &priv->map_config);
+ if (priv->ctx)
+ regmap_bus = &stm32_bsec_optee_regmap_bus;
+ else
+ regmap_bus = &stm32_bsec_regmap_bus;
+
+ map = regmap_init(dev, regmap_bus, priv, &priv->map_config);
if (IS_ERR(map))
return PTR_ERR(map);
@@ -184,20 +257,39 @@ static int stm32_bsec_probe(struct device_d *dev)
stm32_bsec_init_dt(priv, dev, map);
+ dev_dbg(dev, "using %s API\n", priv->ctx ? "OP-TEE" : "SiP");
+
return 0;
}
+/*
+ * STM32MP15/13 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
+ * => 96 x 32-bits data words
+ * - Lower: 1K bits, 2:1 redundancy, incremental bit programming
+ * => 32 (x 32-bits) lower shadow registers = words 0 to 31
+ * - Upper: 2K bits, ECC protection, word programming only
+ * => 64 (x 32-bits) = words 32 to 95
+ */
static struct stm32_bsec_data stm32mp15_bsec_data = {
- .num_regs = 95 * 4,
- .svc_id = STM32_SMC_BSEC,
+ .size = 384,
+ .lower = 32,
+ .ta = false,
+};
+
+static const struct stm32_bsec_data stm32mp13_bsec_data = {
+ .size = 384,
+ .lower = 32,
+ .ta = true,
};
static __maybe_unused struct of_device_id stm32_bsec_dt_ids[] = {
{ .compatible = "st,stm32mp15-bsec", .data = &stm32mp15_bsec_data },
+ { .compatible = "st,stm32mp13-bsec", .data = &stm32mp13_bsec_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_bsec_dt_ids);
-static struct driver_d stm32_bsec_driver = {
+static struct driver stm32_bsec_driver = {
.name = "stm32_bsec",
.probe = stm32_bsec_probe,
.of_compatible = DRV_OF_COMPAT(stm32_bsec_dt_ids),
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index c89ad08f81..bf393fc180 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -15,8 +15,7 @@
struct nvmem_device {
const char *name;
- struct device_d dev;
- const struct nvmem_bus *bus;
+ struct device dev;
struct list_head node;
int stride;
int word_size;
@@ -26,10 +25,16 @@ struct nvmem_device {
bool read_only;
struct cdev cdev;
void *priv;
+ nvmem_cell_post_process_t cell_post_process;
+ int (*reg_write)(void *ctx, unsigned int reg,
+ const void *val, size_t val_size);
+ int (*reg_read)(void *ctx, unsigned int reg,
+ void *val, size_t val_size);
};
struct nvmem_cell {
const char *name;
+ const char *id;
int offset;
int bytes;
int bit_offset;
@@ -95,12 +100,12 @@ static struct cdev_operations nvmem_chrdev_ops = {
static int nvmem_register_cdev(struct nvmem_device *nvmem, const char *name)
{
- struct device_d *dev = &nvmem->dev;
+ struct device *dev = &nvmem->dev;
struct cdev *cdev = &nvmem->cdev;
const char *alias;
int ret;
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
cdev->name = xstrdup(alias ?: name);
cdev->ops = &nvmem_chrdev_ops;
@@ -111,7 +116,7 @@ static int nvmem_register_cdev(struct nvmem_device *nvmem, const char *name)
if (ret)
return ret;
- of_parse_partitions(cdev, dev->device_node);
+ of_parse_partitions(cdev, dev->of_node);
of_partitions_register_fixup(cdev);
return 0;
@@ -125,7 +130,7 @@ static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np)
return NULL;
list_for_each_entry(dev, &nvmem_devs, node)
- if (dev->dev.device_node == nvmem_np)
+ if (dev->dev.of_node == nvmem_np)
return dev;
return NULL;
@@ -145,6 +150,7 @@ static struct nvmem_cell *nvmem_find_cell(const char *cell_id)
static void nvmem_cell_drop(struct nvmem_cell *cell)
{
list_del(&cell->node);
+ kfree(cell->id);
kfree(cell);
}
@@ -205,12 +211,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->word_size = config->word_size;
nvmem->size = config->size;
nvmem->dev.parent = config->dev;
- nvmem->bus = config->bus;
- np = config->cdev ? config->cdev->device_node : config->dev->device_node;
- nvmem->dev.device_node = np;
+ nvmem->reg_read = config->reg_read;
+ nvmem->reg_write = config->reg_write;
+ np = config->cdev ? cdev_of_node(config->cdev) : config->dev->of_node;
+ nvmem->dev.of_node = np;
nvmem->priv = config->priv;
+ nvmem->cell_post_process = config->cell_post_process;
- if (config->read_only || !config->bus->write || of_property_read_bool(np, "read-only"))
+ if (config->read_only || !config->reg_write || of_property_read_bool(np, "read-only"))
nvmem->read_only = true;
dev_set_name(&nvmem->dev, config->name);
@@ -325,12 +333,13 @@ EXPORT_SYMBOL_GPL(of_nvmem_device_get);
* Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
* on success.
*/
-struct nvmem_device *nvmem_device_get(struct device_d *dev, const char *dev_name)
+struct nvmem_device *nvmem_device_get(struct device *dev,
+ const char *dev_name)
{
- if (dev->device_node) { /* try dt first */
+ if (dev->of_node) { /* try dt first */
struct nvmem_device *nvmem;
- nvmem = of_nvmem_device_get(dev->device_node, dev_name);
+ nvmem = of_nvmem_device_get(dev->of_node, dev_name);
if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER)
return nvmem;
@@ -400,8 +409,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
addr = of_get_property(cell_np, "reg", &len);
if (!addr || (len < 2 * sizeof(u32))) {
- dev_err(&nvmem->dev, "nvmem: invalid reg on %s\n",
- cell_np->full_name);
+ dev_err(&nvmem->dev, "nvmem: invalid reg on %pOF\n", cell_np);
rval = -EINVAL;
goto err_mem;
}
@@ -416,6 +424,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
cell->offset = be32_to_cpup(addr++);
cell->bytes = be32_to_cpup(addr);
cell->name = cell_np->name;
+ cell->id = kstrdup_const(name, GFP_KERNEL);
addr = of_get_property(cell_np, "bits", &len);
if (addr && len == (2 * sizeof(u32))) {
@@ -463,12 +472,12 @@ EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
* to a struct nvmem_cell. The nvmem_cell will be freed by the
* nvmem_cell_put().
*/
-struct nvmem_cell *nvmem_cell_get(struct device_d *dev, const char *cell_id)
+struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *cell_id)
{
struct nvmem_cell *cell;
- if (dev->device_node) { /* try dt first */
- cell = of_nvmem_cell_get(dev->device_node, cell_id);
+ if (dev->of_node) { /* try dt first */
+ cell = of_nvmem_cell_get(dev->of_node, cell_id);
if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER)
return cell;
}
@@ -525,14 +534,21 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
{
int rc;
- rc = nvmem->bus->read(nvmem->priv, cell->offset, buf, cell->bytes);
- if (IS_ERR_VALUE(rc))
+ rc = nvmem->reg_read(nvmem->priv, cell->offset, buf, cell->bytes);
+ if (rc < 0)
return rc;
/* shift bits in-place */
if (cell->bit_offset || cell->nbits)
nvmem_shift_read_buffer_in_place(cell, buf);
+ if (nvmem->cell_post_process) {
+ rc = nvmem->cell_post_process(nvmem->priv, cell->id,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+ }
+
*len = cell->bytes;
return 0;
@@ -561,7 +577,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
return ERR_PTR(-ENOMEM);
rc = __nvmem_cell_read(nvmem, cell, buf, len);
- if (IS_ERR_VALUE(rc)) {
+ if (rc < 0) {
kfree(buf);
return ERR_PTR(rc);
}
@@ -590,8 +606,8 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
*b <<= bit_offset;
/* setup the first byte with lsb bits from nvmem */
- rc = nvmem->bus->read(nvmem->priv, cell->offset, &v, 1);
- if (IS_ERR_VALUE(rc))
+ rc = nvmem->reg_read(nvmem->priv, cell->offset, &v, 1);
+ if (rc < 0)
return ERR_PTR(rc);
*b++ |= GENMASK(bit_offset - 1, 0) & v;
@@ -610,9 +626,9 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
/* if it's not end on byte boundary */
if ((nbits + bit_offset) % BITS_PER_BYTE) {
/* setup the last byte with msb bits from nvmem */
- rc = nvmem->bus->read(nvmem->priv, cell->offset + cell->bytes - 1,
+ rc = nvmem->reg_read(nvmem->priv, cell->offset + cell->bytes - 1,
&v, 1);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return ERR_PTR(rc);
*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
@@ -646,13 +662,13 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
return PTR_ERR(buf);
}
- rc = nvmem->bus->write(nvmem->priv, cell->offset, buf, cell->bytes);
+ rc = nvmem->reg_write(nvmem->priv, cell->offset, buf, cell->bytes);
/* free the tmp buffer */
if (cell->bit_offset || cell->nbits)
kfree(buf);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return len;
@@ -680,11 +696,11 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
return -EINVAL;
rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
rc = __nvmem_cell_read(nvmem, &cell, buf, &len);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return len;
@@ -710,7 +726,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
return -EINVAL;
rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return nvmem_cell_write(&cell, buf, cell.bytes);
@@ -743,9 +759,9 @@ int nvmem_device_read(struct nvmem_device *nvmem,
if (!bytes)
return 0;
- rc = nvmem->bus->read(nvmem->priv, offset, buf, bytes);
+ rc = nvmem->reg_read(nvmem->priv, offset, buf, bytes);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return bytes;
@@ -777,9 +793,9 @@ int nvmem_device_write(struct nvmem_device *nvmem,
if (!bytes)
return 0;
- rc = nvmem->bus->write(nvmem->priv, offset, buf, bytes);
+ rc = nvmem->reg_write(nvmem->priv, offset, buf, bytes);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
@@ -819,7 +835,7 @@ EXPORT_SYMBOL_GPL(nvmem_cell_get_and_read);
*
* Return: 0 on success or negative errno.
*/
-int nvmem_cell_read_variable_le_u32(struct device_d *dev, const char *cell_id,
+int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
u32 *val)
{
size_t len;
@@ -828,7 +844,7 @@ int nvmem_cell_read_variable_le_u32(struct device_d *dev, const char *cell_id,
len = sizeof(*val);
- buf = nvmem_cell_get_and_read(dev->device_node, cell_id, len);
+ buf = nvmem_cell_get_and_read(dev->of_node, cell_id, len);
if (IS_ERR(buf))
return PTR_ERR(buf);
@@ -842,3 +858,9 @@ int nvmem_cell_read_variable_le_u32(struct device_d *dev, const char *cell_id,
return 0;
}
EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u32);
+
+struct device *nvmem_device_get_device(struct nvmem_device *nvmem)
+{
+ return &nvmem->dev;
+}
+EXPORT_SYMBOL_GPL(nvmem_device_get_device);
diff --git a/drivers/nvmem/eeprom_93xx46.c b/drivers/nvmem/eeprom_93xx46.c
index 0d19bc2877..3180b0cb69 100644
--- a/drivers/nvmem/eeprom_93xx46.c
+++ b/drivers/nvmem/eeprom_93xx46.c
@@ -300,12 +300,13 @@ static const struct of_device_id eeprom_93xx46_of_table[] = {
{ .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, },
{}
};
+MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table);
static int eeprom_93xx46_probe_dt(struct spi_device *spi)
{
const struct of_device_id *of_id =
of_match_device(eeprom_93xx46_of_table, &spi->dev);
- struct device_node *np = spi->dev.device_node;
+ struct device_node *np = spi->dev.of_node;
struct eeprom_93xx46_platform_data *pd;
enum of_gpio_flags of_flags;
unsigned long flags = GPIOF_OUT_INIT_INACTIVE;
@@ -362,19 +363,14 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi)
return 0;
}
-static const struct nvmem_bus eeprom_93xx46_nvmem_bus = {
- .write = eeprom_93xx46_write,
- .read = eeprom_93xx46_read,
-};
-
-static int eeprom_93xx46_probe(struct device_d *dev)
+static int eeprom_93xx46_probe(struct device *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
struct eeprom_93xx46_platform_data *pd;
struct eeprom_93xx46_dev *edev;
int err;
- if (dev->device_node) {
+ if (dev->of_node) {
err = eeprom_93xx46_probe_dt(spi);
if (err < 0)
return err;
@@ -406,7 +402,9 @@ static int eeprom_93xx46_probe(struct device_d *dev)
edev->nvmem_config.dev = &spi->dev;
edev->nvmem_config.priv = edev;
edev->nvmem_config.read_only = pd->flags & EE_READONLY;
- edev->nvmem_config.bus = &eeprom_93xx46_nvmem_bus;
+ edev->nvmem_config.reg_write = eeprom_93xx46_write;
+ edev->nvmem_config.reg_read = eeprom_93xx46_read;
+
edev->nvmem_config.stride = 4;
edev->nvmem_config.word_size = 1;
edev->nvmem_config.size = edev->size;
@@ -427,7 +425,7 @@ fail:
return err;
}
-static struct driver_d eeprom_93xx46_driver = {
+static struct driver eeprom_93xx46_driver = {
.name = "93xx46",
.probe = eeprom_93xx46_probe,
.of_compatible = DRV_OF_COMPAT(eeprom_93xx46_of_table),
diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
new file mode 100644
index 0000000000..e4e60ed6af
--- /dev/null
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * i.MX9 OCOTP fusebox driver
+ *
+ * Copyright 2023 NXP
+ */
+#include <common.h>
+#include <io.h>
+#include <linux/nvmem-provider.h>
+#include <linux/regmap.h>
+#include <mach/imx/ele.h>
+#include <machine_id.h>
+
+#define UNIQUE_ID_NUM 4
+#define OCOTP_UNIQUE_ID(n) (0xc0 + (n) * 4)
+
+enum fuse_type {
+ FUSE_FSB = 1,
+ FUSE_ELE = 2,
+ FUSE_INVALID = -1
+};
+
+struct ocotp_map_entry {
+ u32 start; /* start word */
+ u32 num; /* num words */
+ enum fuse_type type;
+};
+
+struct ocotp_devtype_data {
+ u32 reg_off;
+ char *name;
+ u32 size;
+ u32 num_entry;
+ u32 flag;
+ struct ocotp_map_entry *entry;
+};
+
+struct imx_ocotp_priv {
+ struct device *dev;
+ struct regmap *map;
+ void __iomem *base;
+ const struct ocotp_devtype_data *data;
+ struct regmap_config map_config;
+ int permanent_write_enable;
+};
+
+static enum fuse_type imx_ocotp_fuse_type(struct imx_ocotp_priv *priv, u32 index)
+{
+ const struct ocotp_devtype_data *data = priv->data;
+ u32 start, end;
+ int i;
+
+ for (i = 0; i < data->num_entry; i++) {
+ start = data->entry[i].start;
+ end = data->entry[i].start + data->entry[i].num;
+
+ if (index >= start && index < end)
+ return data->entry[i].type;
+ }
+
+ return FUSE_INVALID;
+}
+
+static int imx_ocotp_reg_read(void *context, unsigned int offset, unsigned int *val)
+{
+ struct imx_ocotp_priv *priv = context;
+ void __iomem *reg = priv->base + priv->data->reg_off;
+ u32 index;
+ enum fuse_type type;
+ int ret;
+ u32 fuse_word;
+
+ index = offset >> 2;
+
+ type = imx_ocotp_fuse_type(priv, index);
+
+ switch (type) {
+ case FUSE_ELE:
+ ret = ele_read_common_fuse(index, &fuse_word, NULL);
+ if (ret)
+ *val = 0xbeefdead;
+ else
+ *val = fuse_word;
+ break;
+ case FUSE_FSB:
+ *val = readl_relaxed(reg + (index << 2));
+ break;
+ default:
+ *val = 0xdeadbeef;
+ break;
+ }
+
+ return 0;
+};
+
+static int imx_ocotp_reg_write(void *context, unsigned int offset, unsigned int val)
+{
+ struct imx_ocotp_priv *priv = context;
+ u32 index;
+ int ret;
+
+ index = offset >> 2;
+
+ if (priv->permanent_write_enable)
+ ret = ele_write_fuse(index, val, false, NULL);
+ else
+ ret = ele_write_shadow_fuse(index, val, NULL);
+
+ return ret;
+}
+
+static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
+ void *data, size_t bytes)
+{
+ /* Deal with some post processing of nvmem cell data */
+ if (id && !strcmp(id, "mac-address")) {
+ u8 *buf = data;
+
+ if (offset == 0x4ec) {
+ swap(buf[0], buf[5]);
+ swap(buf[1], buf[4]);
+ swap(buf[2], buf[3]);
+ } else if (offset == 0x4f2) {
+ swap(buf[0], buf[1]);
+ swap(buf[2], buf[5]);
+ swap(buf[3], buf[4]);
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct regmap_bus imx_ocotp_regmap_bus = {
+ .reg_write = imx_ocotp_reg_write,
+ .reg_read = imx_ocotp_reg_read,
+};
+
+static void imx_ocotp_set_unique_machine_id(struct imx_ocotp_priv *priv)
+{
+ uint32_t unique_id_parts[UNIQUE_ID_NUM];
+ int i;
+
+ for (i = 0; i < UNIQUE_ID_NUM; i++)
+ if (imx_ocotp_reg_read(priv, OCOTP_UNIQUE_ID(i),
+ &unique_id_parts[i]))
+ return;
+
+ machine_id_set_hashable(unique_id_parts, sizeof(unique_id_parts));
+}
+
+static int permanent_write_enable_set(struct param_d *param, void *ctx)
+{
+ struct imx_ocotp_priv *priv = ctx;
+
+ if (priv->permanent_write_enable) {
+ dev_warn(priv->dev, "Enabling permanent write on fuses.\n");
+ dev_warn(priv->dev, "Writing fuses may damage your device. Be careful!\n");
+ }
+
+ return 0;
+}
+
+static int imx_ele_ocotp_probe(struct device *dev)
+{
+ struct imx_ocotp_priv *priv;
+ struct nvmem_device *nvmem;
+ struct resource *iores;
+ struct ocotp_devtype_data *data;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+ priv->dev = dev;
+
+ ret = dev_get_drvdata(dev, (const void **)&data);
+ if (ret)
+ return ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ priv->base = IOMEM(iores->start);
+ priv->data = data;
+
+ priv->map_config.reg_bits = 32;
+ priv->map_config.val_bits = 32;
+ priv->map_config.reg_stride = 4;
+ priv->map_config.max_register = priv->data->size - priv->map_config.reg_stride;
+
+ priv->map = regmap_init(dev, &imx_ocotp_regmap_bus, priv, &priv->map_config);
+ if (IS_ERR(priv->map))
+ return PTR_ERR(priv->map);
+
+ if (IS_ENABLED(CONFIG_MACHINE_ID))
+ imx_ocotp_set_unique_machine_id(priv);
+
+ nvmem = nvmem_regmap_register_with_pp(priv->map, "imx_ocotp",
+ imx_ocotp_cell_pp);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+
+ dev_add_param_bool(nvmem_device_get_device(nvmem), "permanent_write_enable",
+ permanent_write_enable_set, NULL, &priv->permanent_write_enable, priv);
+
+ return 0;
+}
+
+static struct ocotp_map_entry imx93_entries[] = {
+ { 0, 52, FUSE_FSB },
+ { 63, 1, FUSE_ELE},
+ { 128, 16, FUSE_ELE },
+ { 182, 1, FUSE_ELE },
+ { 188, 1, FUSE_ELE },
+ { 312, 200, FUSE_FSB }
+};
+
+static const struct ocotp_devtype_data imx93_ocotp_data = {
+ .reg_off = 0x8000,
+ .size = 2048,
+ .num_entry = ARRAY_SIZE(imx93_entries),
+ .entry = imx93_entries,
+};
+
+static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
+ { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
+
+static struct driver imx_ele_ocotp_driver = {
+ .name = "imx_ele_ocotp",
+ .of_match_table = imx_ele_ocotp_dt_ids,
+ .probe = imx_ele_ocotp_probe,
+};
+core_platform_driver(imx_ele_ocotp_driver);
+
+MODULE_DESCRIPTION("i.MX OCOTP/ELE driver");
+MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/nvmem/kvx-otp-nv.c b/drivers/nvmem/kvx-otp-nv.c
index d614c16e1e..c4591f5822 100644
--- a/drivers/nvmem/kvx-otp-nv.c
+++ b/drivers/nvmem/kvx-otp-nv.c
@@ -49,16 +49,13 @@ static int kvx_otp_nv_read(void *context, unsigned int offset,
return 0;
}
-static const struct nvmem_bus kvx_otp_nv_bus = {
- .read = kvx_otp_nv_read,
-};
-
static const struct of_device_id kvx_otp_nv_match[] = {
{ .compatible = "kalray,kvx-otp-nv" },
{ /* sentinel */},
};
+MODULE_DEVICE_TABLE(of, kvx_otp_nv_match);
-static int kvx_otp_nv_probe(struct device_d *dev)
+static int kvx_otp_nv_probe(struct device *dev)
{
struct resource *res;
struct nvmem_device *nvmem;
@@ -81,7 +78,7 @@ static int kvx_otp_nv_probe(struct device_d *dev)
econfig.size = resource_size(res);
econfig.dev = dev;
econfig.priv = priv;
- econfig.bus = &kvx_otp_nv_bus;
+ econfig.reg_read = kvx_otp_nv_read;
dev->priv = priv;
@@ -90,7 +87,7 @@ static int kvx_otp_nv_probe(struct device_d *dev)
return PTR_ERR_OR_ZERO(nvmem);
}
-static struct driver_d kvx_otp_nv_driver = {
+static struct driver kvx_otp_nv_driver = {
.name = "kvx-otp-nv",
.probe = kvx_otp_nv_probe,
.of_compatible = DRV_OF_COMPAT(kvx_otp_nv_match),
diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c
index 9fcbd1a6a4..c282efefa8 100644
--- a/drivers/nvmem/ocotp.c
+++ b/drivers/nvmem/ocotp.c
@@ -14,6 +14,7 @@
*/
#include <common.h>
+#include <deep-probe.h>
#include <driver.h>
#include <malloc.h>
#include <xfuncs.h>
@@ -23,11 +24,17 @@
#include <io.h>
#include <of.h>
#include <clock.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/bits.h>
#include <linux/clk.h>
-#include <mach/ocotp.h>
#include <machine_id.h>
-#include <mach/ocotp-fusemap.h>
+#ifdef CONFIG_ARCH_IMX
+#include <mach/imx/ocotp.h>
+#include <mach/imx/ocotp-fusemap.h>
+#else
+#include <mach/mxs/ocotp.h>
+#include <mach/mxs/ocotp-fusemap.h>
+#endif
#include <soc/imx8m/featctrl.h>
#include <linux/nvmem-provider.h>
@@ -46,12 +53,12 @@
#define OCOTP_READ_CTRL 0x30
#define OCOTP_READ_FUSE_DATA 0x40
-#define MX7_OCOTP_DATA0 0x20
-#define MX7_OCOTP_DATA1 0x30
-#define MX7_OCOTP_DATA2 0x40
-#define MX7_OCOTP_DATA3 0x50
-#define MX7_OCOTP_READ_CTRL 0x60
-#define MX7_OCOTP_READ_FUSE_DATA0 0x70
+#define MX7_OCOTP_DATA0 0x20
+#define MX7_OCOTP_DATA1 0x30
+#define MX7_OCOTP_DATA2 0x40
+#define MX7_OCOTP_DATA3 0x50
+#define MX7_OCOTP_READ_CTRL 0x60
+#define MX7_OCOTP_READ_FUSE_DATA0 0x70
#define MX7_OCOTP_READ_FUSE_DATA1 0x80
#define MX7_OCOTP_READ_FUSE_DATA2 0x90
#define MX7_OCOTP_READ_FUSE_DATA3 0xA0
@@ -60,25 +67,31 @@
#define DEF_STROBE_PROG 10000 /* IPG clocks */
/* OCOTP Registers bits and masks */
-#define OCOTP_CTRL_WR_UNLOCK 16
+#define OCOTP_CTRL_ADDR GENMASK(7, 0)
+#define OCOTP_CTRL_BUSY BIT(8)
+#define OCOTP_CTRL_ERROR BIT(9)
+#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
+#define OCOTP_CTRL_WR_UNLOCK GENMASK(31, 16)
#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
-#define OCOTP_CTRL_WR_UNLOCK_MASK 0xFFFF0000
-#define OCOTP_CTRL_ADDR 0
-#define OCOTP_CTRL_ADDR_MASK 0x000000FF
-#define OCOTP_CTRL_BUSY (1 << 8)
-#define OCOTP_CTRL_ERROR (1 << 9)
-#define OCOTP_CTRL_RELOAD_SHADOWS (1 << 10)
-#define OCOTP_TIMING_STROBE_READ_MASK 0x003F0000
-#define OCOTP_TIMING_RELAX_MASK 0x0000F000
-#define OCOTP_TIMING_STROBE_PROG_MASK 0x00000FFF
-#define OCOTP_TIMING_WAIT_MASK 0x0FC00000
+/*
+ * i.MX8MP OCOTP CTRL has a different layout. See RM Rev.1 06/2021
+ * Section 6.3.5.1.2.4
+ */
+#define OCOTP_CTRL_ADDR_8MP GENMASK(8, 0)
+#define OCOTP_CTRL_BUSY_8MP BIT(9)
+#define OCOTP_CTRL_ERROR_8MP BIT(10)
+#define OCOTP_CTRL_RELOAD_SHADOWS_8MP BIT(11)
+#define OCOTP_CTRL_WR_UNLOCK_8MP GENMASK(31, 16)
-#define OCOTP_READ_CTRL_READ_FUSE 0x00000001
+#define OCOTP_TIMING_STROBE_READ GENMASK(21, 16)
+#define OCOTP_TIMING_RELAX GENMASK(15, 12)
+#define OCOTP_TIMING_STROBE_PROG GENMASK(11, 0)
+#define OCOTP_TIMING_WAIT GENMASK(27, 22)
-#define BF(value, field) FIELD_PREP(field##_MASK, value)
+#define OCOTP_READ_CTRL_READ_FUSE BIT(1)
-#define OCOTP_OFFSET_TO_ADDR(o) (OCOTP_OFFSET_TO_INDEX(o) * 4)
+#define OCOTP_OFFSET_TO_ADDR(o) (OCOTP_OFFSET_TO_INDEX(o) * 4)
/* Other definitions */
#define IMX6_OTP_DATA_ERROR_VAL 0xBADABADA
@@ -88,19 +101,46 @@
#define MAC_OFFSET_0 (0x22 * 4)
#define IMX6UL_MAC_OFFSET_1 (0x23 * 4)
#define MAC_OFFSET_1 (0x24 * 4)
+#define IMX8MP_MAC_OFFSET_1 (0x25 * 4)
#define MAX_MAC_OFFSETS 2
#define MAC_BYTES 8
#define UNIQUE_ID_NUM 2
+#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
enum imx_ocotp_format_mac_direction {
OCOTP_HW_TO_MAC,
OCOTP_MAC_TO_HW,
};
+struct ocotp_ctrl_reg {
+ u32 bm_addr;
+ u32 bm_busy;
+ u32 bm_error;
+ u32 bm_reload_shadows;
+ u32 bm_wr_unlock;
+};
+
+const struct ocotp_ctrl_reg ocotp_ctrl_reg_default = {
+ .bm_addr = OCOTP_CTRL_ADDR,
+ .bm_busy = OCOTP_CTRL_BUSY,
+ .bm_error = OCOTP_CTRL_ERROR,
+ .bm_reload_shadows = OCOTP_CTRL_RELOAD_SHADOWS,
+ .bm_wr_unlock = OCOTP_CTRL_WR_UNLOCK,
+};
+
+const struct ocotp_ctrl_reg ocotp_ctrl_reg_8mp = {
+ .bm_addr = OCOTP_CTRL_ADDR_8MP,
+ .bm_busy = OCOTP_CTRL_BUSY_8MP,
+ .bm_error = OCOTP_CTRL_ERROR_8MP,
+ .bm_reload_shadows = OCOTP_CTRL_RELOAD_SHADOWS_8MP,
+ .bm_wr_unlock = OCOTP_CTRL_WR_UNLOCK_8MP,
+};
+
struct ocotp_priv;
struct imx_ocotp_data {
- int num_regs;
+ int nregs;
u32 (*addr_to_offset)(u32 addr);
void (*format_mac)(u8 *dst, const u8 *src,
enum imx_ocotp_format_mac_direction dir);
@@ -110,6 +150,7 @@ struct imx_ocotp_data {
u8 mac_offsets[MAX_MAC_OFFSETS];
u8 mac_offsets_num;
struct imx8m_featctrl_data *feat;
+ const struct ocotp_ctrl_reg *ctrl;
};
struct ocotp_priv_ethaddr {
@@ -123,14 +164,13 @@ struct ocotp_priv {
struct regmap *map;
void __iomem *base;
struct clk *clk;
- struct device_d dev;
+ struct device dev;
int permanent_write_enable;
int sense_enable;
struct ocotp_priv_ethaddr ethaddr[MAX_MAC_OFFSETS];
struct regmap_config map_config;
const struct imx_ocotp_data *data;
int mac_offset_idx;
- struct nvmem_config config;
};
static struct ocotp_priv *imx_ocotp;
@@ -175,10 +215,10 @@ static int imx6_ocotp_set_timing(struct ocotp_priv *priv)
1000000);
strobe_prog += 2 * (relax + 1) - 1;
- timing = readl(priv->base + OCOTP_TIMING) & OCOTP_TIMING_WAIT_MASK;
- timing |= BF(relax, OCOTP_TIMING_RELAX);
- timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
- timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
+ timing = readl(priv->base + OCOTP_TIMING) & OCOTP_TIMING_WAIT;
+ timing |= FIELD_PREP(OCOTP_TIMING_RELAX, relax);
+ timing |= FIELD_PREP(OCOTP_TIMING_STROBE_READ, strobe_read);
+ timing |= FIELD_PREP(OCOTP_TIMING_STROBE_PROG, strobe_prog);
writel(timing, priv->base + OCOTP_TIMING);
@@ -209,8 +249,9 @@ static int imx7_ocotp_set_timing(struct ocotp_priv *priv)
static int imx6_ocotp_wait_busy(struct ocotp_priv *priv, u32 flags)
{
uint64_t start = get_time_ns();
+ u32 bm_ctrl_busy = priv->data->ctrl->bm_busy;
- while (readl(priv->base + OCOTP_CTRL) & (OCOTP_CTRL_BUSY | flags))
+ while (readl(priv->base + OCOTP_CTRL) & (bm_ctrl_busy | flags))
if (is_timeout(start, MSECOND))
return -ETIMEDOUT;
@@ -234,15 +275,18 @@ static int imx6_ocotp_prepare(struct ocotp_priv *priv)
static int imx6_fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
+ const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
+ const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
u32 ctrl_reg;
int ret;
- writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
+ writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR);
ctrl_reg = readl(priv->base + OCOTP_CTRL);
- ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK;
- ctrl_reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
- ctrl_reg |= BF(addr, OCOTP_CTRL_ADDR);
+ ctrl_reg &= ~bm_ctrl_addr;
+ ctrl_reg &= ~bm_ctrl_wr_unlock;
+ ctrl_reg |= field_prep(bm_ctrl_addr, addr);
writel(ctrl_reg, priv->base + OCOTP_CTRL);
writel(OCOTP_READ_CTRL_READ_FUSE, priv->base + OCOTP_READ_CTRL);
@@ -250,7 +294,7 @@ static int imx6_fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
if (ret)
return ret;
- if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR)
+ if (readl(priv->base + OCOTP_CTRL) & bm_ctrl_error)
*pdata = 0xbadabada;
else
*pdata = readl(priv->base + OCOTP_READ_FUSE_DATA);
@@ -260,6 +304,9 @@ static int imx6_fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
static int imx7_fuse_read_addr(struct ocotp_priv *priv, u32 index, u32 *pdata)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
+ const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
+ const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
u32 ctrl_reg;
u32 bank_addr;
u16 word;
@@ -268,12 +315,12 @@ static int imx7_fuse_read_addr(struct ocotp_priv *priv, u32 index, u32 *pdata)
word = index & 0x3;
bank_addr = index >> 2;
- writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
+ writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR);
ctrl_reg = readl(priv->base + OCOTP_CTRL);
- ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK;
- ctrl_reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
- ctrl_reg |= BF(bank_addr, OCOTP_CTRL_ADDR);
+ ctrl_reg &= ~bm_ctrl_addr;
+ ctrl_reg &= ~bm_ctrl_wr_unlock;
+ ctrl_reg |= field_prep(bm_ctrl_addr, bank_addr);
writel(ctrl_reg, priv->base + OCOTP_CTRL);
writel(OCOTP_READ_CTRL_READ_FUSE, priv->base + MX7_OCOTP_READ_CTRL);
@@ -281,7 +328,7 @@ static int imx7_fuse_read_addr(struct ocotp_priv *priv, u32 index, u32 *pdata)
if (ret)
return ret;
- if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR)
+ if (readl(priv->base + OCOTP_CTRL) & bm_ctrl_error)
*pdata = 0xbadabada;
else
switch (word) {
@@ -344,25 +391,29 @@ static int imx_ocotp_reg_read(void *ctx, unsigned int reg, unsigned int *val)
static void imx_ocotp_clear_unlock(struct ocotp_priv *priv, u32 index)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
+ const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
+ const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
u32 ctrl_reg;
- writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
+ writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR);
/* Control register */
ctrl_reg = readl(priv->base + OCOTP_CTRL);
- ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK;
- ctrl_reg |= BF(index, OCOTP_CTRL_ADDR);
- ctrl_reg |= BF(OCOTP_CTRL_WR_UNLOCK_KEY, OCOTP_CTRL_WR_UNLOCK);
+ ctrl_reg &= ~bm_ctrl_addr;
+ ctrl_reg |= field_prep(bm_ctrl_addr, index);
+ ctrl_reg |= field_prep(bm_ctrl_wr_unlock, OCOTP_CTRL_WR_UNLOCK_KEY);
writel(ctrl_reg, priv->base + OCOTP_CTRL);
}
static int imx6_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
int ret;
imx_ocotp_clear_unlock(priv, index);
- writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
+ writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR);
writel(value, priv->base + OCOTP_DATA);
ret = imx6_ocotp_wait_busy(priv, 0);
@@ -423,16 +474,19 @@ static int imx7_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value)
static int imx6_ocotp_reload_shadow(struct ocotp_priv *priv)
{
+ const u32 bm_ctrl_reload_shadows = priv->data->ctrl->bm_reload_shadows;
+
dev_info(&priv->dev, "reloading shadow registers...\n");
- writel(OCOTP_CTRL_RELOAD_SHADOWS, priv->base + OCOTP_CTRL_SET);
+ writel(bm_ctrl_reload_shadows, priv->base + OCOTP_CTRL_SET);
udelay(1);
- return imx6_ocotp_wait_busy(priv, OCOTP_CTRL_RELOAD_SHADOWS);
+ return imx6_ocotp_wait_busy(priv, bm_ctrl_reload_shadows);
}
static int imx6_ocotp_blow_one_u32(struct ocotp_priv *priv, u32 index, u32 data,
u32 *pfused_value)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
int ret;
ret = imx6_ocotp_prepare(priv);
@@ -447,7 +501,7 @@ static int imx6_ocotp_blow_one_u32(struct ocotp_priv *priv, u32 index, u32 data,
return ret;
}
- if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR) {
+ if (readl(priv->base + OCOTP_CTRL) & bm_ctrl_error) {
dev_err(&priv->dev, "bad write status\n");
return -EFAULT;
}
@@ -492,11 +546,17 @@ static void imx_ocotp_field_decode(uint32_t field, unsigned *word,
*mask = GENMASK(width, 0);
}
+static int imx_ocotp_ensure_probed(void);
+
int imx_ocotp_read_field(uint32_t field, unsigned *value)
{
unsigned word, bit, mask, val;
int ret;
+ ret = imx_ocotp_ensure_probed();
+ if (ret)
+ return ret;
+
imx_ocotp_field_decode(field, &word, &bit, &mask);
ret = imx_ocotp_reg_read(imx_ocotp, word, &val);
@@ -519,6 +579,10 @@ int imx_ocotp_write_field(uint32_t field, unsigned value)
unsigned word, bit, mask;
int ret;
+ ret = imx_ocotp_ensure_probed();
+ if (ret)
+ return ret;
+
imx_ocotp_field_decode(field, &word, &bit, &mask);
value &= mask;
@@ -536,14 +600,27 @@ int imx_ocotp_write_field(uint32_t field, unsigned value)
int imx_ocotp_permanent_write(int enable)
{
+ int ret;
+
+ ret = imx_ocotp_ensure_probed();
+ if (ret)
+ return ret;
+
imx_ocotp->permanent_write_enable = enable;
return 0;
}
-bool imx_ocotp_sense_enable(bool enable)
+int imx_ocotp_sense_enable(bool enable)
{
- const bool old_value = imx_ocotp->sense_enable;
+ bool old_value;
+ int ret;
+
+ ret = imx_ocotp_ensure_probed();
+ if (ret)
+ return ret;
+
+ old_value = imx_ocotp->sense_enable;
imx_ocotp->sense_enable = enable;
return old_value;
}
@@ -593,12 +670,12 @@ static int imx_ocotp_read_mac(const struct imx_ocotp_data *data,
u8 buf[MAC_BYTES];
int ret;
- ret = regmap_bulk_read(map, offset, buf, MAC_BYTES);
+ ret = regmap_bulk_read(map, offset, buf, MAC_BYTES / 4);
if (ret < 0)
return ret;
- if (offset != IMX6UL_MAC_OFFSET_1)
+ if (offset != IMX6UL_MAC_OFFSET_1 && offset != IMX8MP_MAC_OFFSET_1)
data->format_mac(mac, buf, OCOTP_HW_TO_MAC);
else
data->format_mac(mac, buf + 2, OCOTP_HW_TO_MAC);
@@ -620,11 +697,12 @@ static int imx_ocotp_set_mac(struct param_d *param, void *priv)
struct ocotp_priv_ethaddr *ethaddr = priv;
int ret;
- ret = regmap_bulk_read(ethaddr->map, ethaddr->offset, buf, MAC_BYTES);
+ ret = regmap_bulk_read(ethaddr->map, ethaddr->offset, buf, MAC_BYTES / 4);
if (ret < 0)
return ret;
- if (ethaddr->offset != IMX6UL_MAC_OFFSET_1)
+ if (ethaddr->offset != IMX6UL_MAC_OFFSET_1 &&
+ ethaddr->offset != IMX8MP_MAC_OFFSET_1)
ethaddr->data->format_mac(buf, ethaddr->value,
OCOTP_MAC_TO_HW);
else
@@ -632,7 +710,7 @@ static int imx_ocotp_set_mac(struct param_d *param, void *priv)
OCOTP_MAC_TO_HW);
return regmap_bulk_write(ethaddr->map, ethaddr->offset,
- buf, MAC_BYTES);
+ buf, MAC_BYTES / 4);
}
static struct regmap_bus imx_ocotp_regmap_bus = {
@@ -640,20 +718,33 @@ static struct regmap_bus imx_ocotp_regmap_bus = {
.reg_read = imx_ocotp_reg_read,
};
+static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
+ void *data, size_t bytes)
+{
+ /* Deal with some post processing of nvmem cell data */
+ if (id && !strcmp(id, "mac-address")) {
+ u8 *buf = data;
+ int i;
+
+ for (i = 0; i < bytes/2; i++)
+ swap(buf[i], buf[bytes - i - 1]);
+ }
+
+ return 0;
+}
+
static int imx_ocotp_init_dt(struct ocotp_priv *priv)
{
char mac[MAC_BYTES];
const __be32 *prop;
- struct device_node *node = priv->dev.parent->device_node;
- u32 tester4;
- int ret, len;
+ struct device_node *node = priv->dev.parent->of_node;
+ u32 tester3, tester4;
+ int ret, len = 0;
if (!node)
return 0;
prop = of_get_property(node, "barebox,provide-mac-address", &len);
- if (!prop)
- return 0;
for (; len >= MAC_ADDRESS_PROPLEN; len -= MAC_ADDRESS_PROPLEN) {
struct device_node *rnode;
@@ -675,25 +766,15 @@ static int imx_ocotp_init_dt(struct ocotp_priv *priv)
if (!of_property_read_bool(node, "barebox,feature-controller"))
return 0;
- ret = regmap_read(priv->map, OCOTP_OFFSET_TO_ADDR(0x450), &tester4);
+ ret = regmap_read(priv->map, OCOTP_OFFSET_TO_ADDR(0x440), &tester3);
if (ret != 0)
return ret;
- return imx8m_feat_ctrl_init(priv->dev.parent, tester4, priv->data->feat);
-}
-
-static int imx_ocotp_write(void *ctx, unsigned offset, const void *val, size_t bytes)
-{
- struct ocotp_priv *priv = ctx;
-
- return regmap_bulk_write(priv->map, offset, val, bytes);
-}
-
-static int imx_ocotp_read(void *ctx, unsigned offset, void *val, size_t bytes)
-{
- struct ocotp_priv *priv = ctx;
+ ret = regmap_read(priv->map, OCOTP_OFFSET_TO_ADDR(0x450), &tester4);
+ if (ret != 0)
+ return ret;
- return regmap_bulk_read(priv->map, offset, val, bytes);
+ return imx8m_feat_ctrl_init(priv->dev.parent, tester3, tester4, priv->data->feat);
}
static void imx_ocotp_set_unique_machine_id(void)
@@ -709,12 +790,7 @@ static void imx_ocotp_set_unique_machine_id(void)
machine_id_set_hashable(unique_id_parts, sizeof(unique_id_parts));
}
-static const struct nvmem_bus imx_ocotp_nvmem_bus = {
- .write = imx_ocotp_write,
- .read = imx_ocotp_read,
-};
-
-static int imx_ocotp_probe(struct device_d *dev)
+static int imx_ocotp_probe(struct device *dev)
{
struct resource *iores;
struct ocotp_priv *priv;
@@ -745,21 +821,14 @@ static int imx_ocotp_probe(struct device_d *dev)
priv->map_config.reg_bits = 32;
priv->map_config.val_bits = 32;
priv->map_config.reg_stride = 4;
- priv->map_config.max_register = data->num_regs - 1;
+ priv->map_config.max_register = priv->map_config.reg_stride * (data->nregs - 1);
priv->map = regmap_init(dev, &imx_ocotp_regmap_bus, priv, &priv->map_config);
if (IS_ERR(priv->map))
return PTR_ERR(priv->map);
- priv->config.name = "imx-ocotp";
- priv->config.dev = dev;
- priv->config.priv = priv;
- priv->config.stride = 4;
- priv->config.word_size = 4;
- priv->config.size = data->num_regs;
- priv->config.bus = &imx_ocotp_nvmem_bus;
-
- nvmem = nvmem_register(&priv->config);
+ nvmem = nvmem_regmap_register_with_pp(priv->map, "imx-ocotp",
+ imx_ocotp_cell_pp);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);
@@ -836,7 +905,7 @@ static u32 vf610_addr_to_offset(u32 addr)
}
static struct imx_ocotp_data imx6q_ocotp_data = {
- .num_regs = 512,
+ .nregs = 128,
.addr_to_offset = imx6q_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0 },
@@ -844,10 +913,11 @@ static struct imx_ocotp_data imx6q_ocotp_data = {
.set_timing = imx6_ocotp_set_timing,
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx_ocotp_data imx6sl_ocotp_data = {
- .num_regs = 256,
+ .nregs = 64,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0 },
@@ -855,10 +925,11 @@ static struct imx_ocotp_data imx6sl_ocotp_data = {
.set_timing = imx6_ocotp_set_timing,
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx_ocotp_data imx6ul_ocotp_data = {
- .num_regs = 512,
+ .nregs = 144,
.addr_to_offset = imx6q_addr_to_offset,
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, IMX6UL_MAC_OFFSET_1 },
@@ -866,10 +937,11 @@ static struct imx_ocotp_data imx6ul_ocotp_data = {
.set_timing = imx6_ocotp_set_timing,
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx_ocotp_data imx6ull_ocotp_data = {
- .num_regs = 256,
+ .nregs = 80,
.addr_to_offset = imx6q_addr_to_offset,
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, IMX6UL_MAC_OFFSET_1 },
@@ -877,10 +949,11 @@ static struct imx_ocotp_data imx6ull_ocotp_data = {
.set_timing = imx6_ocotp_set_timing,
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx_ocotp_data vf610_ocotp_data = {
- .num_regs = 512,
+ .nregs = 128,
.addr_to_offset = vf610_addr_to_offset,
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, MAC_OFFSET_1 },
@@ -888,18 +961,35 @@ static struct imx_ocotp_data vf610_ocotp_data = {
.set_timing = imx6_ocotp_set_timing,
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
+};
+
+static struct imx8m_featctrl_data imx8mp_featctrl_data = {
+ .tester3.cpu_bitmask = 0xc0000,
+ .tester3.vpu_bitmask = 0x43000000,
+ .tester4.npu_bitmask = 0x8,
+ .tester4.gpu_bitmask = 0xc0,
+ .tester4.mipi_dsi_bitmask = 0x60000,
+ .tester4.lvds_bitmask = 0x180000,
+ .tester4.isp_bitmask = 0x3,
+ .tester4.dsp_bitmask = 0x10,
};
static struct imx_ocotp_data imx8mp_ocotp_data = {
- .num_regs = 1024,
+ .nregs = 384,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 2,
- .mac_offsets = { 0x90, 0x96 },
+ .mac_offsets = { 0x90, 0x94 },
.format_mac = imx_ocotp_format_mac,
+ .feat = &imx8mp_featctrl_data,
+ .set_timing = imx6_ocotp_set_timing,
+ .fuse_blow = imx6_fuse_blow_addr,
+ .fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_8mp,
};
static struct imx_ocotp_data imx8mq_ocotp_data = {
- .num_regs = 2048,
+ .nregs = 256,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { 0x90 },
@@ -907,14 +997,16 @@ static struct imx_ocotp_data imx8mq_ocotp_data = {
.set_timing = imx6_ocotp_set_timing,
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx8m_featctrl_data imx8mm_featctrl_data = {
- .vpu_bitmask = 0x1c0000,
+ .tester4.vpu_bitmask = 0x1c0000,
+ .tester4.cpu_bitmask = 0x3,
};
static struct imx_ocotp_data imx8mm_ocotp_data = {
- .num_regs = 2048,
+ .nregs = 256,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { 0x90 },
@@ -923,14 +1015,16 @@ static struct imx_ocotp_data imx8mm_ocotp_data = {
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
.feat = &imx8mm_featctrl_data,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx8m_featctrl_data imx8mn_featctrl_data = {
- .gpu_bitmask = 0x1000000,
+ .tester4.gpu_bitmask = 0x1000000,
+ .tester4.cpu_bitmask = 0x3,
};
static struct imx_ocotp_data imx8mn_ocotp_data = {
- .num_regs = 2048,
+ .nregs = 256,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { 0x90 },
@@ -939,10 +1033,11 @@ static struct imx_ocotp_data imx8mn_ocotp_data = {
.fuse_blow = imx6_fuse_blow_addr,
.fuse_read = imx6_fuse_read_addr,
.feat = &imx8mn_featctrl_data,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static struct imx_ocotp_data imx7d_ocotp_data = {
- .num_regs = 2048,
+ .nregs = 64,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0, IMX6UL_MAC_OFFSET_1 },
@@ -950,6 +1045,7 @@ static struct imx_ocotp_data imx7d_ocotp_data = {
.set_timing = imx7_ocotp_set_timing,
.fuse_blow = imx7_fuse_blow_addr,
.fuse_read = imx7_fuse_read_addr,
+ .ctrl = &ocotp_ctrl_reg_default,
};
static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
@@ -990,8 +1086,22 @@ static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+
+static int imx_ocotp_ensure_probed(void)
+{
+ if (!imx_ocotp && deep_probe_is_supported()) {
+ int ret;
+
+ ret = of_devices_ensure_probed_by_dev_id(imx_ocotp_dt_ids);
+ if (ret)
+ return ret;
+ }
+
+ return imx_ocotp ? 0 : -EPROBE_DEFER;
+}
-static struct driver_d imx_ocotp_driver = {
+static struct driver imx_ocotp_driver = {
.name = "imx_ocotp",
.probe = imx_ocotp_probe,
.of_compatible = DRV_OF_COMPAT(imx_ocotp_dt_ids),
diff --git a/drivers/nvmem/partition.c b/drivers/nvmem/partition.c
index a034c6c4d5..14907e05ba 100644
--- a/drivers/nvmem/partition.c
+++ b/drivers/nvmem/partition.c
@@ -18,11 +18,6 @@ static int nvmem_cdev_read(void *ctx, unsigned offset, void *buf, size_t bytes)
return cdev_read(ctx, buf, bytes, offset, 0);
}
-static struct nvmem_bus nvmem_cdev_bus = {
- .read = nvmem_cdev_read,
- .write = nvmem_cdev_write,
-};
-
struct nvmem_device *nvmem_partition_register(struct cdev *cdev)
{
struct nvmem_config config = {};
@@ -34,7 +29,8 @@ struct nvmem_device *nvmem_partition_register(struct cdev *cdev)
config.stride = 1;
config.word_size = 1;
config.size = cdev->size;
- config.bus = &nvmem_cdev_bus;
+ config.reg_read = nvmem_cdev_read;
+ config.reg_write = nvmem_cdev_write;
return nvmem_register(&config);
}
diff --git a/drivers/nvmem/rave-sp-eeprom.c b/drivers/nvmem/rave-sp-eeprom.c
index 2345b24936..aae337853c 100644
--- a/drivers/nvmem/rave-sp-eeprom.c
+++ b/drivers/nvmem/rave-sp-eeprom.c
@@ -280,12 +280,7 @@ static int rave_sp_eeprom_write(void *ctx, unsigned offset, const void *val, siz
offset, (void *)val, bytes);
}
-static const struct nvmem_bus rave_sp_eeprom_nvmem_bus = {
- .write = rave_sp_eeprom_write,
- .read = rave_sp_eeprom_read,
-};
-
-static int rave_sp_eeprom_probe(struct device_d *dev)
+static int rave_sp_eeprom_probe(struct device *dev)
{
struct rave_sp *sp = dev->parent->priv;
struct nvmem_config config = { 0 };
@@ -293,7 +288,7 @@ static int rave_sp_eeprom_probe(struct device_d *dev)
struct nvmem_device *nvmem;
u32 reg[2], size;
- if (of_property_read_u32_array(dev->device_node,
+ if (of_property_read_u32_array(dev->of_node,
"reg", reg, ARRAY_SIZE(reg))) {
dev_err(dev, "Failed to parse \"reg\" property\n");
return -EINVAL;
@@ -322,14 +317,15 @@ static int rave_sp_eeprom_probe(struct device_d *dev)
/*
* If a name is specified via DT, override the above with it.
*/
- of_property_read_string(dev->device_node, "zii,eeprom-name",
+ of_property_read_string(dev->of_node, "zii,eeprom-name",
&config.name);
config.dev = dev;
config.priv = eeprom;
config.word_size = 1;
config.stride = 1;
config.size = reg[1];
- config.bus = &rave_sp_eeprom_nvmem_bus;
+ config.reg_write = rave_sp_eeprom_write;
+ config.reg_read = rave_sp_eeprom_read;
nvmem = nvmem_register(&config);
if (IS_ERR(nvmem)) {
@@ -344,8 +340,9 @@ static __maybe_unused const struct of_device_id rave_sp_eeprom_of_match[] = {
{ .compatible = "zii,rave-sp-eeprom" },
{}
};
+MODULE_DEVICE_TABLE(of, rave_sp_eeprom_of_match);
-static struct driver_d rave_sp_eeprom_driver = {
+static struct driver rave_sp_eeprom_driver = {
.name = "rave-sp-eeprom",
.probe = rave_sp_eeprom_probe,
.of_compatible = DRV_OF_COMPAT(rave_sp_eeprom_of_match),
diff --git a/drivers/nvmem/regmap.c b/drivers/nvmem/regmap.c
index 56611819a2..24712fbb0f 100644
--- a/drivers/nvmem/regmap.c
+++ b/drivers/nvmem/regmap.c
@@ -6,12 +6,22 @@
#include <errno.h>
#include <init.h>
#include <io.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/nvmem-provider.h>
static int nvmem_regmap_write(void *ctx, unsigned offset, const void *val, size_t bytes)
{
- return regmap_bulk_write(ctx, offset, val, bytes);
+ struct regmap *map = ctx;
+
+ /*
+ * eFuse writes going through this function may be irreversible,
+ * so expect users to observe alignment.
+ */
+ if (bytes % regmap_get_val_bytes(map))
+ return -EINVAL;
+
+ return regmap_bulk_write(map, offset, val,
+ bytes / regmap_get_val_bytes(map));
}
static int nvmem_regmap_read(void *ctx, unsigned offset, void *buf, size_t bytes)
@@ -28,11 +38,11 @@ static int nvmem_regmap_read(void *ctx, unsigned offset, void *buf, size_t bytes
skip_bytes = offset & (stride - 1);
rbytes = roundup(bytes + skip_bytes, stride);
- if (roffset + rbytes > stride * regmap_get_max_register(map))
+ if (roffset + rbytes > regmap_size_bytes(map))
return -EINVAL;
for (i = roffset; i < roffset + rbytes; i += stride) {
- ret = regmap_bulk_read(map, i, &val, stride);
+ ret = regmap_read(map, i, &val);
if (ret) {
dev_err(regmap_get_device(map), "Can't read data%d (%d)\n", i, ret);
return ret;
@@ -53,12 +63,9 @@ static int nvmem_regmap_read(void *ctx, unsigned offset, void *buf, size_t bytes
return 0;
}
-static struct nvmem_bus nvmem_regmap_bus = {
- .read = nvmem_regmap_read,
- .write = nvmem_regmap_write,
-};
-
-struct nvmem_device *nvmem_regmap_register(struct regmap *map, const char *name)
+struct nvmem_device *
+nvmem_regmap_register_with_pp(struct regmap *map, const char *name,
+ nvmem_cell_post_process_t cell_post_process)
{
struct nvmem_config config = {};
@@ -71,8 +78,15 @@ struct nvmem_device *nvmem_regmap_register(struct regmap *map, const char *name)
config.priv = map;
config.stride = 1;
config.word_size = 1;
- config.size = regmap_get_max_register(map) * regmap_get_reg_stride(map);
- config.bus = &nvmem_regmap_bus;
+ config.size = regmap_size_bytes(map);
+ config.cell_post_process = cell_post_process;
+ config.reg_write = nvmem_regmap_write;
+ config.reg_read = nvmem_regmap_read;
return nvmem_register(&config);
}
+
+struct nvmem_device *nvmem_regmap_register(struct regmap *map, const char *name)
+{
+ return nvmem_regmap_register_with_pp(map, name, NULL);
+}
diff --git a/drivers/nvmem/rmem.c b/drivers/nvmem/rmem.c
index b9331f48ff..afa0dd78c8 100644
--- a/drivers/nvmem/rmem.c
+++ b/drivers/nvmem/rmem.c
@@ -9,7 +9,7 @@
#include <init.h>
struct rmem {
- struct device_d *dev;
+ struct device *dev;
const struct resource *mem;
};
@@ -21,11 +21,7 @@ static int rmem_read(void *context, unsigned int offset,
bytes, offset, 0);
}
-static struct nvmem_bus rmem_nvmem_bus = {
- .read = rmem_read,
-};
-
-static int rmem_probe(struct device_d *dev)
+static int rmem_probe(struct device *dev)
{
struct nvmem_config config = { };
struct resource *mem;
@@ -45,7 +41,7 @@ static int rmem_probe(struct device_d *dev)
config.priv = priv;
config.name = "rmem";
config.size = resource_size(mem);
- config.bus = &rmem_nvmem_bus;
+ config.reg_read = rmem_read;
return PTR_ERR_OR_ZERO(nvmem_register(&config));
}
@@ -54,8 +50,9 @@ static const struct of_device_id rmem_match[] = {
{ .compatible = "nvmem-rmem", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, rmem_match);
-static struct driver_d rmem_driver = {
+static struct driver rmem_driver = {
.name = "rmem",
.of_compatible = rmem_match,
.probe = rmem_probe,
diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c
new file mode 100644
index 0000000000..b8da4c5380
--- /dev/null
+++ b/drivers/nvmem/rockchip-otp.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip OTP Driver
+ *
+ * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
+ * Author: Finley Xiao <finley.xiao@rock-chips.com>
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <of.h>
+#include <of_device.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/nvmem-provider.h>
+#include <linux/reset.h>
+
+/* OTP Register Offsets */
+#define OTPC_SBPI_CTRL 0x0020
+#define OTPC_SBPI_CMD_VALID_PRE 0x0024
+#define OTPC_SBPI_CS_VALID_PRE 0x0028
+#define OTPC_SBPI_STATUS 0x002C
+#define OTPC_USER_CTRL 0x0100
+#define OTPC_USER_ADDR 0x0104
+#define OTPC_USER_ENABLE 0x0108
+#define OTPC_USER_Q 0x0124
+#define OTPC_INT_STATUS 0x0304
+#define OTPC_SBPI_CMD0_OFFSET 0x1000
+#define OTPC_SBPI_CMD1_OFFSET 0x1004
+
+/* OTP Register bits and masks */
+#define OTPC_USER_ADDR_MASK GENMASK(31, 16)
+#define OTPC_USE_USER BIT(0)
+#define OTPC_USE_USER_MASK GENMASK(16, 16)
+#define OTPC_USER_FSM_ENABLE BIT(0)
+#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16)
+#define OTPC_SBPI_DONE BIT(1)
+#define OTPC_USER_DONE BIT(2)
+
+#define SBPI_DAP_ADDR 0x02
+#define SBPI_DAP_ADDR_SHIFT 8
+#define SBPI_DAP_ADDR_MASK GENMASK(31, 24)
+#define SBPI_CMD_VALID_MASK GENMASK(31, 16)
+#define SBPI_DAP_CMD_WRF 0xC0
+#define SBPI_DAP_REG_ECC 0x3A
+#define SBPI_ECC_ENABLE 0x00
+#define SBPI_ECC_DISABLE 0x09
+#define SBPI_ENABLE BIT(0)
+#define SBPI_ENABLE_MASK GENMASK(16, 16)
+
+#define OTPC_TIMEOUT 10000
+
+#define RK3568_NBYTES 2
+
+/* RK3588 Register */
+#define RK3588_OTPC_AUTO_CTRL 0x04
+#define RK3588_OTPC_AUTO_EN 0x08
+#define RK3588_OTPC_INT_ST 0x84
+#define RK3588_OTPC_DOUT0 0x20
+#define RK3588_NO_SECURE_OFFSET 0x300
+#define RK3588_NBYTES 4
+#define RK3588_BURST_NUM 1
+#define RK3588_BURST_SHIFT 8
+#define RK3588_ADDR_SHIFT 16
+#define RK3588_AUTO_EN BIT(0)
+#define RK3588_RD_DONE BIT(1)
+
+struct rockchip_data {
+ int size;
+ const char * const *clks;
+ int num_clks;
+ int (*reg_read)(void *ctx, unsigned int reg, void *val, size_t val_size);
+};
+
+struct rockchip_otp {
+ struct device *dev;
+ void __iomem *base;
+ struct clk_bulk_data *clks;
+ struct reset_control *rst;
+ const struct rockchip_data *data;
+};
+
+static int rockchip_otp_reset(struct rockchip_otp *otp)
+{
+ int ret;
+
+ ret = reset_control_assert(otp->rst);
+ if (ret) {
+ dev_err(otp->dev, "failed to assert otp phy %d\n", ret);
+ return ret;
+ }
+
+ udelay(2);
+
+ ret = reset_control_deassert(otp->rst);
+ if (ret) {
+ dev_err(otp->dev, "failed to deassert otp phy %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_otp_wait_status(struct rockchip_otp *otp,
+ unsigned int reg, u32 flag)
+{
+ u32 status = 0;
+ int ret;
+
+ ret = readl_poll_timeout(otp->base + reg, status,
+ (status & flag), OTPC_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* clean int status */
+ writel(flag, otp->base + reg);
+
+ return 0;
+}
+
+static int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable)
+{
+ int ret = 0;
+
+ writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT),
+ otp->base + OTPC_SBPI_CTRL);
+
+ writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE);
+ writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC,
+ otp->base + OTPC_SBPI_CMD0_OFFSET);
+ if (enable)
+ writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
+ else
+ writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
+
+ writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
+
+ ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_SBPI_DONE);
+ if (ret < 0)
+ dev_err(otp->dev, "timeout during ecc_enable\n");
+
+ return ret;
+}
+
+static int px30_otp_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct rockchip_otp *otp = context;
+ u8 *buf = val;
+ int ret;
+
+ ret = rockchip_otp_reset(otp);
+ if (ret) {
+ dev_err(otp->dev, "failed to reset otp phy\n");
+ return ret;
+ }
+
+ ret = rockchip_otp_ecc_enable(otp, false);
+ if (ret < 0) {
+ dev_err(otp->dev, "rockchip_otp_ecc_enable err\n");
+ return ret;
+ }
+
+ writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
+ udelay(5);
+ while (bytes--) {
+ writel(offset++ | OTPC_USER_ADDR_MASK,
+ otp->base + OTPC_USER_ADDR);
+ writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
+ otp->base + OTPC_USER_ENABLE);
+ ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_USER_DONE);
+ if (ret < 0) {
+ dev_err(otp->dev, "timeout during read setup\n");
+ goto read_end;
+ }
+ *buf++ = readb(otp->base + OTPC_USER_Q);
+ }
+
+read_end:
+ writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
+
+ return ret;
+}
+
+static int rk3568_otp_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct rockchip_otp *otp = context;
+ unsigned int addr_start, addr_end, addr_offset, addr_len;
+ u32 out_value;
+ u8 *buf;
+ int ret = 0, i = 0;
+
+ addr_start = rounddown(offset, RK3568_NBYTES) / RK3568_NBYTES;
+ addr_end = roundup(offset + bytes, RK3568_NBYTES) / RK3568_NBYTES;
+ addr_offset = offset % RK3568_NBYTES;
+ addr_len = addr_end - addr_start;
+
+ buf = kzalloc(array3_size(addr_len, RK3568_NBYTES, sizeof(*buf)),
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = rockchip_otp_reset(otp);
+ if (ret) {
+ dev_err(otp->dev, "failed to reset otp phy\n");
+ goto out;
+ }
+
+ ret = rockchip_otp_ecc_enable(otp, false);
+ if (ret < 0) {
+ dev_err(otp->dev, "rockchip_otp_ecc_enable err\n");
+ goto out;
+ }
+
+ writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
+ udelay(5);
+ while (addr_len--) {
+ writel(addr_start++ | OTPC_USER_ADDR_MASK,
+ otp->base + OTPC_USER_ADDR);
+ writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
+ otp->base + OTPC_USER_ENABLE);
+ ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_USER_DONE);
+ if (ret < 0) {
+ dev_err(otp->dev, "timeout during read setup\n");
+ goto read_end;
+ }
+ out_value = readl(otp->base + OTPC_USER_Q);
+ memcpy(&buf[i], &out_value, RK3568_NBYTES);
+ i += RK3568_NBYTES;
+ }
+
+ memcpy(val, buf + addr_offset, bytes);
+
+read_end:
+ writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
+out:
+ kfree(buf);
+
+ return ret;
+}
+
+static int rk3588_otp_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct rockchip_otp *otp = context;
+ unsigned int addr_start, addr_end, addr_len;
+ int ret = 0, i = 0;
+ u32 data;
+ u8 *buf;
+
+ addr_start = round_down(offset, RK3588_NBYTES) / RK3588_NBYTES;
+ addr_end = round_up(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES;
+ addr_len = addr_end - addr_start;
+ addr_start += RK3588_NO_SECURE_OFFSET;
+
+ buf = kzalloc(array_size(addr_len, RK3588_NBYTES), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ while (addr_len--) {
+ writel((addr_start << RK3588_ADDR_SHIFT) |
+ (RK3588_BURST_NUM << RK3588_BURST_SHIFT),
+ otp->base + RK3588_OTPC_AUTO_CTRL);
+ writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN);
+
+ ret = rockchip_otp_wait_status(otp, RK3588_OTPC_INT_ST,
+ RK3588_RD_DONE);
+ if (ret < 0) {
+ dev_err(otp->dev, "timeout during read setup\n");
+ goto read_end;
+ }
+
+ data = readl(otp->base + RK3588_OTPC_DOUT0);
+ memcpy(&buf[i], &data, RK3588_NBYTES);
+
+ i += RK3588_NBYTES;
+ addr_start++;
+ }
+
+ memcpy(val, buf + offset % RK3588_NBYTES, bytes);
+
+read_end:
+ kfree(buf);
+
+ return ret;
+}
+
+static int rockchip_otp_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct rockchip_otp *otp = context;
+ int ret;
+
+ if (!otp->data || !otp->data->reg_read)
+ return -EINVAL;
+
+ ret = clk_bulk_enable(otp->data->num_clks, otp->clks);
+ if (ret < 0) {
+ dev_err(otp->dev, "failed to prepare/enable clks\n");
+ return ret;
+ }
+
+ ret = otp->data->reg_read(context, offset, val, bytes);
+
+ clk_bulk_disable(otp->data->num_clks, otp->clks);
+
+ return ret;
+}
+
+static struct nvmem_config otp_config = {
+ .name = "rockchip-otp",
+ .read_only = true,
+ .stride = 1,
+ .word_size = 1,
+ .reg_read = rockchip_otp_read,
+};
+
+static const char * const px30_otp_clocks[] = {
+ "otp", "apb_pclk", "phy",
+};
+
+static const struct rockchip_data px30_data = {
+ .size = 0x40,
+ .clks = px30_otp_clocks,
+ .num_clks = ARRAY_SIZE(px30_otp_clocks),
+ .reg_read = px30_otp_read,
+};
+
+static const char * const rk3568_otp_clocks[] = {
+ "usr", "sbpi", "apb", "phy",
+};
+
+static const struct rockchip_data rk3568_data = {
+ .size = 0x80,
+ .clks = rk3568_otp_clocks,
+ .num_clks = ARRAY_SIZE(rk3568_otp_clocks),
+ .reg_read = rk3568_otp_read,
+};
+
+static const char * const rk3588_otp_clocks[] = {
+ "otp", "apb_pclk", "phy", "arb",
+};
+
+static const struct rockchip_data rk3588_data = {
+ .size = 0x400,
+ .clks = rk3588_otp_clocks,
+ .num_clks = ARRAY_SIZE(rk3588_otp_clocks),
+ .reg_read = rk3588_otp_read,
+};
+
+static __maybe_unused const struct of_device_id rockchip_otp_match[] = {
+ {
+ .compatible = "rockchip,px30-otp",
+ .data = &px30_data,
+ },
+ {
+ .compatible = "rockchip,rk3308-otp",
+ .data = &px30_data,
+ },
+ {
+ .compatible = "rockchip,rk3568-otp",
+ .data = &rk3568_data,
+ },
+ {
+ .compatible = "rockchip,rk3588-otp",
+ .data = &rk3588_data,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_otp_match);
+
+static int rockchip_otp_probe(struct device *dev)
+{
+ struct rockchip_otp *otp;
+ const struct rockchip_data *data;
+ struct nvmem_device *nvmem;
+ struct resource *res;
+ int ret, i;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return dev_err_probe(dev, -EINVAL, "failed to get match data\n");
+
+ otp = kzalloc(sizeof(*otp), GFP_KERNEL);
+ if (!otp)
+ return -ENOMEM;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ goto err_free;
+ }
+
+ otp->data = data;
+ otp->dev = dev;
+ otp->base = IOMEM(res->start);
+
+ otp->clks = kcalloc(data->num_clks, sizeof(*otp->clks), GFP_KERNEL);
+ if (!otp->clks) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ for (i = 0; i < data->num_clks; ++i)
+ otp->clks[i].id = data->clks[i];
+
+ ret = clk_bulk_get_optional(dev, data->num_clks, otp->clks);
+ if (ret)
+ goto err_clk;
+
+ otp->rst = reset_control_get(dev, NULL);
+ if (IS_ERR(otp->rst)) {
+ ret = PTR_ERR(otp->rst);
+ goto err_rst;
+ }
+
+ otp_config.size = data->size;
+ otp_config.priv = otp;
+ otp_config.dev = dev;
+
+ nvmem = nvmem_register(&otp_config);
+ if (!IS_ERR(nvmem))
+ return 0;
+
+ ret = PTR_ERR(nvmem);
+
+ reset_control_put(otp->rst);
+
+err_rst:
+ clk_bulk_put_all(data->num_clks, otp->clks);
+
+err_clk:
+ kfree(otp->clks);
+
+err_free:
+ kfree(otp);
+
+ return ret;
+}
+
+static struct driver rockchip_otp_driver = {
+ .name = "rockchip-otp",
+ .probe = rockchip_otp_probe,
+ .of_compatible = DRV_OF_COMPAT(rockchip_otp_match),
+};
+device_platform_driver(rockchip_otp_driver);
diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c
index ddfc15e147..9bbee6d587 100644
--- a/drivers/nvmem/snvs_lpgpr.c
+++ b/drivers/nvmem/snvs_lpgpr.c
@@ -10,7 +10,7 @@
#include <of.h>
#include <of_device.h>
#include <malloc.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
#include <linux/nvmem-provider.h>
@@ -27,7 +27,7 @@ struct snvs_lpgpr_cfg {
};
struct snvs_lpgpr_priv {
- struct device_d *dev;
+ struct device *dev;
struct regmap *regmap;
struct nvmem_config cfg;
const struct snvs_lpgpr_cfg *dcfg;
@@ -61,7 +61,7 @@ static int snvs_lpgpr_write(void *ctx, unsigned offset, const void *val, size_t
return -EPERM;
return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
- bytes);
+ bytes / 4);
}
static int snvs_lpgpr_read(void *ctx, unsigned offset, void *val, size_t bytes)
@@ -70,17 +70,12 @@ static int snvs_lpgpr_read(void *ctx, unsigned offset, void *val, size_t bytes)
const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
return regmap_bulk_read(priv->regmap, dcfg->offset + offset,
- val, bytes);
+ val, bytes / 4);
}
-static const struct nvmem_bus snvs_lpgpr_nvmem_bus = {
- .write = snvs_lpgpr_write,
- .read = snvs_lpgpr_read,
-};
-
-static int snvs_lpgpr_probe(struct device_d *dev)
+static int snvs_lpgpr_probe(struct device *dev)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
struct device_node *syscon_node;
struct snvs_lpgpr_priv *priv;
struct nvmem_config *cfg;
@@ -112,7 +107,8 @@ static int snvs_lpgpr_probe(struct device_d *dev)
cfg->stride = 4;
cfg->word_size = 4;
cfg->size = 4;
- cfg->bus = &snvs_lpgpr_nvmem_bus;
+ cfg->reg_write = snvs_lpgpr_write;
+ cfg->reg_read = snvs_lpgpr_read;
nvmem = nvmem_register(cfg);
if (IS_ERR(nvmem)) {
@@ -128,8 +124,9 @@ static __maybe_unused struct of_device_id snvs_lpgpr_dt_ids[] = {
{ .compatible = "fsl,imx6ul-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
{ },
};
+MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
-static struct driver_d snvs_lpgpr_driver = {
+static struct driver snvs_lpgpr_driver = {
.name = "snvs_lpgpr",
.probe = snvs_lpgpr_probe,
.of_compatible = DRV_OF_COMPAT(snvs_lpgpr_dt_ids),
diff --git a/drivers/nvmem/starfive-otp.c b/drivers/nvmem/starfive-otp.c
index 52c5292839..47b94b1399 100644
--- a/drivers/nvmem/starfive-otp.c
+++ b/drivers/nvmem/starfive-otp.c
@@ -8,12 +8,12 @@
#include <malloc.h>
#include <xfuncs.h>
#include <errno.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#include <init.h>
#include <net.h>
#include <io.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <machine_id.h>
#include <linux/reset.h>
#include <linux/clk.h>
@@ -87,7 +87,7 @@
*/
struct starfive_otp {
- int power_gpio;
+ struct gpio_desc *power_gpio;
struct starfive_otp_regs __iomem *regs;
};
@@ -112,7 +112,7 @@ static int starfive_otp_read(void *ctx, unsigned offset, unsigned *val)
{
struct starfive_otp *priv = ctx;
- gpio_set_active(priv->power_gpio, true);
+ gpiod_set_value(priv->power_gpio, true);
mdelay(10);
//otp set to read mode
@@ -122,7 +122,7 @@ static int starfive_otp_read(void *ctx, unsigned offset, unsigned *val)
/* read all requested fuses */
*val = readl(&priv->regs->mem[offset / 4]);
- gpio_set_active(priv->power_gpio, false);
+ gpiod_set_value(priv->power_gpio, false);
mdelay(5);
return 0;
@@ -138,7 +138,7 @@ static struct regmap_bus starfive_otp_regmap_bus = {
.reg_write = starfive_otp_write,
};
-static int starfive_otp_probe(struct device_d *dev)
+static int starfive_otp_probe(struct device *dev)
{
struct starfive_otp *priv;
struct regmap_config config = {};
@@ -162,7 +162,7 @@ static int starfive_otp_probe(struct device_d *dev)
if (IS_ERR(iores))
return PTR_ERR(iores);
- ret = of_property_read_u32(dev->device_node, "fuse-count", &total_fuses);
+ ret = of_property_read_u32(dev->of_node, "fuse-count", &total_fuses);
if (ret < 0) {
dev_err(dev, "missing required fuse-count property\n");
return ret;
@@ -172,14 +172,14 @@ static int starfive_otp_probe(struct device_d *dev)
config.reg_bits = 32;
config.val_bits = 32;
config.reg_stride = 4;
- config.max_register = total_fuses;
+ config.max_register = (total_fuses - 1) * config.reg_stride;
priv = xzalloc(sizeof(*priv));
priv->regs = IOMEM(iores->start);
priv->power_gpio = gpiod_get(dev, "power", GPIOD_OUT_LOW);
- if (priv->power_gpio < 0)
- return priv->power_gpio;
+ if (IS_ERR(priv->power_gpio))
+ return PTR_ERR(priv->power_gpio);
map = regmap_init(dev, &starfive_otp_regmap_bus, priv, &config);
if (IS_ERR(map))
@@ -192,8 +192,9 @@ static struct of_device_id starfive_otp_dt_ids[] = {
{ .compatible = "starfive,fu740-otp" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, starfive_otp_dt_ids);
-static struct driver_d starfive_otp_driver = {
+static struct driver starfive_otp_driver = {
.name = "starfive_otp",
.probe = starfive_otp_probe,
.of_compatible = starfive_otp_dt_ids,
diff --git a/drivers/nvmem/stm32-bsec-optee-ta.c b/drivers/nvmem/stm32-bsec-optee-ta.c
new file mode 100644
index 0000000000..f89ce791dd
--- /dev/null
+++ b/drivers/nvmem/stm32-bsec-optee-ta.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
+ *
+ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/tee_drv.h>
+
+#include "stm32-bsec-optee-ta.h"
+
+/*
+ * Read OTP memory
+ *
+ * [in] value[0].a OTP start offset in byte
+ * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock)
+ * [out] memref[1].buffer Output buffer to store read values
+ * [out] memref[1].size Size of OTP to be read
+ *
+ * Return codes:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
+ */
+#define PTA_BSEC_READ_MEM 0x0
+
+/*
+ * Write OTP memory
+ *
+ * [in] value[0].a OTP start offset in byte
+ * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock)
+ * [in] memref[1].buffer Input buffer to read values
+ * [in] memref[1].size Size of OTP to be written
+ *
+ * Return codes:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
+ */
+#define PTA_BSEC_WRITE_MEM 0x1
+
+/* value of PTA_BSEC access type = value[in] b */
+#define SHADOW_ACCESS 0
+#define FUSE_ACCESS 1
+#define LOCK_ACCESS 2
+
+/* Bitfield definition for LOCK status */
+#define LOCK_PERM BIT(30)
+
+/* OP-TEE STM32MP BSEC TA UUID */
+static const uuid_t stm32mp_bsec_ta_uuid =
+ UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5,
+ 0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03);
+
+/*
+ * Check whether this driver supports the BSEC TA in the TEE instance
+ * represented by the params (ver/data) to this function.
+ */
+static int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver,
+ const void *data)
+{
+ /* Currently this driver only supports GP compliant, OP-TEE based TA */
+ if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
+ (ver->gen_caps & TEE_GEN_CAP_GP))
+ return 1;
+ else
+ return 0;
+}
+
+/* Open a session to OP-TEE for STM32MP BSEC TA */
+static int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id)
+{
+ struct tee_ioctl_open_session_arg sess_arg;
+ int rc;
+
+ memset(&sess_arg, 0, sizeof(sess_arg));
+ export_uuid(sess_arg.uuid, &stm32mp_bsec_ta_uuid);
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
+ sess_arg.num_params = 0;
+
+ rc = tee_client_open_session(ctx, &sess_arg, NULL);
+ if ((rc < 0) || (sess_arg.ret != 0)) {
+ pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n",
+ __func__, sess_arg.ret, rc);
+ if (!rc)
+ rc = -EINVAL;
+ } else {
+ *id = sess_arg.session;
+ }
+
+ return rc;
+}
+
+/* close a session to OP-TEE for STM32MP BSEC TA */
+static void stm32_bsec_ta_close_session(void *ctx, u32 id)
+{
+ tee_client_close_session(ctx, id);
+}
+
+/* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */
+int stm32_bsec_optee_ta_open(struct tee_context **ctx)
+{
+ struct tee_context *tee_ctx;
+ u32 session_id;
+ int rc;
+
+ /* Open context with TEE driver */
+ tee_ctx = tee_client_open_context(NULL, stm32_bsec_optee_ta_match, NULL, NULL);
+ if (IS_ERR(tee_ctx)) {
+ rc = PTR_ERR(tee_ctx);
+ if (rc == -ENOENT)
+ return -EPROBE_DEFER;
+ pr_err("%s: tee_client_open_context failed (%d)\n", __func__, rc);
+
+ return rc;
+ }
+
+ /* Check STM32MP BSEC TA presence */
+ rc = stm32_bsec_ta_open_session(tee_ctx, &session_id);
+ if (rc) {
+ tee_client_close_context(tee_ctx);
+ return rc;
+ }
+
+ stm32_bsec_ta_close_session(tee_ctx, session_id);
+
+ *ctx = tee_ctx;
+
+ return 0;
+}
+
+/* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */
+void stm32_bsec_optee_ta_close(void *ctx)
+{
+ tee_client_close_context(ctx);
+}
+
+/* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */
+int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
+ void *buf, size_t bytes)
+{
+ struct tee_shm *shm;
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[2];
+ u8 *shm_buf;
+ u32 start, num_bytes;
+ int ret;
+ u32 session_id;
+
+ ret = stm32_bsec_ta_open_session(ctx, &session_id);
+ if (ret)
+ return ret;
+
+ memset(&arg, 0, sizeof(arg));
+ memset(&param, 0, sizeof(param));
+
+ arg.func = PTA_BSEC_READ_MEM;
+ arg.session = session_id;
+ arg.num_params = 2;
+
+ /* align access on 32bits */
+ start = ALIGN_DOWN(offset, 4);
+ num_bytes = round_up(offset + bytes - start, 4);
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+ param[0].u.value.a = start;
+ param[0].u.value.b = SHADOW_ACCESS;
+
+ shm = tee_shm_alloc_kernel_buf(ctx, num_bytes);
+ if (IS_ERR(shm)) {
+ ret = PTR_ERR(shm);
+ goto out_tee_session;
+ }
+
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param[1].u.memref.shm = shm;
+ param[1].u.memref.size = num_bytes;
+
+ ret = tee_client_invoke_func(ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n",
+ arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+ if (!ret) {
+ shm_buf = tee_shm_get_va(shm, 0);
+ if (IS_ERR(shm_buf)) {
+ ret = PTR_ERR(shm_buf);
+ pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
+ } else {
+ /* read data from 32 bits aligned buffer */
+ memcpy(buf, &shm_buf[offset % 4], bytes);
+ }
+ }
+
+ tee_shm_free(shm);
+
+out_tee_session:
+ stm32_bsec_ta_close_session(ctx, session_id);
+
+ return ret;
+}
+
+/* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
+int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
+ unsigned int offset, void *buf, size_t bytes)
+{ struct tee_shm *shm;
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[2];
+ u8 *shm_buf;
+ int ret;
+ u32 session_id;
+
+ ret = stm32_bsec_ta_open_session(ctx, &session_id);
+ if (ret)
+ return ret;
+
+ /* Allow only writing complete 32-bits aligned words */
+ if ((bytes % 4) || (offset % 4))
+ return -EINVAL;
+
+ memset(&arg, 0, sizeof(arg));
+ memset(&param, 0, sizeof(param));
+
+ arg.func = PTA_BSEC_WRITE_MEM;
+ arg.session = session_id;
+ arg.num_params = 2;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+ param[0].u.value.a = offset;
+ param[0].u.value.b = FUSE_ACCESS;
+
+ shm = tee_shm_alloc_kernel_buf(ctx, bytes);
+ if (IS_ERR(shm)) {
+ ret = PTR_ERR(shm);
+ goto out_tee_session;
+ }
+
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ param[1].u.memref.shm = shm;
+ param[1].u.memref.size = bytes;
+
+ shm_buf = tee_shm_get_va(shm, 0);
+ if (IS_ERR(shm_buf)) {
+ ret = PTR_ERR(shm_buf);
+ pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
+ tee_shm_free(shm);
+
+ goto out_tee_session;
+ }
+
+ memcpy(shm_buf, buf, bytes);
+
+ ret = tee_client_invoke_func(ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+ pr_debug("Write OTPs %d to %zu, ret=%d\n", offset / 4, (offset + bytes) / 4, ret);
+
+ /* Lock the upper OTPs with ECC protection, word programming only */
+ if (!ret && ((offset + bytes) >= (lower * 4))) {
+ u32 start, nb_lock;
+ u32 *lock = (u32 *)shm_buf;
+ int i;
+
+ /*
+ * don't lock the lower OTPs, no ECC protection and incremental
+ * bit programming, a second write is allowed
+ */
+ start = max_t(u32, offset, lower * 4);
+ nb_lock = (offset + bytes - start) / 4;
+
+ param[0].u.value.a = start;
+ param[0].u.value.b = LOCK_ACCESS;
+ param[1].u.memref.size = nb_lock * 4;
+
+ for (i = 0; i < nb_lock; i++)
+ lock[i] = LOCK_PERM;
+
+ ret = tee_client_invoke_func(ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+ pr_debug("Lock upper OTPs %d to %d, ret=%d\n",
+ start / 4, start / 4 + nb_lock, ret);
+ }
+
+ tee_shm_free(shm);
+
+out_tee_session:
+ stm32_bsec_ta_close_session(ctx, session_id);
+
+ return ret;
+}
diff --git a/drivers/nvmem/stm32-bsec-optee-ta.h b/drivers/nvmem/stm32-bsec-optee-ta.h
new file mode 100644
index 0000000000..7180476c40
--- /dev/null
+++ b/drivers/nvmem/stm32-bsec-optee-ta.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
+ *
+ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
+ */
+
+#include <asm-generic/errno.h>
+#include <linux/types.h>
+
+struct tee_context;
+
+#if IS_ENABLED(CONFIG_STM32_BSEC_OPTEE_TA)
+/**
+ * stm32_bsec_optee_ta_open() - initialize the STM32 BSEC TA
+ * @ctx: the OP-TEE context on success
+ *
+ * Return:
+ * On success, 0. On failure, -errno.
+ */
+int stm32_bsec_optee_ta_open(struct tee_context **ctx);
+
+/**
+ * stm32_bsec_optee_ta_close() - release the STM32 BSEC TA
+ * @ctx: the OP-TEE context
+ *
+ * This function used to clean the OP-TEE resources initialized in
+ * stm32_bsec_optee_ta_open(); it can be used as callback to
+ * devm_add_action_or_reset()
+ */
+void stm32_bsec_optee_ta_close(void *ctx);
+
+/**
+ * stm32_bsec_optee_ta_read() - nvmem read access using TA client driver
+ * @ctx: the OP-TEE context provided by stm32_bsec_optee_ta_open
+ * @offset: nvmem offset
+ * @buf: buffer to fill with nvem values
+ * @bytes: number of bytes to read
+ *
+ * Return:
+ * On success, 0. On failure, -errno.
+ */
+int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
+ void *buf, size_t bytes);
+
+/**
+ * stm32_bsec_optee_ta_write() - nvmem write access using TA client driver
+ * @ctx: the OP-TEE context provided by stm32_bsec_optee_ta_open
+ * @lower: number of lower OTP, not protected by ECC
+ * @offset: nvmem offset
+ * @buf: buffer with nvem values
+ * @bytes: number of bytes to write
+ *
+ * Return:
+ * On success, 0. On failure, -errno.
+ */
+int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
+ unsigned int offset, void *buf, size_t bytes);
+
+#else
+
+static inline int stm32_bsec_optee_ta_open(struct tee_context **ctx)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void stm32_bsec_optee_ta_close(void *ctx)
+{
+}
+
+static inline int stm32_bsec_optee_ta_read(struct tee_context *ctx,
+ unsigned int offset, void *buf,
+ size_t bytes)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int stm32_bsec_optee_ta_write(struct tee_context *ctx,
+ unsigned int lower,
+ unsigned int offset, void *buf,
+ size_t bytes)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_NVMEM_STM32_BSEC_OPTEE_TA */
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 7283331ba9..2791100a2d 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -5,14 +5,18 @@ config OFTREE
config OFTREE_MEM_GENERIC
depends on OFTREE
- depends on PPC || ARM || EFI_BOOTUP || OPENRISC || SANDBOX || RISCV || KVX
+ depends on PPC || ARM || EFI_PAYLOAD || OPENRISC || SANDBOX || RISCV || KVX
def_bool y
config DTC
bool
+config OF
+ bool
+
config OFDEVICE
select OFTREE
+ select OF
select DTC
bool "Enable probing of devices from the devicetree"
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 65e67973ad..03868406e2 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -362,7 +362,7 @@ static u64 __of_translate_address(struct device_node *dev,
int na, ns, pna, pns;
u64 result = OF_BAD_ADDR;
- pr_vdebug("OF: ** translation for device %s **\n", dev->full_name);
+ pr_vdebug("OF: ** translation for device %pOF **\n", dev);
/* Get parent & match bus type */
parent = of_get_parent(dev);
@@ -373,14 +373,13 @@ static u64 __of_translate_address(struct device_node *dev,
/* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
- pr_vdebug("prom_parse: Bad cell count for %s\n",
- dev->full_name);
+ pr_vdebug("prom_parse: Bad cell count for %pOF\n", dev);
return OF_BAD_ADDR;
}
memcpy(addr, in_addr, na * 4);
- pr_vdebug("OF: bus is %s (na=%d, ns=%d) on %s\n",
- bus->name, na, ns, parent->full_name);
+ pr_vdebug("OF: bus is %s (na=%d, ns=%d) on %pOF\n",
+ bus->name, na, ns, parent);
of_dump_addr("OF: translating address:", addr, na);
/* Translate */
@@ -400,13 +399,12 @@ static u64 __of_translate_address(struct device_node *dev,
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
- dev->full_name);
+ printk(KERN_ERR "prom_parse: Bad cell count for %pOF\n", dev);
break;
}
- pr_vdebug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
- pbus->name, pna, pns, parent->full_name);
+ pr_vdebug("OF: parent bus is %s (na=%d, ns=%d) on %pOF\n",
+ pbus->name, pna, pns, parent);
/* Apply bus translation */
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
diff --git a/drivers/of/barebox.c b/drivers/of/barebox.c
index f6e795e047..560d9c0d15 100644
--- a/drivers/of/barebox.c
+++ b/drivers/of/barebox.c
@@ -10,7 +10,6 @@
#include <io.h>
#include <of.h>
#include <malloc.h>
-#include <partition.h>
#include <envfs.h>
#include <fs.h>
@@ -19,7 +18,7 @@
/* If dev describes a file on a fs, mount the fs and change devpath to
* point to the file's path. Otherwise leave devpath alone. Does
* nothing in env in a file support isn't enabled. */
-static int environment_check_mount(struct device_d *dev, char **devpath)
+static int environment_check_mount(struct device *dev, char **devpath)
{
const char *filepath;
int ret;
@@ -27,7 +26,7 @@ static int environment_check_mount(struct device_d *dev, char **devpath)
if (!IS_ENABLED(CONFIG_OF_BAREBOX_ENV_IN_FS))
return 0;
- ret = of_property_read_string(dev->device_node, "file-path", &filepath);
+ ret = of_property_read_string(dev->of_node, "file-path", &filepath);
if (ret == -EINVAL) {
/* No file-path so just use device-path */
return 0;
@@ -53,12 +52,13 @@ static int environment_check_mount(struct device_d *dev, char **devpath)
return 0;
}
-static int environment_probe(struct device_d *dev)
+static int environment_probe(struct device *dev)
{
char *path;
int ret;
- ret = of_find_path(dev->device_node, "device-path", &path, OF_FIND_PATH_FLAGS_BB);
+ ret = of_find_path(dev->of_node, "device-path", &path,
+ OF_FIND_PATH_FLAGS_BB);
if (ret)
return ret;
@@ -80,8 +80,9 @@ static struct of_device_id environment_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, environment_dt_ids);
-static struct driver_d environment_driver = {
+static struct driver environment_driver = {
.name = "barebox-environment",
.probe = environment_probe,
.of_compatible = environment_dt_ids,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index ea2a88764b..3b8878f34b 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -17,12 +17,28 @@
#include <linux/sizes.h>
#include <of_graph.h>
#include <string.h>
+#include <libfile.h>
#include <linux/clk.h>
#include <linux/ctype.h>
#include <linux/err.h>
static struct device_node *root_node;
+/**
+ * of_node_has_prefix - Test if a node name has a given prefix
+ * @np: The node name to test
+ * @prefix: The prefix to see if @np starts with
+ *
+ * Returns:
+ * * strlen(@prefix) if @np starts with @prefix
+ * * 0 if @np does not start with @prefix
+ */
+size_t of_node_has_prefix(const struct device_node *np, const char *prefix)
+{
+ return np ? str_has_prefix(kbasename(np->full_name), prefix) : 0;
+}
+EXPORT_SYMBOL(of_node_has_prefix);
+
bool of_node_name_eq(const struct device_node *np, const char *name)
{
const char *node_name;
@@ -32,7 +48,7 @@ bool of_node_name_eq(const struct device_node *np, const char *name)
return false;
node_name = kbasename(np->full_name);
- len = strchrnul(node_name, '@') - node_name;
+ len = strchrnul(node_name, '@') - node_name;
return (strlen(name) == len) && (strncmp(node_name, name, len) == 0);
}
@@ -153,8 +169,8 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
strncpy(ap->stem, stem, stem_len);
ap->stem[stem_len] = 0;
list_add_tail(&ap->link, &aliases_lookup);
- pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n",
- ap->alias, ap->stem, ap->id, np->full_name);
+ pr_debug("adding DT alias:%s: stem=%s id=%i node=%pOF\n",
+ ap->alias, ap->stem, ap->id, np);
}
static struct device_node *of_alias_resolve(struct device_node *root, struct property *pp)
@@ -530,7 +546,9 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
EXPORT_SYMBOL(of_get_cpu_node);
/** Checks if the given "compat" string matches one of the strings in
- * the device's "compatible" property
+ * the device's "compatible" property. Returns 0 on mismatch and a
+ * positive score on match with the maximum being OF_DEVICE_COMPATIBLE_MAX_SCORE,
+ * which is only returned if the first compatible matched.
*/
int of_device_is_compatible(const struct device_node *device,
const char *compat)
@@ -543,7 +561,7 @@ int of_device_is_compatible(const struct device_node *device,
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
- score = INT_MAX/2 - (index << 2);
+ score = OF_DEVICE_COMPATIBLE_MAX_SCORE - (index << 2);
break;
}
}
@@ -699,6 +717,9 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches,
if (score > best_score) {
best_match = matches;
best_score = score;
+
+ if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE)
+ break;
}
}
@@ -739,11 +760,11 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from,
}
EXPORT_SYMBOL(of_find_matching_node_and_match);
-int of_match(struct device_d *dev, struct driver_d *drv)
+int of_match(struct device *dev, struct driver *drv)
{
const struct of_device_id *id;
- id = of_match_node(drv->of_compatible, dev->device_node);
+ id = of_match_node(drv->of_compatible, dev->of_node);
if (!id)
return 1;
@@ -752,22 +773,24 @@ int of_match(struct device_d *dev, struct driver_d *drv)
return 0;
}
EXPORT_SYMBOL(of_match);
-
/**
* of_find_property_value_of_size
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
- * @len: requested length of property value
+ * @min: minimum allowed length of property value
+ * @max: maximum allowed length of property value (0 means unlimited)
+ * @len: if !=NULL, actual length is written to here
*
* Search for a property in a device node and valid the requested size.
- * Returns the property value on success, -EINVAL if the property does not
- * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
+ *
+ * Return: The property value on success, -EINVAL if the property does not
+ * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data is too small or too large.
*
*/
static const void *of_find_property_value_of_size(const struct device_node *np,
- const char *propname, u32 len)
+ const char *propname, u32 min, u32 max, size_t *len)
{
struct property *prop = of_find_property(np, propname, NULL);
const void *value;
@@ -777,8 +800,13 @@ static const void *of_find_property_value_of_size(const struct device_node *np,
value = of_property_get_value(prop);
if (!value)
return ERR_PTR(-ENODATA);
- if (len > prop->length)
+ if (prop->length < min)
return ERR_PTR(-EOVERFLOW);
+ if (max && prop->length > max)
+ return ERR_PTR(-EOVERFLOW);
+
+ if (len)
+ *len = prop->length;
return value;
}
@@ -803,7 +831,8 @@ int of_property_read_u32_index(const struct device_node *np,
u32 index, u32 *out_value)
{
const u32 *val = of_find_property_value_of_size(np, propname,
- ((index + 1) * sizeof(*out_value)));
+ ((index + 1) * sizeof(*out_value)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -832,7 +861,7 @@ int of_property_count_elems_of_size(const struct device_node *np,
if (!prop)
return -EINVAL;
- if (!prop->value)
+ if (!of_property_get_value(prop))
return -ENODATA;
if (prop->length % elem_size != 0) {
@@ -867,7 +896,8 @@ int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz)
{
const u8 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz * sizeof(*out_values)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -900,7 +930,8 @@ int of_property_read_u16_array(const struct device_node *np,
const char *propname, u16 *out_values, size_t sz)
{
const __be16 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz * sizeof(*out_values)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -932,7 +963,8 @@ int of_property_read_u32_array(const struct device_node *np,
size_t sz)
{
const __be32 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz * sizeof(*out_values)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -960,7 +992,7 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
u64 *out_value)
{
const __be32 *val = of_find_property_value_of_size(np, propname,
- sizeof(*out_value));
+ sizeof(*out_value), 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -971,7 +1003,154 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
EXPORT_SYMBOL_GPL(of_property_read_u64);
/**
- * of_property_read_u64_array - Find and read an array of 64 bit integers
+ * of_property_read_variable_u8_array - Find and read an array of u8 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to found values.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it.
+ *
+ * dts entry of array should be like:
+ * ``property = /bits/ 8 <0x50 0x60 0x70>;``
+ *
+ * Return: The number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_variable_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const u8 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = *val++;
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
+
+/**
+ * of_property_read_variable_u16_array - Find and read an array of u16 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to found values.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 16-bit value(s) from
+ * it.
+ *
+ * dts entry of array should be like:
+ * ``property = /bits/ 16 <0x5000 0x6000 0x7000>;``
+ *
+ * Return: The number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_variable_u16_array(const struct device_node *np,
+ const char *propname, u16 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be16 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = be16_to_cpup(val++);
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
+
+/**
+ * of_property_read_variable_u32_array - Find and read an array of 32 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return found values.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it.
+ *
+ * Return: The number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_variable_u32_array(const struct device_node *np,
+ const char *propname, u32 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = be32_to_cpup(val++);
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
+
+/**
+ * of_property_read_variable_u64_array - Find and read an array of 64 bit integers
* from a property.
*
* @np: device node from which the property value is to be read.
@@ -988,15 +1167,22 @@ EXPORT_SYMBOL_GPL(of_property_read_u64);
*/
int of_property_read_variable_u64_array(const struct device_node *np,
const char *propname, u64 *out_values,
- size_t sz)
+ size_t sz_min, size_t sz_max)
{
- size_t count;
+ size_t sz, count;
const __be32 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
if (IS_ERR(val))
return PTR_ERR(val);
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
count = sz;
while (count--) {
*out_values++ = of_read_number(val, 2);
@@ -1049,7 +1235,7 @@ EXPORT_SYMBOL_GPL(of_property_read_string);
* This function searches a string list property and returns the index
* of a specific string value.
*/
-int of_property_match_string(struct device_node *np, const char *propname,
+int of_property_match_string(const struct device_node *np, const char *propname,
const char *string)
{
struct property *prop = of_find_property(np, propname, NULL);
@@ -1130,6 +1316,7 @@ EXPORT_SYMBOL_GPL(of_prop_next_string);
*
* @np: device node from which the property is to be set.
* @propname: name of the property to be set.
+ * @value true to set, false to delete
*
* Search for a property in a device node and create or delete the property.
* If the property already exists and write value is false, the property is
@@ -1472,15 +1659,13 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
*/
node = of_find_node_by_phandle(phandle);
if (!node) {
- pr_err("%s: could not find phandle\n",
- np->full_name);
+ pr_err("%pOF: could not find phandle\n", np);
goto err;
}
if (cells_name &&
of_property_read_u32(node, cells_name, &count)) {
- pr_err("%s: could not get %s for %s\n",
- np->full_name, cells_name,
- node->full_name);
+ pr_err("%pOF: could not get %s for %pOF\n",
+ np, cells_name, node);
goto err;
}
@@ -1489,8 +1674,7 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
* remaining property data length
*/
if (list + count > list_end) {
- pr_err("%s: arguments longer than property\n",
- np->full_name);
+ pr_err("%pOF: arguments longer than property\n", np);
goto err;
}
}
@@ -1756,7 +1940,7 @@ int barebox_register_of(struct device_node *root)
of_fix_tree(root);
if (IS_ENABLED(CONFIG_OFDEVICE)) {
- of_clk_init(root, NULL);
+ of_clk_init();
if (!deep_probe_is_supported())
return of_probe();
}
@@ -1990,9 +2174,9 @@ int of_property_read_string_helper(const struct device_node *np,
if (!prop)
return -EINVAL;
- if (!prop->value)
+ p = of_property_get_value(prop);
+ if (!p)
return -ENODATA;
- p = prop->value;
end = p + prop->length;
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
@@ -2209,8 +2393,8 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
if (parent) {
node->name = xstrdup(name);
- node->full_name = basprintf("%s/%s",
- node->parent->full_name, name);
+ node->full_name = basprintf("%pOF/%s",
+ node->parent, name);
list_add(&node->list, &parent->list);
} else {
node->name = xstrdup("");
@@ -2317,6 +2501,25 @@ struct property *of_rename_property(struct device_node *np,
return pp;
}
+struct property *of_copy_property(const struct device_node *src,
+ const char *propname,
+ struct device_node *dst)
+{
+ struct property *prop;
+
+ prop = of_find_property(src, propname, NULL);
+ if (!prop)
+ return NULL;
+
+ if (of_property_present(dst, propname))
+ return ERR_PTR(-EEXIST);
+
+ return of_new_property(dst, propname,
+ of_property_get_value(prop), prop->length);
+}
+EXPORT_SYMBOL_GPL(of_copy_property);
+
+
/**
* of_set_property - create a property for a given node
* @node - the node
@@ -2486,6 +2689,7 @@ const struct of_device_id of_default_bus_match_table[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_default_bus_match_table);
static int of_probe_memory(void)
{
@@ -2514,7 +2718,7 @@ mem_initcall(of_probe_memory);
static void of_platform_device_create_root(struct device_node *np)
{
- static struct device_d *dev;
+ static struct device *dev;
int ret;
if (dev)
@@ -2522,7 +2726,7 @@ static void of_platform_device_create_root(struct device_node *np)
dev = xzalloc(sizeof(*dev));
dev->id = DEVICE_ID_SINGLE;
- dev->device_node = np;
+ dev->of_node = np;
dev_set_name(dev, "machine");
ret = platform_device_register(dev);
@@ -2534,6 +2738,7 @@ static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "nvmem-rmem" },
{}
};
+MODULE_DEVICE_TABLE(of, reserved_mem_matches);
/**
* of_probe - Probe unflattened device tree starting at of_get_root_node
@@ -2616,25 +2821,35 @@ out:
return dn;
}
-struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other)
+void of_merge_nodes(struct device_node *np, const struct device_node *other)
{
- struct device_node *np, *child;
+ struct device_node *child;
struct property *pp;
- np = of_new_node(parent, other->name);
- np->phandle = other->phandle;
-
list_for_each_entry(pp, &other->properties, list)
of_new_property(np, pp->name, pp->value, pp->length);
for_each_child_of_node(other, child)
of_copy_node(np, child);
+}
+
+struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other)
+{
+ struct device_node *np;
+
+ np = of_new_node(parent, other->name);
+ np->phandle = other->phandle;
+
+ of_merge_nodes(np, other);
return np;
}
struct device_node *of_dup(const struct device_node *root)
{
+ if (IS_ERR_OR_NULL(root))
+ return ERR_CAST(root);
+
return of_copy_node(NULL, root);
}
@@ -2667,30 +2882,51 @@ void of_delete_node(struct device_node *node)
free(node);
}
-struct device_node *of_get_stdoutpath(unsigned int *baudrate)
+/*
+ * of_find_node_by_chosen - Find a node given a chosen property pointing at it
+ * @propname: the name of the property containing a path or alias
+ * The function will lookup the first string in the property
+ * value up to the first : character or till \0.
+ * @options The Remainder (without : or \0 at the end) will be written
+ * to *options if not NULL.
+ */
+struct device_node *of_find_node_by_chosen(const char *propname,
+ const char **options)
{
+ const char *value, *p;
+ char *buf;
struct device_node *dn;
- const char *name;
- const char *p;
- char *q;
- name = of_get_property(of_chosen, "stdout-path", NULL);
- if (!name)
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ value = of_get_property(of_chosen, propname, NULL);
+ if (!value)
+ return NULL;
- if (!name)
- return 0;
+ p = strchrnul(value, ':');
+ buf = xstrndup(value, p - value);
+
+ dn = of_find_node_by_path_or_alias(NULL, buf);
+
+ free(buf);
- p = strchrnul(name, ':');
+ if (options && *p)
+ *options = p + 1;
- q = xstrndup(name, p - name);
+ return dn;
+}
- dn = of_find_node_by_path_or_alias(NULL, q);
+struct device_node *of_get_stdoutpath(unsigned int *baudrate)
+{
+ const char *opts = NULL;
+ struct device_node *dn;
- free(q);
+ dn = of_find_node_by_chosen("stdout-path", &opts);
+ if (!dn)
+ dn = of_find_node_by_chosen("linux,stdout-path", &opts);
+ if (!dn)
+ return NULL;
- if (baudrate && *p) {
- unsigned rate = simple_strtoul(p + 1, NULL, 10);
+ if (baudrate && opts) {
+ unsigned rate = simple_strtoul(opts, NULL, 10);
if (rate)
*baudrate = rate;
}
@@ -2698,11 +2934,11 @@ struct device_node *of_get_stdoutpath(unsigned int *baudrate)
return dn;
}
-int of_device_is_stdout_path(struct device_d *dev, unsigned int *baudrate)
+int of_device_is_stdout_path(struct device *dev, unsigned int *baudrate)
{
unsigned int tmp = *baudrate;
- if (!dev || !dev->device_node || dev->device_node != of_get_stdoutpath(&tmp))
+ if (!dev || !dev->of_node || dev->of_node != of_get_stdoutpath(&tmp))
return false;
*baudrate = tmp;
@@ -2790,6 +3026,21 @@ int of_device_enable_path(const char *path)
}
/**
+ * of_device_enable_by_alias - enable a device node by alias
+ * @alias - the alias of the device tree node to enable
+ */
+int of_device_enable_by_alias(const char *alias)
+{
+ struct device_node *node;
+
+ node = of_find_node_by_alias(NULL, alias);
+ if (!node)
+ return -ENODEV;
+
+ return of_device_enable(node);
+}
+
+/**
* of_device_disable - disable a devicenode device
* @node - the node to disable
*
@@ -2833,6 +3084,37 @@ int of_device_disable_by_alias(const char *alias)
}
/**
+ * of_read_file - unflatten oftree file
+ * @filename - path to file to unflatten its contents
+ *
+ * Returns the root node of the tree or an error pointer on error.
+ */
+struct device_node *of_read_file(const char *filename)
+{
+ void *fdt;
+ size_t size;
+ struct device_node *root;
+
+ fdt = read_file(filename, &size);
+ if (!fdt) {
+ pr_err("unable to read %s: %m\n", filename);
+ return ERR_PTR(-errno);
+ }
+
+ if (IS_ENABLED(CONFIG_FILETYPE) && file_detect_type(fdt, size) != filetype_oftree) {
+ pr_err("%s is not a flat device tree file.\n", filename);
+ root = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ root = of_unflatten_dtb(fdt, size);
+out:
+ free(fdt);
+
+ return root;
+}
+
+/**
* of_get_reproducible_name() - get a reproducible name of a node
* @node: The node to get a name from
*
@@ -2939,8 +3221,8 @@ int of_graph_parse_endpoint(const struct device_node *node,
struct device_node *port_node = of_get_parent(node);
if (!port_node)
- pr_warn("%s(): endpoint %s has no parent node\n",
- __func__, node->full_name);
+ pr_warn("%s(): endpoint %pOF has no parent node\n",
+ __func__, node);
memset(endpoint, 0, sizeof(*endpoint));
@@ -3012,15 +3294,15 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
port = of_get_child_by_name(parent, "port");
if (!port) {
- pr_err("%s(): no port node found in %s\n",
- __func__, parent->full_name);
+ pr_err("%s(): no port node found in %pOF\n",
+ __func__, parent);
return NULL;
}
} else {
port = of_get_parent(prev);
if (!port) {
- pr_warn("%s(): endpoint %s has no parent node\n",
- __func__, prev->full_name);
+ pr_warn("%s(): endpoint %pOF has no parent node\n",
+ __func__, prev);
return NULL;
}
}
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 5cc288efbe..77c027b57e 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -12,16 +12,16 @@
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
- const struct device_d *dev)
+ const struct device *dev)
{
- if ((!matches) || (!dev->device_node))
+ if ((!matches) || (!dev->of_node))
return NULL;
- return of_match_node(matches, dev->device_node);
+ return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
-const void *of_device_get_match_data(const struct device_d *dev)
+const void *of_device_get_match_data(const struct device *dev)
{
const struct of_device_id *match;
@@ -33,7 +33,7 @@ const void *of_device_get_match_data(const struct device_d *dev)
}
EXPORT_SYMBOL(of_device_get_match_data);
-const char *of_device_get_match_compatible(const struct device_d *dev)
+const char *of_device_get_match_compatible(const struct device *dev)
{
const struct of_device_id *match;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 01d7dc3743..8dca41990c 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -14,8 +14,24 @@
#include <memory.h>
#include <linux/sizes.h>
#include <linux/ctype.h>
+#include <linux/log2.h>
+#include <linux/overflow.h>
+#include <linux/string_helpers.h>
#include <linux/err.h>
+static inline bool __dt_ptr_ok(const struct fdt_header *fdt, const void *p,
+ unsigned elem_size, unsigned elem_align)
+{
+ if (!p || (const void *)fdt > p || !PTR_IS_ALIGNED(p, elem_align) ||
+ p + elem_size > (const void *)fdt + be32_to_cpu(fdt->totalsize)) {
+ pr_err("unflatten: offset overflows or misaligns FDT\n");
+ return false;
+ }
+
+ return true;
+}
+#define dt_ptr_ok(fdt, p) __dt_ptr_ok(fdt, p, sizeof(*(p)), __alignof__(*(p)))
+
static inline uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int size)
{
dt += size;
@@ -27,29 +43,40 @@ static inline uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int
return dt;
}
-static inline char *dt_string(struct fdt_header *f, char *strstart, uint32_t ofs)
+static inline const char *dt_string(struct fdt_header *f, const char *strstart, uint32_t ofs)
{
+ const char *str;
+
if (ofs > f->size_dt_strings)
return NULL;
- else
- return strstart + ofs;
+
+ str = strstart + ofs;
+
+ return string_is_terminated(str, f->size_dt_strings - ofs) ? str : NULL;
}
static int of_reservemap_num_entries(const struct fdt_header *fdt)
{
- const struct fdt_reserve_entry *r;
+ /*
+ * FDT may violate spec mandated 8-byte alignment if unflattening it out of
+ * a FIT image property, so play it safe here.
+ */
+ const struct fdt_reserve_entry_unaligned {
+ fdt64_t address;
+ fdt64_t size;
+ } __packed *r;
int n = 0;
r = (void *)fdt + be32_to_cpu(fdt->off_mem_rsvmap);
- while (r->size) {
+ while (dt_ptr_ok(fdt, r) && r->size) {
n++;
r++;
if (n == OF_MAX_RESERVE_MAP)
return -EINVAL;
}
- return n;
+ return r->size == 0 ? n : -ESPIPE;
}
/**
@@ -88,6 +115,44 @@ static int of_unflatten_reservemap(struct device_node *root,
return 0;
}
+static int fdt_parse_header(const struct fdt_header *fdt, size_t fdt_size,
+ struct fdt_header *out)
+{
+ if (fdt_size < sizeof(struct fdt_header))
+ return -EINVAL;
+
+ if (fdt->magic != cpu_to_fdt32(FDT_MAGIC)) {
+ pr_err("bad magic: 0x%08x\n", fdt32_to_cpu(fdt->magic));
+ return -EINVAL;
+ }
+
+ if (fdt->version != cpu_to_fdt32(17)) {
+ pr_err("bad dt version: 0x%08x\n", fdt32_to_cpu(fdt->version));
+ return -EINVAL;
+ }
+
+ out->totalsize = fdt32_to_cpu(fdt->totalsize);
+ out->off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct);
+ out->size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct);
+ out->off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings);
+ out->size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings);
+
+ if (out->totalsize > fdt_size)
+ return -EINVAL;
+
+ if (size_add(out->off_dt_struct, out->size_dt_struct) > out->totalsize) {
+ pr_err("unflatten: dt size exceeds total size\n");
+ return -ESPIPE;
+ }
+
+ if (size_add(out->off_dt_strings, out->size_dt_strings) > out->totalsize) {
+ pr_err("unflatten: string size exceeds total size\n");
+ return -ESPIPE;
+ }
+
+ return 0;
+}
+
/**
* of_unflatten_dtb - unflatten a dtb binary blob
* @infdt - the fdt blob to unflatten
@@ -113,37 +178,9 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
unsigned int maxlen;
const struct fdt_header *fdt = infdt;
- if (size < sizeof(struct fdt_header))
- return ERR_PTR(-EINVAL);
-
- if (fdt->magic != cpu_to_fdt32(FDT_MAGIC)) {
- pr_err("bad magic: 0x%08x\n", fdt32_to_cpu(fdt->magic));
- return ERR_PTR(-EINVAL);
- }
-
- if (fdt->version != cpu_to_fdt32(17)) {
- pr_err("bad dt version: 0x%08x\n", fdt32_to_cpu(fdt->version));
- return ERR_PTR(-EINVAL);
- }
-
- f.totalsize = fdt32_to_cpu(fdt->totalsize);
- f.off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct);
- f.size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct);
- f.off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings);
- f.size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings);
-
- if (f.totalsize > size)
- return ERR_PTR(-EINVAL);
-
- if (f.off_dt_struct + f.size_dt_struct > f.totalsize) {
- pr_err("unflatten: dt size exceeds total size\n");
- return ERR_PTR(-ESPIPE);
- }
-
- if (f.off_dt_strings + f.size_dt_strings > f.totalsize) {
- pr_err("unflatten: string size exceeds total size\n");
- return ERR_PTR(-ESPIPE);
- }
+ ret = fdt_parse_header(infdt, size, &f);
+ if (ret < 0)
+ return ERR_PTR(ret);
dt_struct = f.off_dt_struct;
dt_strings = (void *)fdt + f.off_dt_strings;
@@ -157,7 +194,13 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
goto err;
while (1) {
- tag = be32_to_cpu(*(uint32_t *)(infdt + dt_struct));
+ __be32 *tagp = (uint32_t *)(infdt + dt_struct);
+ if (!dt_ptr_ok(infdt, tagp)) {
+ ret = -ESPIPE;
+ goto err;
+ }
+
+ tag = be32_to_cpu(*tagp);
switch (tag) {
case FDT_BEGIN_NODE:
@@ -302,15 +345,15 @@ static int lstrcpy(char *dest, const char *src)
int len = 0;
int maxlen = 1023;
- while (*src) {
- *dest++ = *src++;
+ do {
+ *dest++ = *src;
len++;
if (!maxlen)
return -ENOSPC;
maxlen--;
- }
+ } while (*src++);
- return len;
+ return len - 1;
}
static void *memalign_realloc(void *orig, size_t oldsize, size_t newsize)
@@ -343,24 +386,41 @@ static void *memalign_realloc(void *orig, size_t oldsize, size_t newsize)
static int fdt_ensure_space(struct fdt *fdt, int dtsize)
{
+ size_t new_size;
+ void *previous;
+
/*
* We assume strings and names have a maximum length of 1024
* whereas properties can be longer. We allocate new memory
* if we have less than 1024 bytes (+ the property size left.
*/
if (fdt->str_size - fdt->str_nextofs < 1024) {
- fdt->strings = realloc(fdt->strings, fdt->str_size * 2);
- if (!fdt->strings)
+ previous = fdt->strings;
+ new_size = fdt->str_size * 2;
+
+ fdt->strings = realloc(previous, new_size);
+ if (!fdt->strings) {
+ free(previous);
return -ENOMEM;
- fdt->str_size *= 2;
+ }
+
+ fdt->str_size = new_size;
}
if (fdt->dt_size - fdt->dt_nextofs < 1024 + dtsize) {
- fdt->dt = memalign_realloc(fdt->dt, fdt->dt_size,
- fdt->dt_size * 2);
- if (!fdt->dt)
+ previous = fdt->dt;
+ new_size = fdt->dt_size * 2;
+
+ if (new_size <= dtsize)
+ new_size = roundup_pow_of_two(fdt->dt_size + dtsize);
+
+ fdt->dt = memalign_realloc(previous, fdt->dt_size, new_size);
+ if (!fdt->dt) {
+ free(previous);
return -ENOMEM;
- fdt->dt_size *= 2;
+ }
+
+ fdt->dt_size = new_size;
}
return 0;
@@ -554,9 +614,7 @@ void of_clean_reserve_map(void)
* @__fdt: The devicetree blob
*
* This adds the reservemap entries previously collected in
- * of_add_reserve_entry() to a devicetree binary blob. This also
- * adds the devicetree itself to the reserved list, so after calling
- * this function the tree should not be relocated anymore.
+ * of_add_reserve_entry() to a devicetree binary blob.
*/
void fdt_add_reserve_map(void *__fdt)
{
@@ -584,10 +642,135 @@ void fdt_add_reserve_map(void *__fdt)
fdt_res++;
}
- of_write_number(&fdt_res->address, (unsigned long)__fdt, 2);
- of_write_number(&fdt_res->size, be32_to_cpu(fdt->totalsize), 2);
- fdt_res++;
-
of_write_number(&fdt_res->address, 0, 2);
of_write_number(&fdt_res->size, 0, 2);
}
+
+void fdt_print_reserve_map(const void *__fdt)
+{
+ const struct fdt_header *fdt = __fdt;
+ const struct fdt_reserve_entry *fdt_res =
+ __fdt + be32_to_cpu(fdt->off_mem_rsvmap);
+ int n = 0;
+
+ while (1) {
+ uint64_t size = fdt64_to_cpu(fdt_res->size);
+ uint64_t address = fdt64_to_cpu(fdt_res->address);
+
+ if (!size)
+ break;
+
+ printf("/memreserve/ #%d: 0x%08llx - 0x%08llx\n", n, address, address + size - 1);
+
+ n++;
+ fdt_res++;
+ if (n == OF_MAX_RESERVE_MAP)
+ return;
+ }
+}
+
+static int fdt_string_is_compatible(const char *haystack, int haystack_len,
+ const char *needle, int needle_len)
+{
+ const char *p;
+ int index = 0;
+
+ while (haystack_len >= needle_len) {
+ if (memcmp(needle, haystack, needle_len + 1) == 0)
+ return OF_DEVICE_COMPATIBLE_MAX_SCORE - (index << 2);
+
+ p = memchr(haystack, '\0', haystack_len);
+ if (!p)
+ return 0;
+ haystack_len -= (p - haystack) + 1;
+ haystack = p + 1;
+ index++;
+ }
+
+ return 0;
+}
+
+int fdt_machine_is_compatible(const struct fdt_header *fdt, size_t fdt_size, const char *compat)
+{
+ uint32_t tag;
+ const struct fdt_property *fdt_prop;
+ const char *name;
+ uint32_t dt_struct;
+ const struct fdt_node_header *fnh;
+ const void *dt_strings;
+ struct fdt_header f;
+ int ret, len;
+ int expect = FDT_BEGIN_NODE;
+ int compat_len = strlen(compat);
+
+ ret = fdt_parse_header(fdt, fdt_size, &f);
+ if (ret < 0)
+ return 0;
+
+ dt_struct = f.off_dt_struct;
+ dt_strings = (const void *)fdt + f.off_dt_strings;
+
+ while (1) {
+ const __be32 *tagp = (const void *)fdt + dt_struct;
+ if (!dt_ptr_ok(fdt, tagp))
+ return 0;
+
+ tag = be32_to_cpu(*tagp);
+ if (tag != FDT_NOP && tag != expect)
+ return 0;
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ fnh = (const void *)fdt + dt_struct;
+
+ /* The root node must have an empty name */
+ if (fnh->name[0] != '\0')
+ return 0;
+
+ dt_struct = dt_struct_advance(&f, dt_struct,
+ sizeof(struct fdt_node_header) + 1);
+
+ /*
+ * Quoting Device Tree Specification v0.4 §5.4.2:
+ *
+ * [T]his process requires that all property definitions for
+ * a particular node precede any subnode definitions for that
+ * node. Although the structure would not be ambiguous if
+ * properties and subnodes were intermingled, the code needed
+ * to process a flat tree is simplified by this requirement.
+ *
+ * So let's make use of this simplification.
+ */
+ expect = FDT_PROP;
+ break;
+
+ case FDT_PROP:
+ fdt_prop = (const void *)fdt + dt_struct;
+ len = fdt32_to_cpu(fdt_prop->len);
+
+ name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff));
+ if (!name)
+ return 0;
+
+ if (strcmp(name, "compatible")) {
+ dt_struct = dt_struct_advance(&f, dt_struct,
+ sizeof(struct fdt_property) + len);
+ break;
+ }
+
+ return fdt_string_is_compatible(fdt_prop->data, len, compat, compat_len);
+
+ case FDT_NOP:
+ dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE);
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (!dt_struct)
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/drivers/of/of_firmware.c b/drivers/of/of_firmware.c
index 80feb3b90d..c1b69aac04 100644
--- a/drivers/of/of_firmware.c
+++ b/drivers/of/of_firmware.c
@@ -11,7 +11,8 @@ static struct firmware_mgr *of_node_get_mgr(struct device_node *np)
struct device_node *mgr_node;
do {
- mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
+ mgr_node = of_parse_phandle_from(np, of_find_root_node(np),
+ "fpga-mgr", 0);
if (mgr_node)
return firmwaremgr_find_by_node(mgr_node);
} while ((np = of_get_parent(np)) != NULL);
diff --git a/drivers/of/of_gpio.c b/drivers/of/of_gpio.c
index c23923b425..25496a3cf1 100644
--- a/drivers/of/of_gpio.c
+++ b/drivers/of/of_gpio.c
@@ -30,8 +30,8 @@ static void of_gpio_flags_quirks(struct device_node *np,
* be actively ignored.
*/
if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) {
- pr_warn("%s GPIO handle specifies active low - ignored\n",
- np->full_name);
+ pr_warn("%pOF GPIO handle specifies active low - ignored\n",
+ np);
*flags &= ~OF_GPIO_ACTIVE_LOW;
}
if (active_low)
@@ -46,6 +46,44 @@ static void of_gpio_flags_quirks(struct device_node *np,
}
+static struct gpio_chip *of_find_gpiochip_by_xlate(
+ struct of_phandle_args *gpiospec)
+{
+ struct gpio_chip *chip;
+ struct device *dev;
+
+ dev = of_find_device_by_node(gpiospec->np);
+ if (!dev) {
+ pr_debug("%s: unable to find device of node %pOF\n",
+ __func__, gpiospec->np);
+ return NULL;
+ }
+
+ chip = gpio_get_chip_by_dev(dev);
+ if (!chip) {
+ pr_debug("%s: unable to find gpiochip\n", __func__);
+ return NULL;
+ }
+
+ if (!chip->ops->of_xlate ||
+ chip->ops->of_xlate(chip, gpiospec, NULL) < 0) {
+ pr_err("%s: failed to execute of_xlate\n", __func__);
+ return NULL;
+ }
+
+ return chip;
+}
+
+static int of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
+ struct of_phandle_args *gpiospec,
+ enum of_gpio_flags *flags)
+{
+ if (chip->of_gpio_n_cells != gpiospec->args_count)
+ return -EINVAL;
+
+ return chip->ops->of_xlate(chip, gpiospec, flags);
+}
+
/**
* of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API
* @np: device node to get GPIO from
@@ -60,39 +98,36 @@ static void of_gpio_flags_quirks(struct device_node *np,
int of_get_named_gpio_flags(struct device_node *np, const char *propname,
int index, enum of_gpio_flags *flags)
{
- struct of_phandle_args out_args;
- struct device_d *dev;
- int of_flags;
+ struct of_phandle_args gpiospec;
+ struct gpio_chip *chip;
int ret;
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells",
- index, &out_args);
+ index, &gpiospec);
if (ret) {
pr_debug("%s: cannot parse %s property: %d\n",
__func__, propname, ret);
return ret;
}
- dev = of_find_device_by_node(out_args.np);
- if (!dev) {
- pr_debug("%s: unable to find device of node %s\n",
- __func__, out_args.np->full_name);
- return -EPROBE_DEFER;
+ chip = of_find_gpiochip_by_xlate(&gpiospec);
+ if (!chip) {
+ ret = -EPROBE_DEFER;
+ goto out;
}
- ret = gpio_of_xlate(dev, &out_args, &of_flags);
- if (ret == -EPROBE_DEFER)
- return ret;
+ ret = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
if (ret < 0) {
pr_err("%s: unable to get gpio num of device %s: %d\n",
- __func__, dev_name(dev), ret);
- return ret;
+ __func__, dev_name(chip->dev), ret);
+ goto out;
}
- if (flags) {
- *flags = of_flags;
+ if (flags)
of_gpio_flags_quirks(np, propname, flags, index);
- }
+
+out:
+ of_node_put(gpiospec.np);
return ret;
}
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index d28f5109b1..fa2c778138 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -21,7 +21,6 @@ static const char *nand_ecc_modes[] = {
[NAND_ECC_HW] = "hw",
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
[NAND_ECC_ON_DIE] = "on-die",
- [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
[NAND_ECC_SOFT_BCH] = "soft_bch",
};
diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c
index 05c28bf052..42efb1ad1d 100644
--- a/drivers/of/of_path.c
+++ b/drivers/of/of_path.c
@@ -12,14 +12,14 @@
#include <linux/mtd/mtd.h>
-struct device_d *of_find_device_by_node_path(const char *path)
+struct device *of_find_device_by_node_path(const char *path)
{
- struct device_d *dev;
+ struct device *dev;
for_each_device(dev) {
- if (!dev->device_node)
+ if (!dev->of_node)
continue;
- if (!strcmp(path, dev->device_node->full_name))
+ if (!strcmp(path, dev->of_node->full_name))
return dev;
}
@@ -27,21 +27,27 @@ struct device_d *of_find_device_by_node_path(const char *path)
}
/**
- * __of_find_path
+ * __of_cdev_find
*
* @node: The node to find the cdev for, can be the device or a
* partition in the device
* @part: Optionally, a description of a partition of @node. See of_find_path
- * @outpath: if this function returns 0 outpath will contain the path belonging
- * to the input path description. Must be freed with free().
- * @flags: use OF_FIND_PATH_FLAGS_BB to return the .bb device if available
*
*/
-static int __of_find_path(struct device_node *node, const char *part, char **outpath, unsigned flags)
+static struct cdev *__of_cdev_find(struct device_node *node, const char *part)
{
- struct device_d *dev;
+ struct device *dev;
struct cdev *cdev;
- bool add_bb = false;
+
+ /*
+ * On EFI, where devices are not instantiated from device tree, the
+ * state backend may point at a top-level fixed-partitions partition
+ * subnode with a partuuid property, which will be looked up globally.
+ *
+ * In order to support this binding, we do not early exit when
+ * of_partition_ensure_probed fails, but instead try the custom binding.
+ */
+ (void)of_partition_ensure_probed(node);
dev = of_find_device_by_node_path(node->full_name);
if (!dev) {
@@ -54,24 +60,17 @@ static int __of_find_path(struct device_node *node, const char *part, char **out
/* when partuuid is specified short-circuit the search for the cdev */
ret = of_property_read_string(node, "partuuid", &uuid);
- if (!ret) {
- cdev = cdev_by_partuuid(uuid);
- if (!cdev)
- return -ENODEV;
-
- *outpath = basprintf("/dev/%s", cdev->name);
-
- return 0;
- }
+ if (!ret)
+ return cdev_by_partuuid(uuid) ?: ERR_PTR(-ENODEV);
}
dev = of_find_device_by_node_path(devnode->full_name);
if (!dev)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
}
if (dev->bus && !dev->driver)
- return -EPROBE_DEFER;
+ return ERR_PTR(-EPROBE_DEFER);
device_detect(dev);
@@ -80,8 +79,40 @@ static int __of_find_path(struct device_node *node, const char *part, char **out
else
cdev = cdev_by_device_node(node);
- if (!cdev)
- return -ENOENT;
+ return cdev ?: ERR_PTR(-ENOENT);
+}
+
+/**
+ * of_cdev_find
+ *
+ * @node: The node to find the cdev for, can be the device or a
+ * partition in the device
+ *
+ */
+struct cdev *of_cdev_find(struct device_node *node)
+{
+ return __of_cdev_find(node, NULL);
+}
+
+/**
+ * __of_find_path
+ *
+ * @node: The node to find the cdev for, can be the device or a
+ * partition in the device
+ * @part: Optionally, a description of a partition of @node. See of_find_path
+ * @outpath: if this function returns 0 outpath will contain the path belonging
+ * to the input path description. Must be freed with free().
+ * @flags: use OF_FIND_PATH_FLAGS_BB to return the .bb device if available
+ *
+ */
+static int __of_find_path(struct device_node *node, const char *part, char **outpath, unsigned flags)
+{
+ bool add_bb = false;
+ struct cdev *cdev;
+
+ cdev = __of_cdev_find(node, part);
+ if (IS_ERR(cdev))
+ return PTR_ERR(cdev);
if ((flags & OF_FIND_PATH_FLAGS_BB) && cdev->mtd &&
mtd_can_have_bb(cdev->mtd))
@@ -150,9 +181,9 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
part_size = cdev->size;
pr_debug("%s path %s: is a partition with offset 0x%08llx, size 0x%08llx\n",
__func__, path, part_offset, part_size);
- np = cdev->master->device_node;
+ np = cdev_of_node(cdev->master);
} else {
- np = cdev->device_node;
+ np = cdev_of_node(cdev);
}
/*
@@ -161,14 +192,14 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
*/
rnp = of_find_node_by_path_from(root, np->full_name);
if (!rnp) {
- pr_debug("%s path %s: %s not found in passed tree\n", __func__, path,
- np->full_name);
+ pr_debug("%s path %s: %pOF not found in passed tree\n", __func__, path,
+ np);
return NULL;
}
if (!is_partition) {
- pr_debug("%s path %s: returning full device node %s\n", __func__, path,
- rnp->full_name);
+ pr_debug("%s path %s: returning full device node %pOF\n", __func__, path,
+ rnp);
return rnp;
}
@@ -195,7 +226,7 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
ns = of_n_size_cells(np);
if (len < (na + ns) * sizeof(__be32)) {
- pr_err("reg property too small in %s\n", np->full_name);
+ pr_err("reg property too small in %pOF\n", np);
continue;
}
@@ -203,8 +234,8 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
size = of_read_number(reg + na, ns);
if (part_offset == offset && part_size == size) {
- pr_debug("%s path %s: found matching partition in %s\n", __func__, path,
- np->full_name);
+ pr_debug("%s path %s: found matching partition in %pOF\n", __func__, path,
+ np);
return np;
}
}
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 0fc440fdcf..73c7a91db9 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -114,7 +114,7 @@ static char *of_overlay_fix_path(struct device_node *root,
prefix = of_get_child_by_name(fragment, "__overlay__")->full_name;
path_tail = path + strlen(prefix);
- return basprintf("%s%s", target->full_name, path_tail);
+ return basprintf("%pOF%s", target, path_tail);
}
static int of_overlay_apply_symbols(struct device_node *root,
@@ -131,12 +131,12 @@ static int of_overlay_apply_symbols(struct device_node *root,
if (!overlay_symbols) {
pr_debug("overlay doesn't have a __symbols__ node\n");
- return -EINVAL;
+ return 0;
}
if (!root_symbols) {
pr_info("root doesn't have a __symbols__ node\n");
- return -EINVAL;
+ return 0;
}
list_for_each_entry(prop, &overlay_symbols->properties, list) {
@@ -308,26 +308,15 @@ static bool of_overlay_matches_filter(const char *filename, struct device_node *
int of_overlay_apply_file(struct device_node *root, const char *filename,
bool filter)
{
- void *fdt;
struct device_node *ovl;
- size_t size;
int ret;
if (filter && !of_overlay_matches_filter(filename, NULL))
return 0;
- ret = read_file_2(filename, &size, &fdt, FILESIZE_MAX);
- if (ret)
- return ret;
-
- ovl = of_unflatten_dtb(fdt, size);
-
- free(fdt);
-
- if (IS_ERR(ovl)) {
- pr_err("Failed to unflatten %s: %pe\n", filename, ovl);
+ ovl = of_read_file(filename);
+ if (IS_ERR(ovl))
return PTR_ERR(ovl);
- }
if (filter && !of_overlay_matches_filter(NULL, ovl))
return 0;
diff --git a/drivers/of/partition.c b/drivers/of/partition.c
index abbda674d6..df66751fe9 100644
--- a/drivers/of/partition.c
+++ b/drivers/of/partition.c
@@ -26,13 +26,12 @@ enum of_binding_name {
struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
{
+ struct devfs_partition partinfo = {};
const char *partname;
char *filename;
struct cdev *new;
const __be32 *reg;
- u64 offset, size;
int len;
- unsigned long flags = 0;
int na, ns;
if (!node)
@@ -46,12 +45,12 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
ns = of_n_size_cells(node);
if (len < (na + ns) * sizeof(__be32)) {
- pr_err("reg property too small in %s\n", node->full_name);
+ pr_err("reg property too small in %pOF\n", node);
return NULL;
}
- offset = of_read_number(reg, na);
- size = of_read_number(reg + na, ns);
+ partinfo.offset = of_read_number(reg, na);
+ partinfo.size = of_read_number(reg + na, ns);
partname = of_get_property(node, "label", NULL);
if (!partname)
@@ -59,19 +58,23 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
if (!partname)
return NULL;
- debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname, offset, size);
+ debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname,
+ partinfo.offset, partinfo.size);
if (of_get_property(node, "read-only", NULL))
- flags = DEVFS_PARTITION_READONLY;
+ partinfo.flags = DEVFS_PARTITION_READONLY;
- filename = basprintf("%s.%s", cdev->name, partname);
+ partinfo.name = filename = basprintf("%s.%s", cdev->name, partname);
- new = devfs_add_partition(cdev->name, offset, size, flags, filename);
- if (IS_ERR(new))
+ new = cdevfs_add_partition(cdev, &partinfo);
+ if (IS_ERR(new)) {
+ pr_err("Adding partition %s failed: %pe\n", filename, new);
new = NULL;
+ goto out;
+ }
- if (new)
- new->device_node = node;;
+ new->device_node = node;
+ new->flags |= DEVFS_PARTITION_FROM_OF | DEVFS_PARTITION_FOR_FIXUP;
if (IS_ENABLED(CONFIG_NVMEM) && of_device_is_compatible(node, "nvmem-cells")) {
struct nvmem_device *nvmem = nvmem_partition_register(new);
@@ -79,6 +82,7 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
dev_warn(cdev->dev, "nvmem registeration failed: %pe\n", nvmem);
}
+out:
free(filename);
return new;
@@ -91,7 +95,7 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node)
if (!node)
return -EINVAL;
- cdev->device_node = node;
+ cdev_set_of_node(cdev, node);
subnode = of_get_child_by_name(node, "partitions");
if (subnode) {
@@ -107,14 +111,47 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node)
return 0;
}
+/**
+ * of_partition_ensure_probed - ensure a parition is probed
+ * @np: pointer to a partition or to a partitionable device
+ * Unfortunately, there is no completely reliable way
+ * to differentiate partitions from devices prior to
+ * probing, because partitions may also have compatibles.
+ * We only handle nvmem-cells, so anything besides that
+ * is assumed to be a device that should be probed directly.
+ *
+ * Returns zero on success or a negative error code otherwise
+ */
int of_partition_ensure_probed(struct device_node *np)
{
- np = of_get_parent(np);
+ struct device_node *parent = of_get_parent(np);
+
+ /* root node is not a partition */
+ if (!parent)
+ return -EINVAL;
+
+ /* Check if modern partitions binding */
+ if (of_device_is_compatible(parent, "fixed-partitions")) {
+ parent = of_get_parent(parent);
+
+ /*
+ * Can't call of_partition_ensure_probed on root node.
+ * This catches barebox-specific partuuid binding
+ * (top-level partition node)
+ */
+ if (!of_get_parent(parent))
+ return -EINVAL;
+
+ return of_device_ensure_probed(parent);
+ }
- if (of_device_is_compatible(np, "fixed-partitions"))
- np = of_get_parent(np);
+ /* Check if legacy partitions binding */
+ if (!of_property_present(np, "compatible") ||
+ of_device_is_compatible(np, "nvmem-cells"))
+ return of_device_ensure_probed(parent);
- return np ? of_device_ensure_probed(np) : -EINVAL;
+ /* Doesn't look like a partition, so let's probe directly */
+ return of_device_ensure_probed(np);
}
EXPORT_SYMBOL_GPL(of_partition_ensure_probed);
@@ -141,7 +178,7 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
return 0;
list_for_each_entry(partcdev, &cdev->partitions, partition_entry) {
- if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
+ if (!(partcdev->flags & DEVFS_PARTITION_FOR_FIXUP))
continue;
n_parts++;
}
@@ -192,7 +229,7 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */
loff_t partoffset;
- if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
+ if (!(partcdev->flags & DEVFS_PARTITION_FOR_FIXUP))
continue;
if (partcdev->mtd)
@@ -239,21 +276,21 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
static int of_partition_fixup(struct device_node *root, void *ctx)
{
struct cdev *cdev = ctx;
- struct device_node *np;
+ struct device_node *cdev_np, *np;
char *name;
- if (!cdev->device_node)
+ cdev_np = cdev_of_node(cdev);
+ if (!cdev_np)
return -EINVAL;
if (list_empty(&cdev->partitions))
return 0;
- name = of_get_reproducible_name(cdev->device_node);
+ name = of_get_reproducible_name(cdev_np);
np = of_find_node_by_reproducible_name(root, name);
free(name);
if (!np) {
- dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n",
- cdev->device_node->full_name);
+ dev_err(cdev->dev, "Cannot find nodepath %pOF, cannot fixup\n", cdev_np);
return -EINVAL;
}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index a9a5d4c2da..918607a518 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -12,6 +12,7 @@
#include <of.h>
#include <of_address.h>
#include <linux/amba/bus.h>
+#include <mmu.h>
/**
* of_find_device_by_node - Find the platform_device associated with a node
@@ -19,20 +20,18 @@
*
* Returns platform_device pointer, or NULL if not found
*/
-struct device_d *of_find_device_by_node(struct device_node *np)
+struct device *of_find_device_by_node(struct device_node *np)
{
- struct device_d *dev;
- int ret;
+ struct device *dev;
- ret = of_device_ensure_probed(np);
- if (ret)
- return NULL;
+ /* Not having a driver is not an error here */
+ (void)of_device_ensure_probed(np);
if (deep_probe_is_supported())
return np->dev;
for_each_device(dev)
- if (dev->device_node == np)
+ if (dev->of_node == np)
return dev;
return NULL;
}
@@ -46,9 +45,9 @@ EXPORT_SYMBOL(of_find_device_by_node);
* derive a unique name. If it cannot, then it will prepend names from
* parent nodes until a unique name can be derived.
*/
-static void of_device_make_bus_id(struct device_d *dev)
+static void of_device_make_bus_id(struct device *dev)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
const __be32 *reg;
u64 addr;
@@ -73,7 +72,64 @@ static void of_device_make_bus_id(struct device_d *dev)
}
}
-static void of_dma_configure(struct device_d *dev, struct device_node *np)
+static struct device_node *of_get_next_dma_parent(const struct device_node *np)
+{
+ struct of_phandle_args args;
+ int ret, index;
+
+ index = of_property_match_string(np, "interconnect-names", "dma-mem");
+ if (index < 0)
+ return of_get_parent(np);
+
+ ret = of_parse_phandle_with_args(np, "interconnects",
+ "#interconnect-cells",
+ index, &args);
+ if (ret < 0)
+ return of_get_parent(np);
+
+ return args.np;
+}
+
+static enum dev_dma_coherence of_dma_get_coherence(struct device_node *node)
+{
+ if (IS_ENABLED(CONFIG_OF_DMA_COHERENCY)) {
+ while (node) {
+ if (of_property_read_bool(node, "dma-coherent"))
+ return DEV_DMA_COHERENT;
+ if (of_property_read_bool(node, "dma-noncoherent"))
+ return DEV_DMA_NON_COHERENT;
+ node = of_get_next_dma_parent(node);
+ }
+ }
+
+ return DEV_DMA_COHERENCE_DEFAULT;
+}
+
+/**
+ * of_dma_is_coherent - Check if device is coherent
+ * @np: device node
+ *
+ * It returns true if "dma-coherent" property was found
+ * for this device in the DT, or if DMA is coherent by
+ * default for OF devices on the current platform and no
+ * "dma-noncoherent" property was found for this device.
+ */
+bool of_dma_is_coherent(struct device_node *node)
+{
+ switch (of_dma_get_coherence(node)) {
+ case DEV_DMA_COHERENT:
+ return true;
+ case DEV_DMA_NON_COHERENT:
+ return false;
+ case DEV_DMA_COHERENCE_DEFAULT:
+ return IS_ENABLED(CONFIG_ARCH_DMA_DEFAULT_COHERENT);
+ }
+
+ BUG();
+}
+EXPORT_SYMBOL_GPL(of_dma_is_coherent);
+
+static void of_dma_configure(struct device *dev, struct device_node *np)
{
u64 dma_addr, paddr, size = 0;
unsigned long offset;
@@ -87,6 +143,7 @@ static void of_dma_configure(struct device_d *dev, struct device_node *np)
}
dev->dma_offset = offset;
+ dev->dma_coherent = of_dma_get_coherence(np);
}
/**
@@ -97,13 +154,14 @@ static void of_dma_configure(struct device_d *dev, struct device_node *np)
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_platform_device_create(struct device_node *np,
- struct device_d *parent)
+struct device *of_platform_device_create(struct device_node *np,
+ struct device *parent)
{
- struct device_d *dev;
+ struct device *dev;
struct resource *res = NULL, temp_res;
resource_size_t resinval;
int i, ret, num_reg = 0;
+ u32 virt;
if (!of_device_is_available(np))
return NULL;
@@ -112,8 +170,10 @@ struct device_d *of_platform_device_create(struct device_node *np,
* Linux uses the OF_POPULATED flag to skip already populated/created
* devices.
*/
- if (np->dev)
+ if (np->dev) {
+ device_rescan(np->dev);
return np->dev;
+ }
/* count the io resources */
if (of_can_translate_address(np))
@@ -135,7 +195,7 @@ struct device_d *of_platform_device_create(struct device_node *np,
/* setup generic device info */
dev = xzalloc(sizeof(*dev));
dev->id = DEVICE_ID_SINGLE;
- dev->device_node = np;
+ dev->of_node = np;
dev->parent = parent;
dev->resource = res;
dev->num_resources = num_reg;
@@ -143,6 +203,24 @@ struct device_d *of_platform_device_create(struct device_node *np,
of_dma_configure(dev, np);
+ if (num_reg && !of_property_read_u32(np, "virtual-reg", &virt)) {
+ resource_size_t remap_offset = virt - res[0].start;
+
+ for (i = 0; i < num_reg; i++) {
+ void *new_virt = (void *)res[i].start + remap_offset;
+ resource_size_t size = resource_size(&res[i]);
+
+ ret = arch_remap_range(new_virt, res[i].start, size, MAP_UNCACHED);
+ if (!ret) {
+ debug("%s: remap device %s resource %d: %pa -> 0x%p\n",
+ __func__, dev_name(dev), i, &res[i].start, new_virt);
+
+ res[i].start = (resource_size_t)new_virt;
+ res[i].end = res[i].start + size - 1;
+ }
+ }
+ }
+
resinval = (-1);
debug("%s: register device %s, io=%pa\n",
@@ -164,11 +242,11 @@ struct device_d *of_platform_device_create(struct device_node *np,
return NULL;
}
-struct driver_d dummy_driver = {
+struct driver dummy_driver = {
.name = "dummy-driver",
};
-void of_platform_device_dummy_drv(struct device_d *dev)
+void of_platform_device_dummy_drv(struct device *dev)
{
dev->driver = &dummy_driver;
}
@@ -180,9 +258,9 @@ void of_platform_device_dummy_drv(struct device_d *dev)
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register(struct device_node *np)
+struct device *of_device_enable_and_register(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
of_device_enable(np);
@@ -201,7 +279,7 @@ EXPORT_SYMBOL(of_device_enable_and_register);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register_by_name(const char *name)
+struct device *of_device_enable_and_register_by_name(const char *name)
{
struct device_node *node;
@@ -223,7 +301,7 @@ EXPORT_SYMBOL(of_device_enable_and_register_by_name);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register_by_alias(const char *alias)
+struct device *of_device_enable_and_register_by_alias(const char *alias)
{
struct device_node *node;
@@ -236,12 +314,12 @@ struct device_d *of_device_enable_and_register_by_alias(const char *alias)
EXPORT_SYMBOL(of_device_enable_and_register_by_alias);
#ifdef CONFIG_ARM_AMBA
-static struct device_d *of_amba_device_create(struct device_node *np)
+static struct device *of_amba_device_create(struct device_node *np)
{
struct amba_device *dev;
int ret;
- debug("Creating amba device %s\n", np->full_name);
+ debug("Creating amba device %pOF\n", np);
if (!of_device_is_available(np))
return NULL;
@@ -257,7 +335,7 @@ static struct device_d *of_amba_device_create(struct device_node *np)
/* setup generic device info */
dev->dev.id = DEVICE_ID_SINGLE;
- dev->dev.device_node = np;
+ dev->dev.of_node = np;
of_device_make_bus_id(&dev->dev);
ret = of_address_to_resource(np, 0, &dev->res);
@@ -286,7 +364,7 @@ amba_err_free:
return NULL;
}
#else /* CONFIG_ARM_AMBA */
-static inline struct device_d *of_amba_device_create(struct device_node *np)
+static inline struct device *of_amba_device_create(struct device_node *np)
{
return NULL;
}
@@ -303,16 +381,16 @@ static inline struct device_d *of_amba_device_create(struct device_node *np)
*/
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
- struct device_d *parent)
+ struct device *parent)
{
struct device_node *child;
- struct device_d *dev;
+ struct device *dev;
int rc = 0;
/* Make sure it has a compatible property */
if (!of_get_property(bus, "compatible", NULL)) {
- pr_debug("%s() - skipping %s, no compatible prop\n",
- __func__, bus->full_name);
+ pr_debug("%s() - skipping %pOF, no compatible prop\n",
+ __func__, bus);
return 0;
}
@@ -326,7 +404,7 @@ static int of_platform_bus_create(struct device_node *bus,
return 0;
for_each_child_of_node(bus, child) {
- pr_debug(" create child: %s\n", child->full_name);
+ pr_debug(" create child: %pOF\n", child);
rc = of_platform_bus_create(child, matches, dev);
if (rc)
break;
@@ -349,7 +427,7 @@ static int of_platform_bus_create(struct device_node *bus,
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
- struct device_d *parent)
+ struct device *parent)
{
struct device_node *child;
int rc = 0;
@@ -369,17 +447,17 @@ int of_platform_populate(struct device_node *root,
}
EXPORT_SYMBOL_GPL(of_platform_populate);
-static struct device_d *of_device_create_on_demand(struct device_node *np)
+static struct device *of_device_create_on_demand(struct device_node *np)
{
struct device_node *parent;
- struct device_d *parent_dev, *dev;
+ struct device *parent_dev, *dev;
parent = of_get_parent(np);
if (!parent)
return NULL;
- if (!np->dev)
- pr_debug("Creating device for %s\n", np->full_name);
+ if (!np->dev && parent->dev)
+ device_rescan(parent->dev);
/* Create all parent devices needed for the requested device */
parent_dev = parent->dev ? : of_device_create_on_demand(parent);
@@ -394,12 +472,17 @@ static struct device_d *of_device_create_on_demand(struct device_node *np)
if (np->dev)
return np->dev;
+ if (!of_property_present(np, "compatible"))
+ return NULL;
+
+ pr_debug("Creating device for %pOF\n", np);
+
if (of_device_is_compatible(np, "arm,primecell"))
dev = of_amba_device_create(np);
else
dev = of_platform_device_create(np, parent_dev);
- return dev ? : ERR_PTR(-ENODEV);
+ return dev ? : ERR_PTR(-EPROBE_DEFER);
}
/**
@@ -411,23 +494,19 @@ static struct device_d *of_device_create_on_demand(struct device_node *np)
* it.
*
* Return: %0 on success
- * %-ENODEV if either the device can't be populated, the driver is
+ * %-EPROBE_DEFER if either the device can't be populated, the driver is
* missing or the driver probe returns an error.
*/
int of_device_ensure_probed(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
- if (!deep_probe_is_supported())
+ if (!np || !deep_probe_is_supported())
return 0;
dev = of_device_create_on_demand(np);
- if (IS_ERR(dev))
- return PTR_ERR(dev);
-
- if (!dev)
- panic("deep-probe: device for '%s' couldn't be created\n",
- np->full_name);
+ if (IS_ERR_OR_NULL(dev))
+ return -EPROBE_DEFER;
/*
* The deep-probe mechanism relies on the fact that all necessary
@@ -437,7 +516,7 @@ int of_device_ensure_probed(struct device_node *np)
* requirements are fulfilled if 'dev->driver' is not NULL.
*/
if (!dev->driver)
- return -ENODEV;
+ return -EPROBE_DEFER;
return 0;
}
@@ -452,7 +531,7 @@ EXPORT_SYMBOL_GPL(of_device_ensure_probed);
* populated and probed if found.
*
* Return: %0 on success
- * %-ENODEV if either the device can't be populated, the driver is
+ * %-EPROBE_DEFER if either the device can't be populated, the driver is
* missing or the driver probe returns an error
* %-EINVAL if alias can't be found
*/
@@ -480,7 +559,7 @@ EXPORT_SYMBOL_GPL(of_device_ensure_probed_by_alias);
* probes devices which match @ids.
*
* Return: %0 on success
- * %-ENODEV if either the device wasn't found, can't be populated,
+ * %-EPROBE_DEFER if either the device wasn't found, can't be populated,
* the driver is missing or the driver probe returns an error
*/
int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
@@ -513,7 +592,7 @@ EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_dev_id);
* devices which matches @property_name.
*
* Return: %0 on success
- * %-ENODEV if either the device wasn't found, can't be populated,
+ * %-EPROBE_DEFER if either the device wasn't found, can't be populated,
* the driver is missing or the driver probe returns an error
*/
int of_devices_ensure_probed_by_property(const char *property_name)
diff --git a/drivers/of/reserved-mem.c b/drivers/of/reserved-mem.c
index f50a0bd837..599a7c968a 100644
--- a/drivers/of/reserved-mem.c
+++ b/drivers/of/reserved-mem.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: 2021 Rouven Czerwinski <r.czerwinski@pengutronix.de>, Pengutronix
+#define pr_fmt(fmt) "of-reserved-mem: " fmt
+
#include <stdio.h>
#include <of.h>
#include <of_address.h>
@@ -17,6 +19,8 @@ static void request_region(struct resource *r)
if (!resource_contains(bank->res, r))
continue;
+ pr_debug("reserving %s at %pad-%pad\n", r->name, &r->start, &r->end);
+
if (!reserve_sdram_region(r->name, r->start, resource_size(r)))
pr_warn("couldn't request reserved sdram region %pa-%pa\n",
&r->start, &r->end);
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index e7ce6a8c45..8f37805d71 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
config HW_HAS_PCI
- bool "Compile in PCI core" if COMPILE_TEST
+ bool
if HW_HAS_PCI
@@ -55,9 +55,16 @@ config PCI_LAYERSCAPE
select OF_PCI
select PCI
+config PCI_ROCKCHIP
+ bool "Rockchip PCIe controller"
+ depends on ARCH_ROCKCHIP
+ select PCIE_DW
+ select OF_PCI
+ select PCI
+
config PCI_EFI
bool "EFI PCI protocol"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
select PCI
config PCI_ECAM_GENERIC
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index deed79601d..69649fbcd2 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,7 +2,8 @@
#
# Makefile for the PCI bus specific drivers.
#
-obj-y += pci.o bus.o pci_iomap.o
+obj-y += pci.o bus.o pci_iomap.o host-bridge.o
+obj-$(CONFIG_OFDEVICE) += of.o
ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
@@ -13,3 +14,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_EFI) += pci-efi.o
obj-$(CONFIG_PCI_ECAM_GENERIC) += pci-ecam-generic.o
+obj-$(CONFIG_PCI_ROCKCHIP) += pcie-dw-rockchip.o
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index e238b24c02..37bdc9a22f 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -3,6 +3,29 @@
#include <init.h>
#include <driver.h>
#include <linux/pci.h>
+#include <linux/resource_ext.h>
+
+void pci_add_resource_offset(struct list_head *resources, struct resource *res,
+ resource_size_t offset)
+{
+ struct resource_entry *entry;
+
+ entry = resource_list_create_entry(res, 0);
+ if (!entry) {
+ pr_err("PCI: can't add host bridge window %pR\n", res);
+ return;
+ }
+ entry->offset = offset;
+
+ resource_list_add_tail(entry, resources);
+}
+EXPORT_SYMBOL(pci_add_resource_offset);
+
+void pci_add_resource(struct list_head *resources, struct resource *res)
+{
+ pci_add_resource_offset(resources, res, 0);
+}
+EXPORT_SYMBOL(pci_add_resource);
/**
* pci_match_one_device - Tell if a PCI device structure has a matching
@@ -50,7 +73,7 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
}
EXPORT_SYMBOL(pci_match_id);
-static int pci_match(struct device_d *dev, struct driver_d *drv)
+static int pci_match(struct device *dev, struct driver *drv)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_driver *pdrv = to_pci_driver(drv);
@@ -65,7 +88,7 @@ static int pci_match(struct device_d *dev, struct driver_d *drv)
return -1;
}
-static int pci_probe(struct device_d *dev)
+static int pci_probe(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_driver *pdrv = to_pci_driver(dev->driver);
@@ -73,7 +96,7 @@ static int pci_probe(struct device_d *dev)
return pdrv->probe(pdev, pdev->id);
}
-static void pci_remove(struct device_d *dev)
+static void pci_remove(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_driver *pdrv = to_pci_driver(dev->driver);
@@ -97,7 +120,7 @@ pure_initcall(pci_bus_init);
int pci_register_driver(struct pci_driver *pdrv)
{
- struct driver_d *drv = &pdrv->driver;
+ struct driver *drv = &pdrv->driver;
if (!pdrv->id_table)
return -EIO;
@@ -111,9 +134,12 @@ int pci_register_driver(struct pci_driver *pdrv)
int pci_register_device(struct pci_dev *pdev)
{
char str[6];
- struct device_d *dev = &pdev->dev;
+ struct device *dev = &pdev->dev;
int ret;
+ if (!of_device_is_available(pdev->dev.of_node))
+ return 0;
+
dev_set_name(dev, "pci-%04x:%04x.", pdev->vendor, pdev->device);
dev->bus = &pci_bus;
dev->id = DEVICE_ID_DYNAMIC;
diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c
new file mode 100644
index 0000000000..4460ad1971
--- /dev/null
+++ b/drivers/pci/host-bridge.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Host bridge related code
+ */
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <linux/pci.h>
+
+static struct pci_bus *find_pci_root_bus(struct pci_bus *bus)
+{
+ while (bus->parent)
+ bus = bus->parent;
+
+ return bus;
+}
+
+struct pci_controller *pci_find_host_bridge(struct pci_bus *bus)
+{
+ struct pci_bus *root_bus = find_pci_root_bus(bus);
+
+ return root_bus->host;
+}
+EXPORT_SYMBOL_GPL(pci_find_host_bridge);
+
+void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
+ struct resource *res)
+{
+ struct pci_controller *bridge = pci_find_host_bridge(bus);
+ struct resource_entry *window;
+ resource_size_t offset = 0;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ if (resource_contains(window->res, res)) {
+ offset = window->offset;
+ break;
+ }
+ }
+
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+EXPORT_SYMBOL(pcibios_resource_to_bus);
+
+static bool region_contains(struct pci_bus_region *region1,
+ struct pci_bus_region *region2)
+{
+ return region1->start <= region2->start && region1->end >= region2->end;
+}
+
+void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
+ struct pci_bus_region *region)
+{
+ struct pci_controller *bridge = pci_find_host_bridge(bus);
+ struct resource_entry *window;
+ resource_size_t offset = 0;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ struct pci_bus_region bus_region;
+
+ if (resource_type(res) != resource_type(window->res))
+ continue;
+
+ bus_region.start = window->res->start - window->offset;
+ bus_region.end = window->res->end - window->offset;
+
+ if (region_contains(&bus_region, region)) {
+ offset = window->offset;
+ break;
+ }
+ }
+
+ res->start = region->start + offset;
+ res->end = region->end + offset;
+}
+EXPORT_SYMBOL(pcibios_bus_to_resource);
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
new file mode 100644
index 0000000000..1cf7fe302d
--- /dev/null
+++ b/drivers/pci/of.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PCI <-> OF mapping helpers
+ *
+ * Copyright 2011 IBM Corp.
+ */
+#define pr_fmt(fmt) "PCI: OF: " fmt
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <of.h>
+#include <of_address.h>
+#include <common.h>
+#include <linux/resource_ext.h>
+
+/**
+ * of_pci_get_host_bridge_resources() - parsing of PCI host bridge resources from DT
+ * @dev: host bridge device
+ * @resources: list where the range of resources will be added after DT parsing
+ *
+ * This function will parse the "ranges" property of a PCI host bridge device
+ * node and setup the resource mapping based on its content. It is expected
+ * that the property conforms with the Power ePAPR document.
+ *
+ * It returns zero if the range parsing has been successful or a standard error
+ * value if it failed.
+ */
+static int of_pci_get_host_bridge_resources(struct device *dev,
+ struct pci_controller *bridge)
+{
+ struct list_head *resources = &bridge->windows;
+ struct device_node *dev_node = dev->of_node;
+ struct resource *res, tmp_res;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ const char *range_type;
+ int err;
+
+ dev_dbg(dev, "host bridge %pOF ranges:\n", dev_node);
+
+ /* Check for ranges property */
+ err = of_pci_range_parser_init(&parser, dev_node);
+ if (err)
+ return 0;
+
+ dev_dbg(dev, "Parsing ranges property...\n");
+ for_each_of_pci_range(&parser, &range) {
+ /* Read next ranges element */
+ if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
+ range_type = "IO";
+ else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
+ range_type = "MEM";
+ else
+ range_type = "err";
+ dev_dbg(dev, " %6s %#012llx..%#012llx -> %#012llx\n",
+ range_type, range.cpu_addr,
+ range.cpu_addr + range.size - 1, range.pci_addr);
+
+ /*
+ * If we failed translation or got a zero-sized region
+ * then skip this range
+ */
+ if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
+ continue;
+
+ of_pci_range_to_resource(&range, dev_node, &tmp_res);
+
+ res = kmemdup(&tmp_res, sizeof(tmp_res), GFP_KERNEL);
+ if (!res) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+
+ switch (res->flags & IORESOURCE_TYPE_BITS) {
+ case IORESOURCE_IO:
+ bridge->io_resource = res;
+ break;
+
+ case IORESOURCE_MEM:
+ if (res->flags & IORESOURCE_PREFETCH)
+ bridge->mem_pref_resource = res;
+ else
+ bridge->mem_resource = res;
+ break;
+ }
+ }
+
+ return 0;
+
+failed:
+ return err;
+}
+
+int of_pci_bridge_init(struct device *dev, struct pci_controller *bridge)
+{
+ if (!dev || !dev->of_node)
+ return 0;
+
+ return of_pci_get_host_bridge_resources(dev, bridge);
+}
diff --git a/drivers/pci/pci-ecam-generic.c b/drivers/pci/pci-ecam-generic.c
index 58c81ecbb6..4db40975e2 100644
--- a/drivers/pci/pci-ecam-generic.c
+++ b/drivers/pci/pci-ecam-generic.c
@@ -128,8 +128,8 @@ static inline bool is_64bit(const struct resource *res)
static int pcie_ecam_parse_dt(struct generic_ecam_pcie *ecam)
{
- struct device_d *dev = ecam->pci.parent;
- struct device_node *np = dev->device_node;
+ struct device *dev = ecam->pci.parent;
+ struct device_node *np = dev->of_node;
struct of_pci_range_parser parser;
struct of_pci_range range;
struct resource res;
@@ -167,7 +167,7 @@ static int pcie_ecam_parse_dt(struct generic_ecam_pcie *ecam)
return 0;
}
-static int pcie_ecam_probe(struct device_d *dev)
+static int pcie_ecam_probe(struct device *dev)
{
struct generic_ecam_pcie *ecam;
struct resource *iores;
@@ -186,6 +186,8 @@ static int pcie_ecam_probe(struct device_d *dev)
ecam->pci.io_resource = &ecam->io;
ecam->pci.mem_pref_resource = &ecam->prefetch;
+ pci_controller_init(&ecam->pci);
+
ret = pcie_ecam_parse_dt(ecam);
if (ret)
return ret;
@@ -198,8 +200,9 @@ static struct of_device_id pcie_ecam_dt_ids[] = {
{ .compatible = "pci-host-ecam-generic" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, pcie_ecam_dt_ids);
-static struct driver_d pcie_ecam_driver = {
+static struct driver pcie_ecam_driver = {
.name = "pcie-generic-ecam",
.probe = pcie_ecam_probe,
.of_compatible = pcie_ecam_dt_ids,
diff --git a/drivers/pci/pci-efi.c b/drivers/pci/pci-efi.c
index d107ff548d..b8cd663161 100644
--- a/drivers/pci/pci-efi.c
+++ b/drivers/pci/pci-efi.c
@@ -17,7 +17,7 @@
struct efi_pci_priv {
struct efi_pci_root_bridge_io_protocol *protocol;
- struct device_d *dev;
+ struct device *dev;
struct pci_controller pci;
struct resource mem;
struct resource mem_pref;
@@ -34,7 +34,7 @@ struct pci_child_id {
struct pci_child {
struct efi_pci_io_protocol *protocol;
- struct device_d *dev;
+ struct device *dev;
struct list_head list;
struct pci_child_id id;
};
@@ -268,7 +268,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, efi_pci_fixup_dev_parent);
static int efi_pci_probe(struct efi_device *efidev)
{
- struct device_d *child;
+ struct device *child;
struct efi_pci_priv *priv;
efi_status_t efiret;
void *resources;
@@ -277,6 +277,9 @@ static int efi_pci_probe(struct efi_device *efidev)
priv = xzalloc(sizeof(*priv));
+ priv->pci.parent = &efidev->dev;
+ pci_controller_init(&priv->pci);
+
BS->handle_protocol(efidev->handle, &EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID,
(void **)&priv->protocol);
if (!priv->protocol)
@@ -310,7 +313,6 @@ static int efi_pci_probe(struct efi_device *efidev)
}
}
- priv->pci.parent = &efidev->dev;
priv->pci.pci_ops = &efi_pci_ops;
INIT_LIST_HEAD(&priv->children);
diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c
index e7ea0ebb94..ac62d961d9 100644
--- a/drivers/pci/pci-imx6.c
+++ b/drivers/pci/pci-imx6.c
@@ -30,9 +30,9 @@
#include <mfd/imx6q-iomuxc-gpr.h>
#include <mfd/imx7-iomuxc-gpr.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx7-regs.h>
-#include <mach/imx8mq-regs.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx7-regs.h>
+#include <mach/imx/imx8mq-regs.h>
#include "pcie-designware.h"
@@ -297,7 +297,7 @@ static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
{
- struct device_d *dev = imx6_pcie->pci->dev;
+ struct device *dev = imx6_pcie->pci->dev;
u32 gpr1, gpr1x;
unsigned int offset;
int ret;
@@ -351,7 +351,7 @@ static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie)
{
u32 val;
unsigned int retries;
- struct device_d *dev = imx6_pcie->pci->dev;
+ struct device *dev = imx6_pcie->pci->dev;
for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) {
val = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR22);
@@ -367,7 +367,7 @@ static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie)
static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
{
- struct device_d *dev = imx6_pcie->pci->dev;
+ struct device *dev = imx6_pcie->pci->dev;
int ret;
u32 gpr1;
@@ -525,7 +525,7 @@ static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
- struct device_d *dev = pci->dev;
+ struct device *dev = pci->dev;
uint32_t tmp;
uint64_t start = get_time_ns();
@@ -540,7 +540,7 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
return -EINVAL;
}
-static void imx6_pcie_ltssm_enable(struct device_d *dev)
+static void imx6_pcie_ltssm_enable(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev->priv;
u32 gpr12;
@@ -562,7 +562,7 @@ static void imx6_pcie_ltssm_enable(struct device_d *dev)
static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
- struct device_d *dev = pci->dev;
+ struct device *dev = pci->dev;
uint32_t tmp;
int ret;
@@ -669,7 +669,7 @@ static const struct dw_pcie_host_ops imx6_pcie_host_ops = {
};
static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
- struct device_d *dev)
+ struct device *dev)
{
struct dw_pcie *pci = imx6_pcie->pci;
struct pcie_port *pp = &pci->pp;
@@ -686,12 +686,12 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
return 0;
}
-static int imx6_pcie_probe(struct device_d *dev)
+static int imx6_pcie_probe(struct device *dev)
{
struct resource *iores;
struct dw_pcie *pci;
struct imx6_pcie *imx6_pcie;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret;
imx6_pcie = xzalloc(sizeof(*imx6_pcie));
@@ -814,7 +814,7 @@ static int imx6_pcie_probe(struct device_d *dev)
return 0;
}
-static void imx6_pcie_remove(struct device_d *dev)
+static void imx6_pcie_remove(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev->priv;
@@ -874,8 +874,9 @@ static struct of_device_id imx6_pcie_of_match[] = {
{ .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } ,
{},
};
+MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
-static struct driver_d imx6_pcie_driver = {
+static struct driver imx6_pcie_driver = {
.name = "imx6-pcie",
.of_compatible = DRV_OF_COMPAT(imx6_pcie_of_match),
.probe = imx6_pcie_probe,
diff --git a/drivers/pci/pci-layerscape.c b/drivers/pci/pci-layerscape.c
index b0a1ce8135..732232dec5 100644
--- a/drivers/pci/pci-layerscape.c
+++ b/drivers/pci/pci-layerscape.c
@@ -21,7 +21,7 @@
#include <linux/kernel.h>
#include <of_address.h>
#include <of_pci.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <magicvar.h>
#include <globalvar.h>
#include <mfd/syscon.h>
@@ -217,11 +217,11 @@ static int ls1021_pcie_host_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct ls_pcie *pcie = to_ls_pcie(pci);
- struct device_d *dev = pci->dev;
+ struct device *dev = pci->dev;
u32 index[2];
int ret;
- pcie->scfg = syscon_regmap_lookup_by_phandle(dev->device_node,
+ pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
"fsl,pcie-scfg");
if (IS_ERR(pcie->scfg)) {
ret = PTR_ERR(pcie->scfg);
@@ -230,7 +230,7 @@ static int ls1021_pcie_host_init(struct pcie_port *pp)
return ret;
}
- if (of_property_read_u32_array(dev->device_node,
+ if (of_property_read_u32_array(dev->of_node,
"fsl,pcie-scfg", index, 2)) {
pcie->scfg = NULL;
return -EINVAL;
@@ -312,12 +312,13 @@ static const struct of_device_id ls_pcie_of_match[] = {
{ .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata },
{ },
};
+MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
static int __init ls_add_pcie_port(struct ls_pcie *pcie)
{
struct dw_pcie *pci = &pcie->pci;
struct pcie_port *pp = &pci->pp;
- struct device_d *dev = pci->dev;
+ struct device *dev = pci->dev;
int ret;
pp->ops = pcie->drvdata->ops;
@@ -369,7 +370,7 @@ BAREBOX_MAGICVAR(global.layerscape_pcie.share_stream_ids,
static int ls_pcie_of_fixup(struct device_node *root, void *ctx)
{
struct ls_pcie *pcie = ctx;
- struct device_d *dev = pcie->pci.dev;
+ struct device *dev = pcie->pci.dev;
struct device_node *np;
phandle iommu_handle = 0;
char *name;
@@ -378,7 +379,7 @@ static int ls_pcie_of_fixup(struct device_node *root, void *ctx)
int ret, i;
u32 devid, stream_id;
- name = of_get_reproducible_name(dev->device_node);
+ name = of_get_reproducible_name(dev->of_node);
np = root;
do {
@@ -479,7 +480,7 @@ out:
return ret;
}
-static int __init ls_pcie_probe(struct device_d *dev)
+static int __init ls_pcie_probe(struct device *dev)
{
struct dw_pcie *pci;
struct ls_pcie *pcie;
@@ -532,7 +533,7 @@ static int __init ls_pcie_probe(struct device_d *dev)
return 0;
}
-static struct driver_d ls_pcie_driver = {
+static struct driver ls_pcie_driver = {
.name = "layerscape-pcie",
.of_compatible = DRV_OF_COMPAT(ls_pcie_of_match),
.probe = ls_pcie_probe,
@@ -550,13 +551,16 @@ static void ls_pcie_fixup(struct pci_dev *pcidev)
uint32_t devid;
int base_bus_num = 0;
+ if (!of_match_device(ls_pcie_of_match, host->parent))
+ return;
+
stream_id = ls_pcie_next_streamid(lspcie);
index = ls_pcie_next_lut_index(lspcie);
p = pcidev->bus;
while (p) {
base_bus_num = p->number;
- p = p->parent_bus;
+ p = p->parent;
}
devid = (pcidev->bus->number - base_bus_num) << 8 | pcidev->devfn;
diff --git a/drivers/pci/pci-mvebu.c b/drivers/pci/pci-mvebu.c
index 41bccd2783..988465a344 100644
--- a/drivers/pci/pci-mvebu.c
+++ b/drivers/pci/pci-mvebu.c
@@ -262,7 +262,7 @@ static int mvebu_get_target_attr(struct device_node *np, int devfn,
return -ENOENT;
}
-static struct mvebu_pcie *mvebu_pcie_port_probe(struct device_d *dev,
+static struct mvebu_pcie *mvebu_pcie_port_probe(struct device *dev,
struct device_node *np)
{
struct mvebu_pcie *pcie;
@@ -292,14 +292,14 @@ static struct mvebu_pcie *mvebu_pcie_port_probe(struct device_d *dev,
return ERR_PTR(-EINVAL);
}
- if (mvebu_get_target_attr(dev->device_node, devfn, IORESOURCE_MEM,
+ if (mvebu_get_target_attr(dev->of_node, devfn, IORESOURCE_MEM,
&mem_target, &mem_attr)) {
dev_err(dev, "unable to get target/attr for mem window\n");
return ERR_PTR(-EINVAL);
}
/* I/O windows are optional */
- mvebu_get_target_attr(dev->device_node, devfn, IORESOURCE_IO,
+ mvebu_get_target_attr(dev->of_node, devfn, IORESOURCE_IO,
&io_target, &io_attr);
reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
@@ -322,6 +322,9 @@ static struct mvebu_pcie *mvebu_pcie_port_probe(struct device_d *dev,
}
pcie = xzalloc(sizeof(*pcie));
+
+ pci_controller_init(&pcie->pci);
+
pcie->port = port;
pcie->lane = lane;
pcie->lane_mask = lane_mask;
@@ -395,10 +398,11 @@ static struct of_device_id mvebu_pcie_dt_ids[] = {
#endif
{ },
};
+MODULE_DEVICE_TABLE(of, mvebu_pcie_dt_ids);
-static int mvebu_pcie_probe(struct device_d *dev)
+static int mvebu_pcie_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct of_device_id *match = of_match_node(mvebu_pcie_dt_ids, np);
struct mvebu_pcie_ops *ops = (struct mvebu_pcie_ops *)match->data;
struct device_node *pnp;
@@ -437,7 +441,7 @@ static int mvebu_pcie_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mvebu_pcie_driver = {
+static struct driver mvebu_pcie_driver = {
.name = "mvebu-pcie",
.probe = mvebu_pcie_probe,
.of_compatible = mvebu_pcie_dt_ids,
diff --git a/drivers/pci/pci-tegra.c b/drivers/pci/pci-tegra.c
index d3b8ab59b2..9ef50207ab 100644
--- a/drivers/pci/pci-tegra.c
+++ b/drivers/pci/pci-tegra.c
@@ -22,7 +22,7 @@
#include <linux/phy/phy.h>
#include <linux/reset.h>
#include <linux/sizes.h>
-#include <mach/tegra-powergate.h>
+#include <mach/tegra/tegra-powergate.h>
#include <regulator.h>
/* register definitions */
@@ -214,7 +214,7 @@ struct tegra_pcie_soc_data {
};
struct tegra_pcie {
- struct device_d *dev;
+ struct device *dev;
struct pci_controller pci;
void __iomem *pads;
@@ -827,7 +827,7 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
{
- struct device_d *dev = pcie->dev;
+ struct device *dev = pcie->dev;
int err;
err = tegra_pcie_clocks_get(pcie);
@@ -903,7 +903,7 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
u32 *xbar)
{
- struct device_node *np = pcie->dev->device_node;
+ struct device_node *np = pcie->dev->of_node;
if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
switch (lanes) {
@@ -954,7 +954,7 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
{
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
- struct device_node *np = pcie->dev->device_node, *port;
+ struct device_node *np = pcie->dev->of_node, *port;
struct of_pci_range_parser parser;
struct of_pci_range range;
struct resource *rp_res;
@@ -1167,7 +1167,6 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
tegra_pcie_port_free(port);
}
- pcie->pci.parent = pcie->dev;
pcie->pci.pci_ops = &tegra_pcie_ops;
pcie->pci.mem_resource = &pcie->mem;
pcie->pci.mem_pref_resource = &pcie->prefetch;
@@ -1230,8 +1229,9 @@ static __maybe_unused struct of_device_id tegra_pcie_of_match[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, tegra_pcie_of_match);
-static int tegra_pcie_probe(struct device_d *dev)
+static int tegra_pcie_probe(struct device *dev)
{
struct tegra_pcie *pcie;
int err;
@@ -1240,6 +1240,9 @@ static int tegra_pcie_probe(struct device_d *dev)
if (!pcie)
return -ENOMEM;
+ pcie->pci.parent = pcie->dev;
+ pci_controller_init(&pcie->pci);
+
INIT_LIST_HEAD(&pcie->buses);
INIT_LIST_HEAD(&pcie->ports);
dev_get_drvdata(dev, (const void **)&pcie->soc_data);
@@ -1277,7 +1280,7 @@ put_resources:
return err;
}
-static struct driver_d tegra_pcie_driver = {
+static struct driver tegra_pcie_driver = {
.name = "tegra-pcie",
.of_compatible = DRV_OF_COMPAT(tegra_pcie_of_match),
.probe = tegra_pcie_probe,
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 9b00a1c9eb..e6370b5c7f 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4,6 +4,7 @@
#include <common.h>
#include <linux/sizes.h>
#include <linux/pci.h>
+#include <linux/bitfield.h>
static unsigned int pci_scan_bus(struct pci_bus *bus);
@@ -41,35 +42,38 @@ static void pci_bus_register_devices(struct pci_bus *bus)
pci_bus_register_devices(child_bus);
}
+void pci_controller_init(struct pci_controller *hose)
+{
+ INIT_LIST_HEAD(&hose->windows);
+
+ of_pci_bridge_init(hose->parent, hose);
+}
+
void register_pci_controller(struct pci_controller *hose)
{
struct pci_bus *bus;
bus = pci_alloc_bus();
hose->bus = bus;
- bus->parent = hose->parent;
bus->host = hose;
- bus->resource[PCI_BUS_RESOURCE_MEM] = hose->mem_resource;
- bus->resource[PCI_BUS_RESOURCE_MEM_PREF] = hose->mem_pref_resource;
- bus->resource[PCI_BUS_RESOURCE_IO] = hose->io_resource;
if (pcibios_assign_all_busses()) {
bus->number = bus_index++;
if (hose->set_busno)
hose->set_busno(hose, bus->number);
- if (bus->resource[PCI_BUS_RESOURCE_MEM])
- last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
+ if (hose->mem_resource)
+ last_mem = hose->mem_resource->start;
else
last_mem = 0;
- if (bus->resource[PCI_BUS_RESOURCE_MEM_PREF])
- last_mem_pref = bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->start;
+ if (hose->mem_pref_resource)
+ last_mem_pref = hose->mem_pref_resource->start;
else
last_mem_pref = 0;
- if (bus->resource[PCI_BUS_RESOURCE_IO])
- last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
+ if (hose->io_resource)
+ last_io = hose->io_resource->start;
else
last_io = 0;
}
@@ -152,6 +156,189 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask)
return size + 1;
}
+static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
+{
+ unsigned long flags = IORESOURCE_PCI_FIXED;
+
+ switch (prop) {
+ case PCI_EA_P_MEM:
+ case PCI_EA_P_VF_MEM:
+ flags |= IORESOURCE_MEM;
+ break;
+ case PCI_EA_P_MEM_PREFETCH:
+ case PCI_EA_P_VF_MEM_PREFETCH:
+ flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ break;
+ case PCI_EA_P_IO:
+ flags |= IORESOURCE_IO;
+ break;
+ default:
+ return 0;
+ }
+
+ return flags;
+}
+
+static struct resource *pci_ea_get_resource(struct pci_dev *dev, u8 bei,
+ u8 prop)
+{
+ if (bei <= PCI_EA_BEI_BAR5 && prop <= PCI_EA_P_IO)
+ return &dev->resource[bei];
+ else if (bei == PCI_EA_BEI_ROM)
+ return &dev->resource[PCI_ROM_RESOURCE];
+ else
+ return NULL;
+}
+
+/* Read an Enhanced Allocation (EA) entry */
+static int pci_ea_read(struct pci_dev *dev, int offset)
+{
+ struct resource *res;
+ int ent_size, ent_offset = offset;
+ resource_size_t start, end;
+ unsigned long flags;
+ u32 dw0, bei, base, max_offset;
+ u8 prop;
+ bool support_64 = (sizeof(resource_size_t) >= 8);
+
+ pci_read_config_dword(dev, ent_offset, &dw0);
+ ent_offset += 4;
+
+ /* Entry size field indicates DWORDs after 1st */
+ ent_size = (FIELD_GET(PCI_EA_ES, dw0) + 1) << 2;
+
+ if (!(dw0 & PCI_EA_ENABLE)) /* Entry not enabled */
+ goto out;
+
+ bei = FIELD_GET(PCI_EA_BEI, dw0);
+ prop = FIELD_GET(PCI_EA_PP, dw0);
+
+ /*
+ * If the Property is in the reserved range, try the Secondary
+ * Property instead.
+ */
+ if (prop > PCI_EA_P_BRIDGE_IO && prop < PCI_EA_P_MEM_RESERVED)
+ prop = FIELD_GET(PCI_EA_SP, dw0);
+ if (prop > PCI_EA_P_BRIDGE_IO)
+ goto out;
+
+ res = pci_ea_get_resource(dev, bei, prop);
+ if (!res) {
+ dev_dbg(&dev->dev, "Unsupported EA entry BEI: %u\n", bei);
+ goto out;
+ }
+
+ flags = pci_ea_flags(dev, prop);
+ if (!flags) {
+ dev_err(&dev->dev, "Unsupported EA properties: %#x\n", prop);
+ goto out;
+ }
+
+ /* Read Base */
+ pci_read_config_dword(dev, ent_offset, &base);
+ start = (base & PCI_EA_FIELD_MASK);
+ ent_offset += 4;
+
+ /* Read MaxOffset */
+ pci_read_config_dword(dev, ent_offset, &max_offset);
+ ent_offset += 4;
+
+ /* Read Base MSBs (if 64-bit entry) */
+ if (base & PCI_EA_IS_64) {
+ u32 base_upper;
+
+ pci_read_config_dword(dev, ent_offset, &base_upper);
+ ent_offset += 4;
+
+ flags |= IORESOURCE_MEM_64;
+
+ /* entry starts above 32-bit boundary, can't use */
+ if (!support_64 && base_upper)
+ goto out;
+
+ if (support_64)
+ start |= ((u64)base_upper << 32);
+ }
+
+ end = start + (max_offset | 0x03);
+
+ /* Read MaxOffset MSBs (if 64-bit entry) */
+ if (max_offset & PCI_EA_IS_64) {
+ u32 max_offset_upper;
+
+ pci_read_config_dword(dev, ent_offset, &max_offset_upper);
+ ent_offset += 4;
+
+ flags |= IORESOURCE_MEM_64;
+
+ /* entry too big, can't use */
+ if (!support_64 && max_offset_upper)
+ goto out;
+
+ if (support_64)
+ end += ((u64)max_offset_upper << 32);
+ }
+
+ if (end < start) {
+ dev_err(&dev->dev, "EA Entry crosses address boundary\n");
+ goto out;
+ }
+
+ if (ent_size != ent_offset - offset) {
+ dev_err(&dev->dev, "EA Entry Size (%d) does not match length read (%d)\n",
+ ent_size, ent_offset - offset);
+ goto out;
+ }
+
+ res->start = start;
+ res->end = end;
+ res->flags = flags;
+
+ if (bei <= PCI_EA_BEI_BAR5)
+ dev_dbg(&dev->dev, "BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n",
+ bei, res, prop);
+ else if (bei == PCI_EA_BEI_ROM)
+ dev_dbg(&dev->dev, "ROM: %pR (from Enhanced Allocation, properties %#02x)\n",
+ res, prop);
+ else if (bei >= PCI_EA_BEI_VF_BAR0 && bei <= PCI_EA_BEI_VF_BAR5)
+ dev_dbg(&dev->dev, "VF BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n",
+ bei - PCI_EA_BEI_VF_BAR0, res, prop);
+ else
+ dev_dbg(&dev->dev, "BEI %d res: %pR (from Enhanced Allocation, properties %#02x)\n",
+ bei, res, prop);
+
+out:
+ return offset + ent_size;
+}
+
+/* Enhanced Allocation Initialization */
+static void pci_ea_init(struct pci_dev *dev)
+{
+ int ea;
+ u8 num_ent;
+ int offset;
+ int i;
+
+ /* find PCI EA capability in list */
+ ea = pci_find_capability(dev, PCI_CAP_ID_EA);
+ if (!ea)
+ return;
+
+ /* determine the number of entries */
+ pci_bus_read_config_byte(dev->bus, dev->devfn, ea + PCI_EA_NUM_ENT,
+ &num_ent);
+ num_ent &= PCI_EA_NUM_ENT_MASK;
+
+ offset = ea + PCI_EA_FIRST_ENT;
+
+ /* Skip DWORD 2 for type 1 functions */
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+ offset += 4;
+
+ /* parse each EA entry */
+ for (i = 0; i < num_ent; ++i)
+ offset = pci_ea_read(dev, offset);
+}
static void setup_device(struct pci_dev *dev, int max_bar)
{
@@ -172,7 +359,7 @@ static void setup_device(struct pci_dev *dev, int max_bar)
u32 orig, mask, size;
unsigned long flags;
const char *kind;
- int busres;
+ struct resource *busres;
pci_read_config_dword(dev, pci_base_address_0, &orig);
pci_write_config_dword(dev, pci_base_address_0, 0xfffffffe);
@@ -189,20 +376,20 @@ static void setup_device(struct pci_dev *dev, int max_bar)
flags = IORESOURCE_IO;
kind = "IO";
last_addr = &last_io;
- busres = PCI_BUS_RESOURCE_IO;
+ busres = dev->bus->host->io_resource;
} else if ((mask & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
last_mem_pref) /* prefetchable MEM */ {
size = pci_size(orig, mask, 0xfffffff0);
flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
kind = "P-MEM";
last_addr = &last_mem_pref;
- busres = PCI_BUS_RESOURCE_MEM_PREF;
+ busres = dev->bus->host->mem_pref_resource;;
} else { /* non-prefetch MEM */
size = pci_size(orig, mask, 0xfffffff0);
flags = IORESOURCE_MEM;
kind = "NP-MEM";
last_addr = &last_mem;
- busres = PCI_BUS_RESOURCE_MEM;
+ busres = dev->bus->host->mem_resource;
}
if (!size) {
@@ -214,16 +401,27 @@ static void setup_device(struct pci_dev *dev, int max_bar)
size);
if (pcibios_assign_all_busses()) {
- if (ALIGN(*last_addr, size) + size >
- dev->bus->resource[busres]->end) {
+ struct resource res;
+ struct pci_bus_region region;
+
+ if (ALIGN(*last_addr, size) + size > busres->end) {
pr_debug("BAR does not fit within bus %s res\n", kind);
- return;
+ continue;
}
*last_addr = ALIGN(*last_addr, size);
- pci_write_config_dword(dev, pci_base_address_0, *last_addr);
+
+ res.flags = flags;
+ res.start = *last_addr;
+ res.end = res.start + size - 1;
+
+ pcibios_resource_to_bus(dev->bus, &region, &res);
+
+ pci_write_config_dword(dev, pci_base_address_0,
+ lower_32_bits(region.start));
if (mask & PCI_BASE_ADDRESS_MEM_TYPE_64)
- pci_write_config_dword(dev, pci_base_address_1, 0);
+ pci_write_config_dword(dev, pci_base_address_1,
+ upper_32_bits(region.start));
start = *last_addr;
*last_addr += size;
} else {
@@ -249,6 +447,8 @@ static void setup_device(struct pci_dev *dev, int max_bar)
}
}
+ pci_ea_init(dev);
+
pci_fixup_device(pci_fixup_header, dev);
if (pcibios_assign_all_busses())
@@ -341,15 +541,15 @@ static void postscan_setup_bridge(struct pci_dev *dev)
}
static struct device_node *
-pci_of_match_device(struct device_d *parent, unsigned int devfn)
+pci_of_match_device(struct device *parent, unsigned int devfn)
{
struct device_node *np;
u32 reg;
- if (!IS_ENABLED(CONFIG_OFTREE) || !parent || !parent->device_node)
+ if (!IS_ENABLED(CONFIG_OFTREE) || !parent || !parent->of_node)
return NULL;
- for_each_child_of_node(parent->device_node, np) {
+ for_each_child_of_node(parent->of_node, np) {
if (!of_property_read_u32_array(np, "reg", &reg, 1)) {
/*
* Only match device/function pair of the device
@@ -365,6 +565,38 @@ pci_of_match_device(struct device_d *parent, unsigned int devfn)
return NULL;
}
+/**
+ * pcie_flr - initiate a PCIe function level reset
+ * @dev: device to reset
+ *
+ * Initiate a function level reset on @dev.
+ */
+int pci_flr(struct pci_dev *pdev)
+{
+ u16 val;
+ int pcie_off;
+ u32 cap;
+
+ /* look for PCI Express Capability */
+ pcie_off = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (!pcie_off)
+ return -ENOENT;
+
+ /* check FLR capability */
+ pci_read_config_dword(pdev, pcie_off + PCI_EXP_DEVCAP, &cap);
+ if (!(cap & PCI_EXP_DEVCAP_FLR))
+ return -ENOENT;
+
+ pci_read_config_word(pdev, pcie_off + PCI_EXP_DEVCTL, &val);
+ val |= PCI_EXP_DEVCTL_BCR_FLR;
+ pci_write_config_word(pdev, pcie_off + PCI_EXP_DEVCTL, val);
+
+ /* wait 100ms, per PCI spec */
+ mdelay(100);
+
+ return 0;
+}
+
static unsigned int pci_scan_bus(struct pci_bus *bus)
{
struct pci_dev *dev;
@@ -379,6 +611,8 @@ static unsigned int pci_scan_bus(struct pci_bus *bus)
max = bus->secondary;
for (devfn = 0; devfn < 0xff; ++devfn) {
+ struct device *parent;
+
if (PCI_FUNC(devfn) && !is_multi) {
/* not a multi-function device */
continue;
@@ -398,11 +632,12 @@ static unsigned int pci_scan_bus(struct pci_bus *bus)
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
- dev->dev.parent = bus->parent;
- dev->dev.device_node = pci_of_match_device(bus->parent, devfn);
- if (dev->dev.device_node)
- pr_debug("found DT node %s for device %04x:%04x\n",
- dev->dev.device_node->full_name,
+ parent = bus->self ? &bus->self->dev : bus->host->parent;
+ dev->dev.parent = parent;
+ dev->dev.of_node = pci_of_match_device(parent, devfn);
+ if (dev->dev.of_node)
+ pr_debug("found DT node %pOF for device %04x:%04x\n",
+ dev->dev.of_node,
dev->vendor, dev->device);
/* non-destructively determine if device can be a master: */
@@ -426,26 +661,23 @@ static unsigned int pci_scan_bus(struct pci_bus *bus)
pr_debug("%02x:%02x [%04x:%04x]\n", bus->number, dev->devfn,
dev->vendor, dev->device);
- switch (hdr_type & 0x7f) {
+ switch (hdr_type & PCI_HEADER_TYPE_MASK) {
case PCI_HEADER_TYPE_NORMAL:
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
setup_device(dev, 6);
+
+ pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
+ pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
break;
case PCI_HEADER_TYPE_BRIDGE:
child_bus = pci_alloc_bus();
/* inherit parent properties */
child_bus->host = bus->host;
- child_bus->parent_bus = bus;
- child_bus->resource[PCI_BUS_RESOURCE_MEM] =
- bus->resource[PCI_BUS_RESOURCE_MEM];
- child_bus->resource[PCI_BUS_RESOURCE_MEM_PREF] =
- bus->resource[PCI_BUS_RESOURCE_MEM_PREF];
- child_bus->resource[PCI_BUS_RESOURCE_IO] =
- bus->resource[PCI_BUS_RESOURCE_IO];
+ child_bus->parent = bus;
- child_bus->parent = &dev->dev;
+ child_bus->self = dev;
if (pcibios_assign_all_busses()) {
child_bus->number = bus_index++;
@@ -539,6 +771,22 @@ int pci_enable_device(struct pci_dev *dev)
}
EXPORT_SYMBOL(pci_enable_device);
+/**
+ * pci_select_bars - Make BAR mask from the type of resource
+ * @dev: the PCI device for which BAR mask is made
+ * @flags: resource type mask to be selected
+ *
+ * This helper routine makes bar mask from the type of resource.
+ */
+int pci_select_bars(struct pci_dev *dev, unsigned long flags)
+{
+ int i, bars = 0;
+ for (i = 0; i < PCI_NUM_RESOURCES; i++)
+ if (pci_resource_flags(dev, i) & flags)
+ bars |= (1 << i);
+ return bars;
+}
+
static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap, int *ttl)
{
@@ -621,7 +869,7 @@ u8 pci_find_capability(struct pci_dev *dev, int cap)
{
u8 pos;
- pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);
+ pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type & PCI_HEADER_TYPE_MASK);
if (pos)
pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);
diff --git a/drivers/pci/pcie-designware-host.c b/drivers/pci/pcie-designware-host.c
index a8309653c6..cd1d9433aa 100644
--- a/drivers/pci/pcie-designware-host.c
+++ b/drivers/pci/pcie-designware-host.c
@@ -30,7 +30,6 @@
#include <abort.h>
static const struct pci_ops dw_pcie_ops;
-static unsigned long global_io_offset;
static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
@@ -71,35 +70,25 @@ static void dw_pcie_set_local_bus_nr(struct pci_controller *host, int busno)
int __init dw_pcie_host_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- struct device_d *dev = pci->dev;
- struct device_node *np = dev->device_node;
- struct of_pci_range range;
+ struct device *dev = pci->dev;
+ struct device_node *np = dev->of_node;
struct of_pci_range_parser parser;
struct resource *cfg_res;
- u32 na, ns;
- const __be32 *addrp;
- int index, ret;
+ struct resource_entry *window;
+ int ret;
- /* Find the address cell size and the number of cells in order to get
- * the untranslated address.
- */
- of_property_read_u32(np, "#address-cells", &na);
- ns = of_n_size_cells(np);
+ pp->pci.parent = dev;
+ pci_controller_init(&pp->pci);
cfg_res = dev_get_resource_by_name(dev, IORESOURCE_MEM, "config");
- if (cfg_res) {
+ if (!IS_ERR(cfg_res)) {
pp->cfg0_size = resource_size(cfg_res) >> 1;
pp->cfg1_size = resource_size(cfg_res) >> 1;
pp->cfg0_base = cfg_res->start;
pp->cfg1_base = cfg_res->start + pp->cfg0_size;
-
- /* Find the untranslated configuration space address */
- index = of_property_match_string(np, "reg-names", "config");
- addrp = of_get_address(np, index, NULL, NULL);
- pp->cfg0_mod_base = of_read_number(addrp, ns);
- pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size;
} else {
dev_err(dev, "Missing *config* reg space\n");
+ return -ENODEV;
}
if (of_pci_range_parser_init(&parser, np)) {
@@ -107,52 +96,23 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
return -EINVAL;
}
- /* Get the I/O and memory ranges from DT */
- for_each_of_pci_range(&parser, &range) {
- unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
-
- if (restype == IORESOURCE_IO) {
- of_pci_range_to_resource(&range, np, &pp->io);
- pp->io.name = "I/O";
- pp->io.start = range.pci_addr + global_io_offset;
- pp->io.end = range.pci_addr + range.size + global_io_offset - 1;
- pp->io_size = resource_size(&pp->io);
- pp->io_bus_addr = range.pci_addr;
- pp->io_base = range.cpu_addr;
-
- /* Find the untranslated IO space address */
- pp->io_mod_base = of_read_number(parser.range -
- parser.np + na, ns);
- }
- if (restype == IORESOURCE_MEM) {
- of_pci_range_to_resource(&range, np, &pp->mem);
- pp->mem.name = "MEM";
- pp->mem_size = resource_size(&pp->mem);
- pp->mem_bus_addr = range.pci_addr;
-
- /* Find the untranslated MEM space address */
- pp->mem_mod_base = of_read_number(parser.range -
- parser.np + na, ns);
- }
- if (restype == 0) {
- of_pci_range_to_resource(&range, np, &pp->cfg);
- pp->cfg0_size = resource_size(&pp->cfg) >> 1;
- pp->cfg1_size = resource_size(&pp->cfg) >> 1;
- pp->cfg0_base = pp->cfg.start;
- pp->cfg1_base = pp->cfg.start + pp->cfg0_size;
-
- /* Find the untranslated configuration space address */
- pp->cfg0_mod_base = of_read_number(parser.range -
- parser.np + na, ns);
- pp->cfg1_mod_base = pp->cfg0_mod_base +
- pp->cfg0_size;
+ resource_list_for_each_entry(window, &pp->pci.windows) {
+ switch (resource_type(window->res)) {
+ case IORESOURCE_IO:
+ pp->io_size = resource_size(window->res);
+ pp->io_bus_addr = window->res->start - window->offset;
+ pp->io_base = window->res->start;
+ break;
+ case IORESOURCE_MEM:
+ pp->mem_size = resource_size(window->res);
+ pp->mem_bus_addr = window->res->start - window->offset;
+ pp->mem_base = window->res->start;
+ break;
}
}
if (!pci->dbi_base)
- pci->dbi_base = IOMEM(pp->cfg.start);
-
- pp->mem_base = pp->mem.start;
+ pci->dbi_base = IOMEM(cfg_res->start);
if (!pp->va_cfg0_base)
pp->va_cfg0_base = IOMEM((unsigned long)pp->cfg0_base);
@@ -170,11 +130,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
return ret;
}
- pp->pci.parent = dev;
pp->pci.pci_ops = &dw_pcie_ops;
pp->pci.set_busno = dw_pcie_set_local_bus_nr;
- pp->pci.mem_resource = &pp->mem;
- pp->pci.io_resource = &pp->io;
register_pci_controller(&pp->pci);
@@ -200,12 +157,12 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
if (bus->primary == pp->root_bus_nr) {
type = PCIE_ATU_TYPE_CFG0;
- cpu_addr = pp->cfg0_mod_base;
+ cpu_addr = pp->cfg0_base;
cfg_size = pp->cfg0_size;
va_cfg_base = pp->va_cfg0_base;
} else {
type = PCIE_ATU_TYPE_CFG1;
- cpu_addr = pp->cfg1_mod_base;
+ cpu_addr = pp->cfg1_base;
cfg_size = pp->cfg1_size;
va_cfg_base = pp->va_cfg1_base;
}
@@ -216,7 +173,7 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
ret = dw_pcie_read(va_cfg_base + where, size, val);
if (pci->num_viewport <= 2)
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
- PCIE_ATU_TYPE_IO, pp->io_mod_base,
+ PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
return ret;
@@ -240,12 +197,12 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
if (bus->primary == pp->root_bus_nr) {
type = PCIE_ATU_TYPE_CFG0;
- cpu_addr = pp->cfg0_mod_base;
+ cpu_addr = pp->cfg0_base;
cfg_size = pp->cfg0_size;
va_cfg_base = pp->va_cfg0_base;
} else {
type = PCIE_ATU_TYPE_CFG1;
- cpu_addr = pp->cfg1_mod_base;
+ cpu_addr = pp->cfg1_base;
cfg_size = pp->cfg1_size;
va_cfg_base = pp->va_cfg1_base;
}
@@ -256,7 +213,7 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
ret = dw_pcie_write(va_cfg_base + where, size, val);
if (pci->num_viewport <= 2)
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
- PCIE_ATU_TYPE_IO, pp->io_mod_base,
+ PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
return ret;
@@ -371,7 +328,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
*/
if (!pp->ops->rd_other_conf) {
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0,
- PCIE_ATU_TYPE_MEM, pp->mem_mod_base,
+ PCIE_ATU_TYPE_MEM, pp->mem_base,
pp->mem_bus_addr, pp->mem_size);
if (pci->num_viewport > 2)
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2,
@@ -391,4 +348,4 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
-} \ No newline at end of file
+}
diff --git a/drivers/pci/pcie-designware.c b/drivers/pci/pcie-designware.c
index ecfd6d5fa6..72820de282 100644
--- a/drivers/pci/pcie-designware.c
+++ b/drivers/pci/pcie-designware.c
@@ -237,15 +237,17 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
int dw_pcie_wait_for_link(struct dw_pcie *pci)
{
- int retries;
+ u64 start = get_time_ns();
/* Check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ while (1) {
if (dw_pcie_link_up(pci)) {
dev_dbg(pci->dev, "Link up\n");
return 0;
}
- udelay(LINK_WAIT_USLEEP_MAX);
+
+ if (is_timeout(start, SECOND))
+ break;
}
dev_err(pci->dev, "Phy link never came up\n");
@@ -281,8 +283,8 @@ void dw_pcie_setup(struct dw_pcie *pci)
int ret;
u32 val;
u32 lanes;
- struct device_d *dev = pci->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = pci->dev;
+ struct device_node *np = dev->of_node;
if (pci->version >= 0x480A || (!pci->version &&
dw_pcie_iatu_unroll_enabled(pci))) {
diff --git a/drivers/pci/pcie-designware.h b/drivers/pci/pcie-designware.h
index 7a4516dd1b..0c93e6f3b8 100644
--- a/drivers/pci/pcie-designware.h
+++ b/drivers/pci/pcie-designware.h
@@ -13,10 +13,6 @@
#include <linux/bitfield.h>
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MAX 100000
-
/* Parameters for the waiting for iATU enabled routine */
#define LINK_WAIT_MAX_IATU_RETRIES 5
#define LINK_WAIT_IATU_MAX 10000
@@ -147,22 +143,17 @@ struct dw_pcie_host_ops {
struct pcie_port {
u8 root_bus_nr;
u64 cfg0_base;
- u64 cfg0_mod_base;
void __iomem *va_cfg0_base;
u32 cfg0_size;
u64 cfg1_base;
- u64 cfg1_mod_base;
void __iomem *va_cfg1_base;
u32 cfg1_size;
u64 io_base;
- u64 io_mod_base;
phys_addr_t io_bus_addr;
u32 io_size;
u64 mem_base;
- u64 mem_mod_base;
phys_addr_t mem_bus_addr;
u32 mem_size;
- struct resource cfg;
struct resource io;
struct resource mem;
struct resource busn;
@@ -180,7 +171,7 @@ struct dw_pcie_ops {
};
struct dw_pcie {
- struct device_d *dev;
+ struct device *dev;
void __iomem *dbi_base;
/* Used when iatu_unroll_enabled is true */
void __iomem *atu_base;
diff --git a/drivers/pci/pcie-dw-rockchip.c b/drivers/pci/pcie-dw-rockchip.c
new file mode 100644
index 0000000000..22f41cc6bb
--- /dev/null
+++ b/drivers/pci/pcie-dw-rockchip.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe host controller driver for Rockchip SoCs.
+ *
+ * Copyright (C) 2021 Rockchip Electronics Co., Ltd.
+ * http://www.rock-chips.com
+ *
+ * Author: Simon Xue <xxm@rock-chips.com>
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <abort.h>
+#include <malloc.h>
+#include <io.h>
+#include <init.h>
+#include <gpio.h>
+#include <asm/mmu.h>
+#include <of_gpio.h>
+#include <of_device.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <of_address.h>
+#include <of_pci.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <linux/sizes.h>
+#include <linux/bitfield.h>
+
+#include "pcie-designware.h"
+
+/*
+ * The upper 16 bits of PCIE_CLIENT_CONFIG are a write
+ * mask for the lower 16 bits.
+ */
+#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val))
+#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val)
+#define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val)
+
+#define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40)
+#define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc)
+#define PCIE_SMLH_LINKUP BIT(16)
+#define PCIE_RDLH_LINKUP BIT(17)
+#define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP)
+#define PCIE_L0S_ENTRY 0x11
+#define PCIE_CLIENT_GENERAL_CONTROL 0x0
+#define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8
+#define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c
+#define PCIE_CLIENT_GENERAL_DEBUG 0x104
+#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
+#define PCIE_CLIENT_LTSSM_STATUS 0x300
+#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
+#define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0)
+
+struct rockchip_pcie {
+ struct dw_pcie pci;
+ void __iomem *apb_base;
+ struct phy *phy;
+ struct clk_bulk_data *clks;
+ unsigned int clk_cnt;
+ struct reset_control *rst;
+ struct gpio_desc *rst_gpio;
+ struct regulator *vpcie3v3;
+ struct irq_domain *irq_domain;
+};
+
+static inline struct rockchip_pcie *to_rockchip_pcie(struct dw_pcie *dw)
+{
+ return container_of(dw, struct rockchip_pcie, pci);
+}
+
+static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip,
+ u32 reg)
+{
+ return readl_relaxed(rockchip->apb_base + reg);
+}
+
+static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip,
+ u32 val, u32 reg)
+{
+ writel_relaxed(val, rockchip->apb_base + reg);
+}
+
+static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
+{
+ rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
+ PCIE_CLIENT_GENERAL_CONTROL);
+}
+
+static int rockchip_pcie_link_up(struct dw_pcie *pci)
+{
+ struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
+ u32 val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS);
+
+ if ((val & PCIE_LINKUP) == PCIE_LINKUP &&
+ (val & PCIE_LTSSM_STATUS_MASK) == PCIE_L0S_ENTRY)
+ return 1;
+
+ return 0;
+}
+
+static int rockchip_pcie_start_link(struct dw_pcie *pci)
+{
+ struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
+
+ /* Reset device */
+ gpiod_set_value(rockchip->rst_gpio, 0);
+
+ rockchip_pcie_enable_ltssm(rockchip);
+
+ /*
+ * PCIe requires the refclk to be stable for 100µs prior to releasing
+ * PERST. See table 2-4 in section 2.6.2 AC Specifications of the PCI
+ * Express Card Electromechanical Specification, 1.1. However, we don't
+ * know if the refclk is coming from RC's PHY or external OSC. If it's
+ * from RC, so enabling LTSSM is the just right place to release #PERST.
+ * We need more extra time as before, rather than setting just
+ * 100us as we don't know how long should the device need to reset.
+ */
+ mdelay(100);
+ gpiod_set_value(rockchip->rst_gpio, 1);
+
+ dw_pcie_wait_for_link(pci);
+
+ return 0;
+}
+
+static int rockchip_pcie_host_init(struct pcie_port *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
+ u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE);
+
+ /* LTSSM enable control mode */
+ rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
+
+ rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_RC_MODE,
+ PCIE_CLIENT_GENERAL_CONTROL);
+
+ dw_pcie_setup_rc(pp);
+ rockchip_pcie_start_link(pci);
+
+ return 0;
+}
+
+static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
+ .host_init = rockchip_pcie_host_init,
+};
+
+static int rockchip_pcie_clk_init(struct rockchip_pcie *rockchip)
+{
+ struct device *dev = rockchip->pci.dev;
+ int ret;
+
+ ret = clk_bulk_get_all(dev, &rockchip->clks);
+ if (ret < 0)
+ return ret;
+
+ rockchip->clk_cnt = ret;
+
+ return clk_bulk_enable(rockchip->clk_cnt, rockchip->clks);
+}
+
+static int rockchip_pcie_resource_get(struct device *dev,
+ struct rockchip_pcie *rockchip)
+{
+ struct resource *r;
+
+ r = dev_request_mem_resource_by_name(dev, "apb");
+ if (IS_ERR(r))
+ return PTR_ERR(r);
+ rockchip->apb_base = IOMEM(r->start);
+
+
+ r = dev_request_mem_resource_by_name(dev, "dbi");
+ if (IS_ERR(r))
+ return PTR_ERR(r);
+ rockchip->pci.dbi_base = IOMEM(r->start);
+
+ rockchip->rst_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(rockchip->rst_gpio))
+ return PTR_ERR(rockchip->rst_gpio);
+
+ rockchip->rst = reset_control_array_get(dev);
+ if (IS_ERR(rockchip->rst))
+ return dev_err_probe(dev, PTR_ERR(rockchip->rst),
+ "failed to get reset lines\n");
+
+ return 0;
+}
+
+static int rockchip_pcie_phy_init(struct rockchip_pcie *rockchip)
+{
+ struct device *dev = rockchip->pci.dev;
+ int ret;
+
+ rockchip->phy = phy_get(dev, "pcie-phy");
+ if (IS_ERR(rockchip->phy))
+ return dev_err_probe(dev, PTR_ERR(rockchip->phy),
+ "missing PHY\n");
+
+ ret = phy_init(rockchip->phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_power_on(rockchip->phy);
+ if (ret)
+ phy_exit(rockchip->phy);
+
+ return ret;
+}
+
+static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rockchip)
+{
+ phy_exit(rockchip->phy);
+ phy_power_off(rockchip->phy);
+}
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = rockchip_pcie_link_up,
+};
+
+static int rockchip_pcie_probe(struct device *dev)
+{
+ struct rockchip_pcie *rockchip;
+ struct pcie_port *pp;
+ int ret;
+
+ rockchip = xzalloc(sizeof(*rockchip));
+ if (!rockchip)
+ return -ENOMEM;
+
+ rockchip->pci.dev = dev;
+ rockchip->pci.ops = &dw_pcie_ops;
+
+ pp = &rockchip->pci.pp;
+ pp->ops = &rockchip_pcie_host_ops;
+
+ ret = rockchip_pcie_resource_get(dev, rockchip);
+ if (ret)
+ return ret;
+
+ ret = reset_control_assert(rockchip->rst);
+ if (ret)
+ return ret;
+
+ /* DON'T MOVE ME: must be enable before PHY init */
+ rockchip->vpcie3v3 = regulator_get(dev, "vpcie3v3");
+ if (IS_ERR(rockchip->vpcie3v3)) {
+ if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(rockchip->vpcie3v3),
+ "failed to get vpcie3v3 regulator\n");
+ rockchip->vpcie3v3 = NULL;
+ } else {
+ ret = regulator_enable(rockchip->vpcie3v3);
+ if (ret) {
+ dev_err(dev, "failed to enable vpcie3v3 regulator\n");
+ return ret;
+ }
+ }
+
+ ret = rockchip_pcie_phy_init(rockchip);
+ if (ret)
+ goto disable_regulator;
+
+ ret = reset_control_deassert(rockchip->rst);
+ if (ret)
+ goto deinit_phy;
+
+ ret = rockchip_pcie_clk_init(rockchip);
+ if (ret)
+ goto deinit_phy;
+
+ ret = dw_pcie_host_init(pp);
+ if (!ret)
+ return 0;
+
+ clk_bulk_disable(rockchip->clk_cnt, rockchip->clks);
+deinit_phy:
+ rockchip_pcie_phy_deinit(rockchip);
+disable_regulator:
+ if (rockchip->vpcie3v3)
+ regulator_disable(rockchip->vpcie3v3);
+
+ return ret;
+}
+
+static const struct of_device_id rockchip_pcie_of_match[] = {
+ { .compatible = "rockchip,rk3568-pcie", },
+ { .compatible = "rockchip,rk3588-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_pcie_of_match);
+
+static struct driver rockchip_pcie_driver = {
+ .name = "rockchip-dw-pcie",
+ .of_compatible = DRV_OF_COMPAT(rockchip_pcie_of_match),
+ .probe = rockchip_pcie_probe,
+};
+device_platform_driver(rockchip_pcie_driver);
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 4df9bd0523..52c7929149 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -29,6 +29,7 @@ source "drivers/phy/rockchip/Kconfig"
config PHY_STM32_USBPHYC
tristate "STM32 USB HS PHY Controller"
depends on ARCH_STM32MP || COMPILE_TEST
+ depends on COMMON_CLK
help
Enable this to support the High-Speed USB transceivers that are part
of some STMicroelectronics STM32 SoCs.
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 4eb1f9a55c..04e8bcf188 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -1,5 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
+
config PHY_FSL_IMX8MQ_USB
- bool "Freescale i.MX8M USB3 PHY"
- default ARCH_IMX8MQ
+ bool "Freescale i.MX8MQ/P USB3 PHY"
+ default ARCH_IMX8MQ || ARCH_IMX8MP
+ help
+ Enable this to add support for the USB PHY found on
+ the i.MX8M Quad and Plus.
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index fc5fb006b5..b9f9fad1fd 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -13,7 +13,7 @@
#include <malloc.h>
#include <of_device.h>
#include <of.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
#define PHY_CTRL0 0x0
@@ -141,8 +141,9 @@ static const struct of_device_id imx8mq_usb_phy_of_match[] = {
.data = &imx8mp_usb_phy_ops,},
{ }
};
+MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
-static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev,
+static struct phy *imx8mq_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct imx8mq_usb_phy *imx_phy = dev->priv;
@@ -150,7 +151,7 @@ static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev,
return imx_phy->phy;
}
-static int imx8mq_usb_phy_probe(struct device_d *dev)
+static int imx8mq_usb_phy_probe(struct device *dev)
{
struct phy_provider *phy_provider;
struct imx8mq_usb_phy *imx_phy;
@@ -183,7 +184,7 @@ static int imx8mq_usb_phy_probe(struct device_d *dev)
return PTR_ERR_OR_ZERO(phy_provider);
}
-static struct driver_d imx8mq_usb_phy_driver = {
+static struct driver imx8mq_usb_phy_driver = {
.name = "imx8mq-usb-phy",
.probe = imx8mq_usb_phy_probe,
.of_compatible = DRV_OF_COMPAT(imx8mq_usb_phy_of_match),
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 3a36f0a1b2..0a2f1b0d11 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -11,7 +11,7 @@
#include <common.h>
#include <malloc.h>
#include <linux/phy/phy.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
static LIST_HEAD(phy_provider_list);
static int phy_ida;
@@ -24,7 +24,7 @@ static int phy_ida;
*
* Called to create a phy using phy framework.
*/
-struct phy *phy_create(struct device_d *dev, struct device_node *node,
+struct phy *phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops)
{
int ret;
@@ -43,7 +43,7 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node,
dev_set_name(&phy->dev, "phy");
phy->dev.id = id;
phy->dev.parent = dev;
- phy->dev.device_node = node ?: dev->device_node;
+ phy->dev.of_node = node ?: dev->of_node;
phy->id = id;
phy->ops = ops;
@@ -79,9 +79,9 @@ free_ida:
* This is used in the case of dt boot for finding the phy instance from
* phy provider.
*/
-struct phy_provider *__of_phy_provider_register(struct device_d *dev,
- struct phy * (*of_xlate)(struct device_d *dev,
- struct of_phandle_args *args))
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
{
struct phy_provider *phy_provider;
@@ -203,6 +203,21 @@ int phy_power_off(struct phy *phy)
return 0;
}
+int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set_mode)
+ return 0;
+
+ ret = phy->ops->set_mode(phy, mode, submode);
+ if (!ret)
+ phy->attrs.mode = mode;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_mode_ext);
+
struct usb_phy *phy_to_usbphy(struct phy *phy)
{
if (!phy)
@@ -225,10 +240,10 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
return ERR_PTR(ret);
list_for_each_entry(phy_provider, &phy_provider_list, list) {
- if (phy_provider->dev->device_node == node)
+ if (phy_provider->dev->of_node == node)
return phy_provider;
- for_each_child_of_node(phy_provider->dev->device_node, child)
+ for_each_child_of_node(phy_provider->dev->of_node, child)
if (child == node)
return phy_provider;
}
@@ -295,21 +310,21 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id)
* -ENODEV if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
-struct phy *of_phy_get_by_phandle(struct device_d *dev, const char *phandle,
+struct phy *of_phy_get_by_phandle(struct device *dev, const char *phandle,
u8 index)
{
struct device_node *np;
struct phy_provider *phy_provider;
- if (!dev->device_node) {
+ if (!dev->of_node) {
dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
- np = of_parse_phandle(dev->device_node, phandle, index);
+ np = of_parse_phandle(dev->of_node, phandle, index);
if (!np) {
- dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
- dev->device_node->full_name);
+ dev_dbg(dev, "failed to get %s phandle in %pOF node\n", phandle,
+ dev->of_node);
return ERR_PTR(-ENODEV);
}
@@ -331,7 +346,7 @@ struct phy *of_phy_get_by_phandle(struct device_d *dev, const char *phandle,
* -ENODEV if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
-struct phy *phy_get(struct device_d *dev, const char *string)
+struct phy *phy_get(struct device *dev, const char *string)
{
int index = 0;
struct phy *phy = ERR_PTR(-ENODEV);
@@ -341,10 +356,10 @@ struct phy *phy_get(struct device_d *dev, const char *string)
return ERR_PTR(-EINVAL);
}
- if (dev->device_node) {
- index = of_property_match_string(dev->device_node, "phy-names",
- string);
- phy = _of_phy_get(dev->device_node, index);
+ if (dev->of_node) {
+ index = of_property_match_string(dev->of_node, "phy-names",
+ string);
+ phy = _of_phy_get(dev->of_node, index);
}
return phy;
@@ -360,7 +375,7 @@ struct phy *phy_get(struct device_d *dev, const char *string)
* NULL if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
-struct phy *phy_optional_get(struct device_d *dev, const char *string)
+struct phy *phy_optional_get(struct device *dev, const char *string)
{
struct phy *phy = phy_get(dev, string);
@@ -377,10 +392,10 @@ struct phy *phy_optional_get(struct device_d *dev, const char *string)
*
* Gets the phy using _of_phy_get()
*/
-struct phy *phy_get_by_index(struct device_d *dev, int index)
+struct phy *phy_get_by_index(struct device *dev, int index)
{
- if (!dev->device_node)
+ if (!dev->of_node)
return ERR_PTR(-ENODEV);
- return _of_phy_get(dev->device_node, index);
+ return _of_phy_get(dev->of_node, index);
}
diff --git a/drivers/phy/phy-stm32-usbphyc.c b/drivers/phy/phy-stm32-usbphyc.c
index 2fa1f0fd01..6bac5e1e59 100644
--- a/drivers/phy/phy-stm32-usbphyc.c
+++ b/drivers/phy/phy-stm32-usbphyc.c
@@ -14,11 +14,14 @@
#include <linux/phy/phy.h>
#include <linux/reset.h>
#include <linux/math64.h>
-#include <usb/phy.h>
+#include <asm-generic/atomic.h>
+#include <linux/usb/phy.h>
+#include <linux/units.h>
#define STM32_USBPHYC_PLL 0x0
#define STM32_USBPHYC_MISC 0x8
#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100))
+#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100))
#define STM32_USBPHYC_VERSION 0x3F4
/* STM32_USBPHYC_PLL bit fields */
@@ -40,6 +43,83 @@
#define STM32_USBPHYC_MON_SEL_LOCKP 0x1F
#define STM32_USBPHYC_MON_OUT_LOCKP BIT(3)
+/* STM32_USBPHYC_TUNE bit fields */
+#define INCURREN BIT(0)
+#define INCURRINT BIT(1)
+#define LFSCAPEN BIT(2)
+#define HSDRVSLEW BIT(3)
+#define HSDRVDCCUR BIT(4)
+#define HSDRVDCLEV BIT(5)
+#define HSDRVCURINCR BIT(6)
+#define FSDRVRFADJ BIT(7)
+#define HSDRVRFRED BIT(8)
+#define HSDRVCHKITRM GENMASK(12, 9)
+#define HSDRVCHKZTRM GENMASK(14, 13)
+#define OTPCOMP GENMASK(19, 15)
+#define SQLCHCTL GENMASK(21, 20)
+#define HDRXGNEQEN BIT(22)
+#define HSRXOFF GENMASK(24, 23)
+#define HSFALLPREEM BIT(25)
+#define SHTCCTCTLPROT BIT(26)
+#define STAGSEL BIT(27)
+
+enum boosting_vals {
+ BOOST_1000_UA = 1000,
+ BOOST_2000_UA = 2000,
+};
+
+enum dc_level_vals {
+ DC_NOMINAL,
+ DC_PLUS_5_TO_7_MV,
+ DC_PLUS_10_TO_14_MV,
+ DC_MINUS_5_TO_7_MV,
+ DC_MAX,
+};
+
+enum current_trim {
+ CUR_NOMINAL,
+ CUR_PLUS_1_56_PCT,
+ CUR_PLUS_3_12_PCT,
+ CUR_PLUS_4_68_PCT,
+ CUR_PLUS_6_24_PCT,
+ CUR_PLUS_7_8_PCT,
+ CUR_PLUS_9_36_PCT,
+ CUR_PLUS_10_92_PCT,
+ CUR_PLUS_12_48_PCT,
+ CUR_PLUS_14_04_PCT,
+ CUR_PLUS_15_6_PCT,
+ CUR_PLUS_17_16_PCT,
+ CUR_PLUS_19_01_PCT,
+ CUR_PLUS_20_58_PCT,
+ CUR_PLUS_22_16_PCT,
+ CUR_PLUS_23_73_PCT,
+ CUR_MAX,
+};
+
+enum impedance_trim {
+ IMP_NOMINAL,
+ IMP_MINUS_2_OHMS,
+ IMP_MINUS_4_OMHS,
+ IMP_MINUS_6_OHMS,
+ IMP_MAX,
+};
+
+enum squelch_level {
+ SQLCH_NOMINAL,
+ SQLCH_PLUS_7_MV,
+ SQLCH_MINUS_5_MV,
+ SQLCH_PLUS_14_MV,
+ SQLCH_MAX,
+};
+
+enum rx_offset {
+ NO_RX_OFFSET,
+ RX_OFFSET_PLUS_5_MV,
+ RX_OFFSET_PLUS_10_MV,
+ RX_OFFSET_MINUS_5_MV,
+ RX_OFFSET_MAX,
+};
+
/* STM32_USBPHYC_VERSION bit fields */
#define MINREV GENMASK(3, 0)
#define MAJREV GENMASK(7, 4)
@@ -47,7 +127,6 @@
#define PLL_FVCO_MHZ 2880
#define PLL_INFF_MIN_RATE_HZ 19200000
#define PLL_INFF_MAX_RATE_HZ 38400000
-#define HZ_PER_MHZ 1000000L
struct pll_params {
u8 ndiv;
@@ -57,30 +136,34 @@ struct pll_params {
struct stm32_usbphyc_phy {
struct phy *phy;
struct stm32_usbphyc *usbphyc;
+ struct regulator *vbus;
u32 index;
bool active;
+ u32 tune;
};
struct stm32_usbphyc {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct clk *clk;
+ struct reset_control *rst;
struct stm32_usbphyc_phy **phys;
int nphys;
struct regulator *vdda1v1;
struct regulator *vdda1v8;
- int n_pll_cons;
+ atomic_t n_pll_cons;
+ struct clk_hw clk48_hw;
int switch_setup;
};
static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
{
- writel(readl(reg) | bits, reg);
+ writel_relaxed(readl_relaxed(reg) | bits, reg);
}
static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
{
- writel(readl(reg) & ~bits, reg);
+ writel_relaxed(readl_relaxed(reg) & ~bits, reg);
}
static int stm32_usbphyc_regulators_enable(struct stm32_usbphyc *usbphyc)
@@ -168,7 +251,7 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
if (pll_params.frac)
usbphyc_pll |= PLLFRACCTL | frac;
- writel(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
+ writel_relaxed(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
@@ -185,7 +268,7 @@ static int __stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
stm32_usbphyc_clr_bits(pll_reg, PLLEN);
/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
- if (readl_poll_timeout(pll_reg, pllen, !(pllen & PLLEN), 50))
+ if (readl_relaxed_poll_timeout(pll_reg, pllen, !(pllen & PLLEN), 50))
dev_err(usbphyc->dev, "PLL not reset\n");
return stm32_usbphyc_regulators_disable(usbphyc);
@@ -194,7 +277,7 @@ static int __stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
{
/* Check if a phy port is still active or clk48 in use */
- if (--usbphyc->n_pll_cons > 0)
+ if (atomic_dec_return(&usbphyc->n_pll_cons) != 1)
return 0;
return __stm32_usbphyc_pll_disable(usbphyc);
@@ -203,14 +286,14 @@ static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
{
void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
- bool pllen = readl(pll_reg) & PLLEN;
+ bool pllen = readl_relaxed(pll_reg) & PLLEN;
int ret;
/*
* Check if a phy port or clk48 prepare has configured the pll
* and ensure the PLL is enabled
*/
- if (++usbphyc->n_pll_cons > 1 && pllen)
+ if (atomic_inc_return(&usbphyc->n_pll_cons) > 1 && pllen)
return 0;
if (pllen) {
@@ -222,7 +305,7 @@ static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
ret = __stm32_usbphyc_pll_disable(usbphyc);
if (ret)
- return ret;
+ goto dec_n_pll_cons;
}
ret = stm32_usbphyc_regulators_enable(usbphyc);
@@ -241,7 +324,7 @@ reg_disable:
stm32_usbphyc_regulators_disable(usbphyc);
dec_n_pll_cons:
- usbphyc->n_pll_cons--;
+ atomic_dec(&usbphyc->n_pll_cons);
return ret;
}
@@ -261,22 +344,33 @@ static int stm32_usbphyc_phy_init(struct phy *phy)
return ret;
/* Check that PLL Lock input to PHY is High */
- writel(monsel, usbphyc->base + reg_mon);
- ret = readl_poll_timeout(usbphyc->base + reg_mon, monout,
- (monout & STM32_USBPHYC_MON_OUT_LOCKP),
- 1000);
+ writel_relaxed(monsel, usbphyc->base + reg_mon);
+ ret = readl_relaxed_poll_timeout(usbphyc->base + reg_mon, monout,
+ (monout & STM32_USBPHYC_MON_OUT_LOCKP),
+ 1000);
if (ret) {
dev_err(usbphyc->dev, "PLL Lock input to PHY is Low (val=%x)\n",
(u32)(monout & STM32_USBPHYC_MON_OUT));
goto pll_disable;
}
+ /* This mdelay seems to be necessary on some machines, since the
+ * monsel status does not seem to be accurate. On rare occasions
+ * just working with the phy after this pll check the usb
+ * peripheral (e.g. on the dwc2) run into timeout issues and
+ * leading to no functional usb. With this short mdelay this
+ * issue was not reported again.
+ */
+ mdelay(1);
+
usbphyc_phy->active = true;
return 0;
pll_disable:
- return stm32_usbphyc_pll_disable(usbphyc);
+ stm32_usbphyc_pll_disable(usbphyc);
+
+ return ret;
}
static int stm32_usbphyc_phy_exit(struct phy *phy)
@@ -289,11 +383,177 @@ static int stm32_usbphyc_phy_exit(struct phy *phy)
return stm32_usbphyc_pll_disable(usbphyc);
}
+static int stm32_usbphyc_phy_power_on(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+ if (usbphyc_phy->vbus)
+ return regulator_enable(usbphyc_phy->vbus);
+
+ return 0;
+}
+
+static int stm32_usbphyc_phy_power_off(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+ if (usbphyc_phy->vbus)
+ return regulator_disable(usbphyc_phy->vbus);
+
+ return 0;
+}
+
static const struct phy_ops stm32_usbphyc_phy_ops = {
.init = stm32_usbphyc_phy_init,
.exit = stm32_usbphyc_phy_exit,
+ .power_on = stm32_usbphyc_phy_power_on,
+ .power_off = stm32_usbphyc_phy_power_off,
};
+static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw)
+{
+ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
+
+ return stm32_usbphyc_pll_enable(usbphyc);
+}
+
+static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw)
+{
+ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
+
+ stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ return 48000000;
+}
+
+static const struct clk_ops usbphyc_clk48_ops = {
+ .enable = stm32_usbphyc_clk48_prepare,
+ .disable = stm32_usbphyc_clk48_unprepare,
+ .recalc_rate = stm32_usbphyc_clk48_recalc_rate,
+};
+
+static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc)
+{
+ struct device_node *node = usbphyc->dev->of_node;
+ struct clk_init_data init = { };
+ int ret = 0;
+
+ init.name = "ck_usbo_48m";
+ init.ops = &usbphyc_clk48_ops;
+
+ usbphyc->clk48_hw.init = &init;
+
+ ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw);
+ if (ret)
+ return ret;
+
+ return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &usbphyc->clk48_hw);
+}
+
+static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc,
+ struct device_node *np, u32 index)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = usbphyc->phys[index];
+ u32 reg = STM32_USBPHYC_TUNE(index);
+ u32 otpcomp, val;
+ int ret;
+
+ /* Backup OTP compensation code */
+ otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg));
+
+ ret = of_property_read_u32(np, "st,current-boost-microamp", &val);
+ if (ret != -EINVAL) {
+ if (!ret && (val == BOOST_1000_UA || val == BOOST_2000_UA)) {
+ val = (val == BOOST_2000_UA) ? 1 : 0;
+ usbphyc_phy->tune |= INCURREN | FIELD_PREP(INCURRINT, val);
+ } else {
+ dev_warn(usbphyc->dev, "phy%d: invalid st,current-boost-microamp\n", index);
+ }
+ }
+
+ if (!of_property_read_bool(np, "st,no-lsfs-fb-cap"))
+ usbphyc_phy->tune |= LFSCAPEN;
+
+ if (of_property_read_bool(np, "st,decrease-hs-slew-rate"))
+ usbphyc_phy->tune |= HSDRVSLEW;
+
+ ret = of_property_read_u32(np, "st,tune-hs-dc-level", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < DC_MAX) {
+ if (val == DC_MINUS_5_TO_7_MV) {/* Decreases HS driver DC level */
+ usbphyc_phy->tune |= HSDRVDCCUR;
+ } else if (val > 0) { /* Increases HS driver DC level */
+ val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0;
+ usbphyc_phy->tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val);
+ }
+ } else {
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-dc-level\n", index);
+ }
+ }
+
+ if (of_property_read_bool(np, "st,enable-fs-rftime-tuning"))
+ usbphyc_phy->tune |= FSDRVRFADJ;
+
+ if (of_property_read_bool(np, "st,enable-hs-rftime-reduction"))
+ usbphyc_phy->tune |= HSDRVRFRED;
+
+ ret = of_property_read_u32(np, "st,trim-hs-current", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < CUR_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKITRM, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-current\n", index);
+ }
+
+ ret = of_property_read_u32(np, "st,trim-hs-impedance", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < IMP_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKZTRM, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-impedance\n", index);
+ }
+
+ ret = of_property_read_u32(np, "st,tune-squelch-level", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < SQLCH_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(SQLCHCTL, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-squelch\n", index);
+ }
+
+ if (of_property_read_bool(np, "st,enable-hs-rx-gain-eq"))
+ usbphyc_phy->tune |= HDRXGNEQEN;
+
+ ret = of_property_read_u32(np, "st,tune-hs-rx-offset", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < RX_OFFSET_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSRXOFF, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-rx-offset\n", index);
+ }
+
+ if (of_property_read_bool(np, "st,no-hs-ftime-ctrl"))
+ usbphyc_phy->tune |= HSFALLPREEM;
+
+ if (!of_property_read_bool(np, "st,no-lsfs-sc"))
+ usbphyc_phy->tune |= SHTCCTCTLPROT;
+
+ if (of_property_read_bool(np, "st,enable-hs-tx-staggering"))
+ usbphyc_phy->tune |= STAGSEL;
+
+ /* Restore OTP compensation code */
+ usbphyc_phy->tune |= FIELD_PREP(OTPCOMP, otpcomp);
+
+ /*
+ * By default, if no st,xxx tuning property is used, usbphyc_phy->tune is equal to
+ * STM32_USBPHYC_TUNE reset value (LFSCAPEN | SHTCCTCTLPROT | OTPCOMP).
+ */
+ writel_relaxed(usbphyc_phy->tune, usbphyc->base + reg);
+}
+
static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
u32 utmi_switch)
{
@@ -306,7 +566,7 @@ static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
usbphyc->switch_setup = utmi_switch;
}
-static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
+static struct phy *stm32_usbphyc_of_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct stm32_usbphyc *usbphyc = dev->priv;
@@ -315,7 +575,7 @@ static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
int port = 0;
for (port = 0; port < usbphyc->nphys; port++) {
- if (phynode == usbphyc->phys[port]->phy->dev.device_node) {
+ if (phynode == usbphyc->phys[port]->phy->dev.of_node) {
usbphyc_phy = usbphyc->phys[port];
break;
}
@@ -348,10 +608,10 @@ static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
return usbphyc_phy->phy;
}
-static int stm32_usbphyc_probe(struct device_d *dev)
+static int stm32_usbphyc_probe(struct device *dev)
{
struct stm32_usbphyc *usbphyc;
- struct device_node *child, *np = dev->device_node;
+ struct device_node *child, *np = dev->of_node;
struct resource *iores;
struct phy_provider *phy_provider;
u32 pllen, version;
@@ -390,8 +650,8 @@ static int stm32_usbphyc_probe(struct device_d *dev)
* Wait for minimum width of powerdown pulse (ENABLE = Low):
* we have to ensure the PLL is disabled before phys initialization.
*/
- if (readl_poll_timeout(usbphyc->base + STM32_USBPHYC_PLL,
- pllen, !(pllen & PLLEN), 50)) {
+ if (readl_relaxed_poll_timeout(usbphyc->base + STM32_USBPHYC_PLL,
+ pllen, !(pllen & PLLEN), 50)) {
dev_warn(usbphyc->dev, "PLL not reset\n");
ret = -EPROBE_DEFER;
goto clk_disable;
@@ -407,23 +667,21 @@ static int stm32_usbphyc_probe(struct device_d *dev)
usbphyc->vdda1v1 = regulator_get(dev, "vdda1v1");
if (IS_ERR(usbphyc->vdda1v1)) {
- ret = PTR_ERR(usbphyc->vdda1v1);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get vdda1v1 supply: %d\n", ret);
+ ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v1),
+ "failed to get vdda1v1 supply\n");
goto clk_disable;
}
usbphyc->vdda1v8 = regulator_get(dev, "vdda1v8");
if (IS_ERR(usbphyc->vdda1v8)) {
- ret = PTR_ERR(usbphyc->vdda1v8);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get vdda1v8 supply: %d\n", ret);
+ ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v8),
+ "failed to get vdda1v8 supply\n");
goto clk_disable;
}
for_each_child_of_node(np, child) {
struct stm32_usbphyc_phy *usbphyc_phy;
- struct device_d *phydev;
+ struct device *phydev;
struct phy *phy;
u32 index;
@@ -435,10 +693,7 @@ static int stm32_usbphyc_probe(struct device_d *dev)
phy = phy_create(phydev, child, &stm32_usbphyc_phy_ops);
if (IS_ERR(phy)) {
- ret = PTR_ERR(phy);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to create phy%d: %d\n",
- port, ret);
+ ret = dev_errp_probe(dev, phy, "creating phy%d\n", port);
goto clk_disable;
}
@@ -447,6 +702,8 @@ static int stm32_usbphyc_probe(struct device_d *dev)
ret = of_property_read_u32(child, "reg", &index);
if (ret || index > usbphyc->nphys) {
dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+ if (!ret)
+ ret = -EINVAL;
goto clk_disable;
}
@@ -459,6 +716,17 @@ static int stm32_usbphyc_probe(struct device_d *dev)
usbphyc->phys[port]->index = index;
usbphyc->phys[port]->active = false;
+ usbphyc->phys[port]->vbus = regulator_get(&phy->dev, "vbus");
+ if (IS_ERR(usbphyc->phys[port]->vbus)) {
+ ret = PTR_ERR(usbphyc->phys[port]->vbus);
+ if (ret == -EPROBE_DEFER)
+ goto clk_disable;
+ usbphyc->phys[port]->vbus = NULL;
+ }
+
+ /* Configure phy tuning */
+ stm32_usbphyc_phy_tuning(usbphyc, child, index);
+
port++;
}
@@ -469,8 +737,14 @@ static int stm32_usbphyc_probe(struct device_d *dev)
goto clk_disable;
}
- version = readl(usbphyc->base + STM32_USBPHYC_VERSION);
- dev_info(dev, "registered rev: %lu.%lu\n",
+ ret = stm32_usbphyc_clk48_register(usbphyc);
+ if (ret) {
+ dev_err(dev, "failed to register ck_usbo_48m clock: %d\n", ret);
+ goto clk_disable;
+ }
+
+ version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
+ dev_info(dev, "registered rev:%lu.%lu\n",
FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
return 0;
@@ -486,7 +760,7 @@ release_region:
return ret;
}
-static void stm32_usbphyc_remove(struct device_d *dev)
+static void stm32_usbphyc_remove(struct device *dev)
{
struct stm32_usbphyc *usbphyc = dev->priv;
int port;
@@ -503,8 +777,9 @@ static const struct of_device_id stm32_usbphyc_of_match[] = {
{ .compatible = "st,stm32mp1-usbphyc", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_usbphyc_of_match);
-static struct driver_d stm32_usbphyc_driver = {
+static struct driver stm32_usbphyc_driver = {
.name = "stm32-usbphyc",
.probe = stm32_usbphyc_probe,
.remove = stm32_usbphyc_remove,
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index e6cee372e7..8a10190f8e 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -13,3 +13,8 @@ config PHY_ROCKCHIP_NANENG_COMBO_PHY
Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII
combo PHY with NaNeng IP block.
+config PHY_ROCKCHIP_SNPS_PCIE3
+ bool "Rockchip Snps PCIe3 PHY Driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ Enable this to support the Rockchip snps PCIe3 PHY.
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index 51ebf06aa1..1eaee4cbee 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
+obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 459ad9b355..34abbd85db 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -11,10 +11,11 @@
#include <errno.h>
#include <driver.h>
#include <malloc.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
#include <linux/phy/phy.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
#include <regulator.h>
@@ -166,7 +167,7 @@ struct rockchip_usb2phy {
struct phy_provider *provider;
struct clk *clk480m;
struct clk_hw clk480m_hw;
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
};
@@ -255,8 +256,8 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
return 0;
}
-static struct phy *rockchip_usb2phy_of_xlate(struct device_d *dev,
- struct of_phandle_args *args)
+static struct phy *rockchip_usb2phy_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
{
struct rockchip_usb2phy *rphy = dev->priv;
struct device_node *phynode = args->np;
@@ -267,7 +268,7 @@ static struct phy *rockchip_usb2phy_of_xlate(struct device_d *dev,
if (!rphy->phys[port].phy)
continue;
- if (phynode == rphy->phys[port].phy->dev.device_node) {
+ if (phynode == rphy->phys[port].phy->dev.of_node) {
p = &rphy->phys[port];
return p->phy;
}
@@ -338,7 +339,7 @@ static const struct clk_ops rockchip_usb2phy_clkout_ops = {
static int rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
{
- struct device_node *node = rphy->dev->device_node;
+ struct device_node *node = rphy->dev->of_node;
struct clk_init_data init = {};
const char *clk_name;
int ret;
@@ -379,13 +380,13 @@ err_ret:
return ret;
}
-static int rockchip_usb2phy_probe(struct device_d *dev)
+static int rockchip_usb2phy_probe(struct device *dev)
{
const struct rockchip_usb2phy_cfg *phy_cfgs;
struct rockchip_usb2phy *rphy;
u32 reg, index;
- int ret, port = 0;
- struct device_node *child, *np = dev->device_node;
+ int port = 0;
+ struct device_node *child, *np = dev->of_node;
struct resource *iores;
rphy = xzalloc(sizeof(*rphy));
@@ -396,7 +397,7 @@ static int rockchip_usb2phy_probe(struct device_d *dev)
of_device_is_compatible(np, "rockchip,rk3568-usb2phy"))
rphy->grf_base = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf");
else
- rphy->grf_base = syscon_node_to_regmap(dev->parent->device_node);
+ rphy->grf_base = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(rphy->grf_base))
return PTR_ERR(rphy->grf_base);
@@ -431,7 +432,7 @@ static int rockchip_usb2phy_probe(struct device_d *dev)
for_each_child_of_node(np, child) {
struct rockchip_usb2phy_phy *p;
struct phy *phy;
- struct device_d *phydev;
+ struct device *phydev;
if (!strcmp(child->name, "host-port")) {
port = USB2PHY_PORT_OTG;
@@ -452,13 +453,8 @@ static int rockchip_usb2phy_probe(struct device_d *dev)
of_platform_device_dummy_drv(phydev);
phy = phy_create(phydev, child, &rockchip_usb2phy_ops);
- if (IS_ERR(phy)) {
- ret = PTR_ERR(phy);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to create phy%d: %d\n",
- port, ret);
- return ret;
- }
+ if (IS_ERR(phy))
+ return dev_errp_probe(dev, phy, "creating phy%d\n", port);
p = xzalloc(sizeof(*p));
@@ -987,8 +983,9 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = {
{ .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
{ }
};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
-static struct driver_d rockchip_usb2phy_driver = {
+static struct driver rockchip_usb2phy_driver = {
.probe = rockchip_usb2phy_probe,
.name = "rockchip-usb2phy",
.of_compatible = rockchip_usb2phy_dt_match,
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
index 2d86d86334..b864ecb76e 100644
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -12,11 +12,12 @@
#include <errno.h>
#include <driver.h>
#include <malloc.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
#include <linux/phy/phy.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/reset.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
#include <linux/iopoll.h>
#include <dt-bindings/phy/phy.h>
@@ -41,6 +42,7 @@ struct rockchip_combphy_grfcfg {
struct combphy_reg pipe_rxterm_set;
struct combphy_reg pipe_txelec_set;
struct combphy_reg pipe_txcomp_set;
+ struct combphy_reg pipe_clk_24m;
struct combphy_reg pipe_clk_25m;
struct combphy_reg pipe_clk_100m;
struct combphy_reg pipe_phymode_sel;
@@ -61,6 +63,7 @@ struct rockchip_combphy_grfcfg {
struct combphy_reg con2_for_sata;
struct combphy_reg con3_for_sata;
struct combphy_reg pipe_con0_for_sata;
+ struct combphy_reg pipe_con1_for_sata;
struct combphy_reg pipe_sgmii_mac_sel;
struct combphy_reg pipe_xpcs_phy_ready;
struct combphy_reg u3otg0_port_en;
@@ -71,6 +74,7 @@ struct rockchip_combphy_cfg {
const int num_clks;
const struct clk_bulk_data *clks;
const struct rockchip_combphy_grfcfg *grfcfg;
+ bool force_det_out; /* Tx detect Rx errata */
int (*combphy_cfg)(struct rockchip_combphy_priv *priv);
};
@@ -79,7 +83,7 @@ struct rockchip_combphy_priv {
void __iomem *mmio;
int num_clks;
struct clk_bulk_data *clks;
- struct device_d *dev;
+ struct device *dev;
struct regmap *pipe_grf;
struct regmap *phy_grf;
struct phy *phy;
@@ -265,7 +269,7 @@ static const struct phy_ops rochchip_combphy_ops = {
.exit = rockchip_combphy_exit,
};
-static struct phy *rockchip_combphy_xlate(struct device_d *dev,
+static struct phy *rockchip_combphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct rockchip_combphy_priv *priv = dev->priv;
@@ -284,10 +288,10 @@ static struct phy *rockchip_combphy_xlate(struct device_d *dev,
return priv->phy;
}
-static int rockchip_combphy_parse_dt(struct device_d *dev,
+static int rockchip_combphy_parse_dt(struct device *dev,
struct rockchip_combphy_priv *priv)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct rockchip_combphy_cfg *phy_cfg = priv->cfg;
int ret, mac_id;
@@ -317,19 +321,13 @@ static int rockchip_combphy_parse_dt(struct device_d *dev,
true);
priv->phy_rst = reset_control_get(dev, NULL);
- if (IS_ERR(priv->phy_rst)) {
- ret = PTR_ERR(priv->phy_rst);
-
- if (ret != -EPROBE_DEFER)
- dev_warn(dev, "failed to get phy reset\n");
-
- return ret;
- }
+ if (IS_ERR(priv->phy_rst))
+ return dev_errp_probe(dev, priv->phy_rst, "getting phy reset\n");
return reset_control_assert(priv->phy_rst);
}
-static int rockchip_combphy_probe(struct device_d *dev)
+static int rockchip_combphy_probe(struct device *dev)
{
struct phy_provider *phy_provider;
struct rockchip_combphy_priv *priv;
@@ -386,7 +384,7 @@ static int rockchip_combphy_probe(struct device_d *dev)
static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
{
- struct device_node *np = priv->dev->device_node;
+ struct device_node *np = priv->dev->of_node;
const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
struct clk *refclk = NULL;
unsigned long rate;
@@ -580,15 +578,291 @@ static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = {
.combphy_cfg = rk3568_combphy_cfg,
};
+static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ struct clk *refclk = NULL;
+ unsigned long rate;
+ int i;
+ u32 val;
+
+ /* Configure PHY reference clock frequency */
+ for (i = 0; i < priv->num_clks; i++) {
+ if (!strncmp(priv->clks[i].id, "ref", 6)) {
+ refclk = priv->clks[i].clk;
+ break;
+ }
+ }
+
+ if (!refclk) {
+ dev_err(priv->dev, "No refclk found\n");
+ return -EINVAL;
+ }
+
+ switch (priv->mode) {
+ case PHY_TYPE_PCIE:
+ /* Set SSC downward spread spectrum */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(5, 4);
+ val |= 0x01 << 4;
+ writel(val, priv->mmio + 0x7c);
+
+ param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
+ break;
+ case PHY_TYPE_USB3:
+ /* Set SSC downward spread spectrum */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(5, 4);
+ val |= 0x01 << 4;
+ writel(val, priv->mmio + 0x7c);
+
+ /* Enable adaptive CTLE for USB3.0 Rx */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(0, 0);
+ val |= 0x01;
+ writel(val, priv->mmio + (0x0e << 2));
+
+ /* Set PLL KVCO fine tuning signals */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~(0x7 << 2);
+ val |= 0x2 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Set PLL LPF R1 to su_trim[10:7]=1001 */
+ writel(0x4, priv->mmio + (0xb << 2));
+
+ /* Set PLL input clock divider 1/2 */
+ val = readl(priv->mmio + (0x5 << 2));
+ val &= ~(0x3 << 6);
+ val |= 0x1 << 6;
+ writel(val, priv->mmio + (0x5 << 2));
+
+ /* Set PLL loop divider */
+ writel(0x32, priv->mmio + (0x11 << 2));
+
+ /* Set PLL KVCO to min and set PLL charge pump current to max */
+ writel(0xf0, priv->mmio + (0xa << 2));
+
+ /* Set Rx squelch input filler bandwidth */
+ writel(0x0d, priv->mmio + (0x14 << 2));
+
+ param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
+ param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
+ param_write(priv->phy_grf, &cfg->usb_mode_set, true);
+ break;
+ case PHY_TYPE_SATA:
+ /* Enable adaptive CTLE for SATA Rx */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(0, 0);
+ val |= 0x01;
+ writel(val, priv->mmio + (0x0e << 2));
+ /* Set tx_rterm = 50 ohm and rx_rterm = 43.5 ohm */
+ writel(0x8F, priv->mmio + (0x06 << 2));
+
+ param_write(priv->phy_grf, &cfg->con0_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con1_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con2_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con3_for_sata, true);
+ param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true);
+ param_write(priv->pipe_grf, &cfg->pipe_con1_for_sata, true);
+ break;
+ case PHY_TYPE_SGMII:
+ case PHY_TYPE_QSGMII:
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ rate = clk_get_rate(refclk);
+
+ switch (rate) {
+ case 24000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_24m, true);
+ if (priv->mode == PHY_TYPE_USB3 || priv->mode == PHY_TYPE_SATA) {
+ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(7, 6);
+ val |= 0x01 << 6;
+ writel(val, priv->mmio + (0x0e << 2));
+
+ val = readl(priv->mmio + (0x0f << 2));
+ val &= ~GENMASK(7, 0);
+ val |= 0x5f;
+ writel(val, priv->mmio + (0x0f << 2));
+ } else if (priv->mode == PHY_TYPE_PCIE) {
+ /* PLL KVCO tuning fine */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~GENMASK(4, 2);
+ val |= 0x4 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Set up rx_trim */
+ val = 0x0;
+ writel(val, priv->mmio + (0x1b << 2));
+
+ /* Set up su_trim: T0_1 */
+ val = 0x90;
+ writel(val, priv->mmio + (0xa << 2));
+ val = 0x02;
+ writel(val, priv->mmio + (0xb << 2));
+ val = 0x57;
+ writel(val, priv->mmio + (0xd << 2));
+
+ val = 0x5f;
+ writel(val, priv->mmio + (0xf << 2));
+ }
+ break;
+ case 25000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
+ break;
+ case 100000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
+ if (priv->mode == PHY_TYPE_PCIE) {
+ /* gate_tx_pck_sel length select work for L1SS */
+ val = 0xc0;
+ writel(val, priv->mmio + 0x74);
+
+ /* PLL KVCO tuning fine */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~GENMASK(4, 2);
+ val |= 0x4 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */
+ val = 0x4c;
+ writel(val, priv->mmio + (0x1b << 2));
+
+ /* Set up su_trim: T3_P1 650mv */
+ val = 0x90;
+ writel(val, priv->mmio + (0xa << 2));
+ val = 0x43;
+ writel(val, priv->mmio + (0xb << 2));
+ val = 0x88;
+ writel(val, priv->mmio + (0xc << 2));
+ val = 0x56;
+ writel(val, priv->mmio + (0xd << 2));
+ } else if (priv->mode == PHY_TYPE_SATA) {
+ /* downward spread spectrum +500ppm */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(7, 4);
+ val |= 0x50;
+ writel(val, priv->mmio + (0x1f << 2));
+
+ /* ssc ppm adjust to 3500ppm */
+ val = readl(priv->mmio + (0x9 << 2));
+ val &= ~GENMASK(3, 0);
+ val |= 0x7;
+ writel(val, priv->mmio + (0x9 << 2));
+ }
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(priv->dev->of_node, "rockchip,ext-refclk")) {
+ param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
+ if (priv->mode == PHY_TYPE_PCIE && rate == 100000000) {
+ val = 0x10;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ val = 0x0c;
+ writel(val, priv->mmio + (0x1b << 2));
+
+ /* Set up su_trim: T3_P1 650mv */
+ val = 0x90;
+ writel(val, priv->mmio + (0xa << 2));
+ val = 0x43;
+ writel(val, priv->mmio + (0xb << 2));
+ val = 0x88;
+ writel(val, priv->mmio + (0xc << 2));
+ val = 0x56;
+ writel(val, priv->mmio + (0xd << 2));
+ }
+ }
+
+ if (of_property_read_bool(priv->dev->of_node, "rockchip,enable-ssc")) {
+ val = readl(priv->mmio + (0x7 << 2));
+ val |= BIT(4);
+ writel(val, priv->mmio + (0x7 << 2));
+
+ if (priv->mode == PHY_TYPE_PCIE && rate == 24000000) {
+ /* Xin24M T0_1 650mV */
+ writel(0x00, priv->mmio + (0x10 << 2));
+ writel(0x32, priv->mmio + (0x11 << 2));
+ writel(0x00, priv->mmio + (0x1b << 2));
+ writel(0x90, priv->mmio + (0x0a << 2));
+ writel(0x02, priv->mmio + (0x0b << 2));
+ writel(0x08, priv->mmio + (0x0c << 2));
+ writel(0x57, priv->mmio + (0x0d << 2));
+ writel(0x40, priv->mmio + (0x0e << 2));
+ writel(0x5f, priv->mmio + (0x0f << 2));
+ writel(0x10, priv->mmio + (0x20 << 2));
+ }
+ }
+
+ return 0;
+}
+
+static const struct rockchip_combphy_grfcfg rk3588_combphy_grfcfgs = {
+ /* pipe-phy-grf */
+ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 },
+ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 },
+ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 },
+ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 },
+ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 },
+ .pipe_clk_24m = { 0x0004, 14, 13, 0x00, 0x00 },
+ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 },
+ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 },
+ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 },
+ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 },
+ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 },
+ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 },
+ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 },
+ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 },
+ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 },
+ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 },
+ .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0129 },
+ .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c1 },
+ .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x0407 },
+ /* pipe-grf */
+ .pipe_con0_for_sata = { 0x0000, 11, 5, 0x00, 0x22 },
+ .pipe_con1_for_sata = { 0x0004, 2, 0, 0x00, 0x2 },
+};
+
+
+static const struct clk_bulk_data rk3588_clks[] = {
+ { .id = "ref" },
+ { .id = "apb" },
+ { .id = "pipe" },
+};
+
+static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = {
+ .num_clks = ARRAY_SIZE(rk3588_clks),
+ .clks = rk3588_clks,
+ .grfcfg = &rk3588_combphy_grfcfgs,
+ .combphy_cfg = rk3588_combphy_cfg,
+ .force_det_out = true,
+};
+
static const struct of_device_id rockchip_combphy_of_match[] = {
{
.compatible = "rockchip,rk3568-naneng-combphy",
.data = &rk3568_combphy_cfgs,
+ }, {
+ .compatible = "rockchip,rk3588-naneng-combphy",
+ .data = &rk3588_combphy_cfgs,
},
{ },
};
+MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match);
-static struct driver_d rockchip_combphy_driver = {
+static struct driver rockchip_combphy_driver = {
.probe = rockchip_combphy_probe,
.name = "naneng-combphy",
.of_compatible = rockchip_combphy_of_match,
diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c
new file mode 100644
index 0000000000..7f39b261ca
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip PCIE3.0 phy driver
+ *
+ * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#include <of.h>
+#include <common.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+//#include <linux/phy/pcie.h>
+#include <mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* Register for RK3568 */
+#define GRF_PCIE30PHY_CON1 0x4
+#define GRF_PCIE30PHY_CON6 0x18
+#define GRF_PCIE30PHY_CON9 0x24
+#define GRF_PCIE30PHY_DA_OCM (BIT(15) | BIT(31))
+#define GRF_PCIE30PHY_STATUS0 0x80
+#define GRF_PCIE30PHY_WR_EN (0xf << 16)
+#define SRAM_INIT_DONE(reg) (reg & BIT(14))
+
+#define RK3568_BIFURCATION_LANE_0_1 BIT(0)
+
+/* Register for RK3588 */
+#define PHP_GRF_PCIESEL_CON 0x100
+#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0
+#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904
+#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04
+#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0))
+
+#define RK3588_BIFURCATION_LANE_0_1 BIT(0)
+#define RK3588_BIFURCATION_LANE_2_3 BIT(1)
+#define RK3588_LANE_AGGREGATION BIT(2)
+
+struct rockchip_p3phy_ops;
+
+struct rockchip_p3phy_priv {
+ const struct rockchip_p3phy_ops *ops;
+ void __iomem *mmio;
+ /* mode: RC, EP */
+ int mode;
+ /* pcie30_phymode: Aggregation, Bifurcation */
+ int pcie30_phymode;
+ struct regmap *phy_grf;
+ struct regmap *pipe_grf;
+ struct reset_control *p30phy;
+ struct phy *phy;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ int num_lanes;
+ u32 lanes[4];
+};
+
+struct rockchip_p3phy_ops {
+ int (*phy_init)(struct rockchip_p3phy_priv *priv);
+};
+
+static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ return 0;
+}
+
+static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv)
+{
+ struct phy *phy = priv->phy;
+ bool bifurcation = false;
+ int ret;
+ u32 reg;
+
+ /* Deassert PCIe PMA output clamp mode */
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM);
+
+ for (int i = 0; i < priv->num_lanes; i++) {
+ dev_info(&phy->dev, "lane number %d, val %d\n", i, priv->lanes[i]);
+ if (priv->lanes[i] > 1)
+ bifurcation = true;
+ }
+
+ /* Set bifurcation if needed, and it doesn't care RC/EP */
+ if (bifurcation) {
+ dev_info(&phy->dev, "bifurcation enabled\n");
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
+ GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1);
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1,
+ GRF_PCIE30PHY_DA_OCM);
+ } else {
+ dev_dbg(&phy->dev, "bifurcation disabled\n");
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
+ GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1);
+ }
+
+ reset_control_deassert(priv->p30phy);
+
+ ret = regmap_read_poll_timeout(priv->phy_grf,
+ GRF_PCIE30PHY_STATUS0,
+ reg, SRAM_INIT_DONE(reg),
+ 500);
+ if (ret)
+ dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n",
+ __func__, reg);
+ return ret;
+}
+
+static const struct rockchip_p3phy_ops rk3568_ops = {
+ .phy_init = rockchip_p3phy_rk3568_init,
+};
+
+static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv)
+{
+ u32 reg = 0;
+ u8 mode = 0;
+ int ret;
+
+ /* Deassert PCIe PMA output clamp mode */
+ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24));
+
+ /* Set bifurcation if needed */
+ for (int i = 0; i < priv->num_lanes; i++) {
+ if (!priv->lanes[i])
+ mode |= (BIT(i) << 3);
+
+ if (priv->lanes[i] > 1)
+ mode |= (BIT(i) >> 1);
+ }
+
+ if (!mode)
+ reg = RK3588_LANE_AGGREGATION;
+ else {
+ if (mode & (BIT(0) | BIT(1)))
+ reg |= RK3588_BIFURCATION_LANE_0_1;
+
+ if (mode & (BIT(2) | BIT(3)))
+ reg |= RK3588_BIFURCATION_LANE_2_3;
+ }
+
+ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, (0x7<<16) | reg);
+
+ /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */
+ if (!IS_ERR(priv->pipe_grf)) {
+ reg = (mode & (BIT(6) | BIT(7))) >> 6;
+ if (reg)
+ regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON,
+ (reg << 16) | reg);
+ }
+
+ reset_control_deassert(priv->p30phy);
+
+ ret = regmap_read_poll_timeout(priv->phy_grf,
+ RK3588_PCIE3PHY_GRF_PHY0_STATUS1,
+ reg, RK3588_SRAM_INIT_DONE(reg),
+ 500);
+ ret |= regmap_read_poll_timeout(priv->phy_grf,
+ RK3588_PCIE3PHY_GRF_PHY1_STATUS1,
+ reg, RK3588_SRAM_INIT_DONE(reg),
+ 500);
+ if (ret)
+ dev_err(&priv->phy->dev, "lock failed 0x%x, check input refclk and power supply\n",
+ reg);
+ return ret;
+}
+
+static const struct rockchip_p3phy_ops rk3588_ops = {
+ .phy_init = rockchip_p3phy_rk3588_init,
+};
+
+static int rochchip_p3phy_init(struct phy *phy)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(&priv->phy->dev, "failed to enable PCIe bulk clks %d\n", ret);
+ return ret;
+ }
+
+ reset_control_assert(priv->p30phy);
+ udelay(1);
+
+ if (priv->ops->phy_init) {
+ ret = priv->ops->phy_init(priv);
+ if (ret)
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ }
+
+ return ret;
+}
+
+static int rochchip_p3phy_exit(struct phy *phy)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ reset_control_assert(priv->p30phy);
+ return 0;
+}
+
+static const struct phy_ops rochchip_p3phy_ops = {
+ .init = rochchip_p3phy_init,
+ .exit = rochchip_p3phy_exit,
+ .set_mode = rockchip_p3phy_set_mode,
+};
+
+static struct phy *rockchip_p3phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct rockchip_p3phy_priv *priv = dev->priv;
+
+ return priv->phy;
+}
+
+static int rockchip_p3phy_probe(struct device *dev)
+{
+ struct phy_provider *phy_provider;
+ struct rockchip_p3phy_priv *priv;
+ struct device_node *np = dev->of_node;
+ struct resource *res;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ return ret;
+ }
+
+ priv->mmio = IOMEM(res->start);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->ops = device_get_match_data(dev);
+ if (!priv->ops) {
+ dev_err(dev, "no of match data provided\n");
+ return -EINVAL;
+ }
+
+ priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf");
+ if (IS_ERR(priv->phy_grf)) {
+ dev_err(dev, "failed to find rockchip,phy_grf regmap\n");
+ return PTR_ERR(priv->phy_grf);
+ }
+
+ if (of_device_is_compatible(np, "rockchip,rk3588-pcie3-phy")) {
+ priv->pipe_grf =
+ syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,pipe-grf");
+ if (IS_ERR(priv->pipe_grf))
+ dev_info(dev, "failed to find rockchip,pipe_grf regmap\n");
+ } else {
+ priv->pipe_grf = NULL;
+ }
+
+ priv->num_lanes = of_property_read_variable_u32_array(dev->of_node, "data-lanes",
+ priv->lanes, 2,
+ ARRAY_SIZE(priv->lanes));
+
+ /* if no data-lanes assume aggregation */
+ if (priv->num_lanes == -EINVAL) {
+ dev_dbg(dev, "no data-lanes property found\n");
+ priv->num_lanes = 1;
+ priv->lanes[0] = 1;
+ } else if (priv->num_lanes < 0) {
+ dev_err(dev, "failed to read data-lanes property %d\n", priv->num_lanes);
+ return priv->num_lanes;
+ }
+
+ priv->phy = phy_create(dev, NULL, &rochchip_p3phy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ priv->p30phy = reset_control_get_optional(dev, "phy");
+ if (IS_ERR(priv->p30phy)) {
+ return dev_err_probe(dev, PTR_ERR(priv->p30phy),
+ "failed to get phy reset control\n");
+ }
+ if (!priv->p30phy)
+ dev_info(dev, "no phy reset control specified\n");
+
+ priv->num_clks = clk_bulk_get_all(dev, &priv->clks);
+ if (priv->num_clks < 1)
+ return -ENODEV;
+
+ dev->priv = priv;
+ phy_set_drvdata(priv->phy, priv);
+ phy_provider = of_phy_provider_register(dev, rockchip_p3phy_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_p3phy_of_match[] = {
+ { .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops },
+ { .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match);
+
+static struct driver rockchip_p3phy_driver = {
+ .probe = rockchip_p3phy_probe,
+ .name = "rockchip-snps-pcie3-phy",
+ .of_compatible = rockchip_p3phy_of_match,
+};
+coredevice_platform_driver(rockchip_p3phy_driver);
+
+MODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/usb-nop-xceiv.c b/drivers/phy/usb-nop-xceiv.c
index 06a1ff9791..9a0acf9e7f 100644
--- a/drivers/phy/usb-nop-xceiv.c
+++ b/drivers/phy/usb-nop-xceiv.c
@@ -9,7 +9,7 @@
#include <errno.h>
#include <driver.h>
#include <malloc.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
#include <linux/phy/phy.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -24,7 +24,7 @@ struct nop_usbphy {
int reset;
};
-static struct phy *nop_usbphy_xlate(struct device_d *dev,
+static struct phy *nop_usbphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct nop_usbphy *nopphy = dev->priv;
@@ -64,7 +64,7 @@ static const struct phy_ops nop_phy_ops = {
.init = nop_usbphy_init,
};
-static int nop_usbphy_probe(struct device_d *dev)
+static int nop_usbphy_probe(struct device *dev)
{
int ret;
struct nop_usbphy *nopphy;
@@ -80,7 +80,7 @@ static int nop_usbphy_probe(struct device_d *dev)
if (IS_ERR(nopphy->clk))
nopphy->clk = NULL;
- nopphy->reset = of_get_named_gpio_flags(dev->device_node,
+ nopphy->reset = of_get_named_gpio_flags(dev->of_node,
"reset-gpios", 0, &of_flags);
if (gpio_is_valid(nopphy->reset)) {
/* assert reset */
@@ -130,8 +130,9 @@ static __maybe_unused struct of_device_id nop_usbphy_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, nop_usbphy_dt_ids);
-static struct driver_d nop_usbphy_driver = {
+static struct driver nop_usbphy_driver = {
.name = "usb-nop-xceiv",
.probe = nop_usbphy_probe,
.of_compatible = DRV_OF_COMPAT(nop_usbphy_dt_ids),
diff --git a/drivers/pinctrl/imx-iomux-v1.c b/drivers/pinctrl/imx-iomux-v1.c
index c82d716e1c..a0878fa9eb 100644
--- a/drivers/pinctrl/imx-iomux-v1.c
+++ b/drivers/pinctrl/imx-iomux-v1.c
@@ -4,7 +4,7 @@
#include <init.h>
#include <malloc.h>
#include <pinctrl.h>
-#include <mach/iomux-v1.h>
+#include <mach/imx/iomux-v1.h>
#include <linux/err.h>
/*
@@ -206,7 +206,7 @@ static int imx_iomux_v1_set_state(struct pinctrl_device *pdev, struct device_nod
const __be32 *list;
int npins, size, i;
- dev_dbg(iomux->pinctrl.dev, "set state: %s\n", np->full_name);
+ dev_dbg(iomux->pinctrl.dev, "set state: %pOF\n", np);
list = of_get_property(np, "fsl,pins", &size);
if (!list)
@@ -225,8 +225,8 @@ static int imx_iomux_v1_set_state(struct pinctrl_device *pdev, struct device_nod
unsigned int gpio_iconfa = MX1_MUX_ICONFA(mux);
unsigned int gpio_iconfb = MX1_MUX_ICONFB(mux);
- dev_dbg(pdev->dev, "%s, pin 0x%x, function %d, gpio %d, direction %d, oconf %d, iconfa %d, iconfb %d\n",
- np->full_name, pin_id, afunction, gpio_in_use,
+ dev_dbg(pdev->dev, "%pOF, pin 0x%x, function %d, gpio %d, direction %d, oconf %d, iconfa %d, iconfb %d\n",
+ np, pin_id, afunction, gpio_in_use,
direction, gpio_oconf, gpio_iconfa,
gpio_iconfb);
@@ -251,7 +251,7 @@ static struct pinctrl_ops imx_iomux_v1_ops = {
.set_state = imx_iomux_v1_set_state,
};
-static int imx_pinctrl_dt(struct device_d *dev, void __iomem *base)
+static int imx_pinctrl_dt(struct device *dev, void __iomem *base)
{
struct imx_iomux_v1 *iomux;
int ret;
@@ -270,7 +270,7 @@ static int imx_pinctrl_dt(struct device_d *dev, void __iomem *base)
return ret;
}
-static int imx_iomux_v1_probe(struct device_d *dev)
+static int imx_iomux_v1_probe(struct device *dev)
{
int ret = 0;
void __iomem *base;
@@ -279,9 +279,9 @@ static int imx_iomux_v1_probe(struct device_d *dev)
if (IS_ERR(base))
return PTR_ERR(base);
- ret = of_platform_populate(dev->device_node, NULL, NULL);
+ ret = of_platform_populate(dev->of_node, NULL, NULL);
- if (IS_ENABLED(CONFIG_PINCTRL) && dev->device_node)
+ if (IS_ENABLED(CONFIG_PINCTRL) && dev->of_node)
ret = imx_pinctrl_dt(dev, base);
return ret;
@@ -296,8 +296,9 @@ static __maybe_unused struct of_device_id imx_iomux_v1_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_iomux_v1_dt_ids);
-static struct driver_d imx_iomux_v1_driver = {
+static struct driver imx_iomux_v1_driver = {
.name = "imx-iomuxv1",
.probe = imx_iomux_v1_probe,
.of_compatible = DRV_OF_COMPAT(imx_iomux_v1_dt_ids),
diff --git a/drivers/pinctrl/imx-iomux-v2.c b/drivers/pinctrl/imx-iomux-v2.c
index 35772d13a8..4757587e37 100644
--- a/drivers/pinctrl/imx-iomux-v2.c
+++ b/drivers/pinctrl/imx-iomux-v2.c
@@ -7,7 +7,7 @@
#include <io.h>
#include <init.h>
#include <linux/err.h>
-#include <mach/iomux-mx31.h>
+#include <mach/imx/iomux-mx31.h>
/*
* IOMUX register (base) addresses
@@ -105,7 +105,7 @@ int imx_iomux_setup_multiple_pins(const unsigned int *pin_list, unsigned count)
return 0;
}
-static int imx_iomux_probe(struct device_d *dev)
+static int imx_iomux_probe(struct device *dev)
{
struct resource *iores;
iores = dev_request_mem_resource(dev, 0);
@@ -123,6 +123,7 @@ static __maybe_unused struct of_device_id imx_iomux_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_iomux_dt_ids);
static struct platform_device_id imx_iomux_ids[] = {
{
@@ -132,7 +133,7 @@ static struct platform_device_id imx_iomux_ids[] = {
},
};
-static struct driver_d imx_iomux_driver = {
+static struct driver imx_iomux_driver = {
.name = "imx-iomuxv2",
.probe = imx_iomux_probe,
.of_compatible = DRV_OF_COMPAT(imx_iomux_dt_ids),
diff --git a/drivers/pinctrl/imx-iomux-v3.c b/drivers/pinctrl/imx-iomux-v3.c
index ecadff5e82..673674c1dc 100644
--- a/drivers/pinctrl/imx-iomux-v3.c
+++ b/drivers/pinctrl/imx-iomux-v3.c
@@ -11,8 +11,8 @@
#include <of.h>
#include <pinctrl.h>
#include <malloc.h>
-#include <mach/iomux-v3.h>
-#include <mach/generic.h>
+#include <mach/imx/iomux-v3.h>
+#include <mach/imx/generic.h>
struct imx_iomux_v3 {
void __iomem *base;
@@ -77,7 +77,7 @@ static int imx_iomux_v3_set_state(struct pinctrl_device *pdev, struct device_nod
const char *name;
u32 share_conf_val = 0;
- dev_dbg(iomux->pinctrl.dev, "set state: %s\n", np->full_name);
+ dev_dbg(iomux->pinctrl.dev, "set state: %pOF\n", np);
if (share_conf) {
u32 drive_strength, slew_rate;
@@ -120,8 +120,7 @@ static int imx_iomux_v3_set_state(struct pinctrl_device *pdev, struct device_nod
return -EINVAL;
if (!size || size % fsl_pin_size) {
- dev_err(iomux->pinctrl.dev, "Invalid fsl,pins property in %s\n",
- np->full_name);
+ dev_err(iomux->pinctrl.dev, "Invalid fsl,pins property in %pOF\n", np);
return -EINVAL;
}
@@ -156,7 +155,7 @@ static struct pinctrl_ops imx_iomux_v3_ops = {
.set_state = imx_iomux_v3_set_state,
};
-static int imx_pinctrl_dt(struct device_d *dev, void __iomem *base)
+static int imx_pinctrl_dt(struct device *dev, void __iomem *base)
{
struct imx_iomux_v3 *iomux;
const struct imx_iomux_v3_data *drvdata;
@@ -179,7 +178,7 @@ static int imx_pinctrl_dt(struct device_d *dev, void __iomem *base)
return ret;
}
-static int imx_iomux_v3_probe(struct device_d *dev)
+static int imx_iomux_v3_probe(struct device *dev)
{
void __iomem *base;
struct resource *iores;
@@ -198,7 +197,7 @@ static int imx_iomux_v3_probe(struct device_d *dev)
*/
iomuxv3_base = base;
- if (IS_ENABLED(CONFIG_PINCTRL) && dev->device_node)
+ if (IS_ENABLED(CONFIG_PINCTRL) && dev->of_node)
ret = imx_pinctrl_dt(dev, base);
return ret;
@@ -248,11 +247,14 @@ static __maybe_unused struct of_device_id imx_iomux_v3_dt_ids[] = {
}, {
.compatible = "fsl,imx8mq-iomuxc",
}, {
+ .compatible = "fsl,imx93-iomuxc",
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_iomux_v3_dt_ids);
-static struct driver_d imx_iomux_v3_driver = {
+static struct driver imx_iomux_v3_driver = {
.name = "imx-iomuxv3",
.probe = imx_iomux_v3_probe,
.of_compatible = DRV_OF_COMPAT(imx_iomux_v3_dt_ids),
diff --git a/drivers/pinctrl/mvebu/armada-370.c b/drivers/pinctrl/mvebu/armada-370.c
index 8b574b89b9..116adb2a70 100644
--- a/drivers/pinctrl/mvebu/armada-370.c
+++ b/drivers/pinctrl/mvebu/armada-370.c
@@ -384,12 +384,13 @@ static struct of_device_id armada_370_pinctrl_of_match[] = {
},
{ },
};
+MODULE_DEVICE_TABLE(of, armada_370_pinctrl_of_match);
-static int armada_370_pinctrl_probe(struct device_d *dev)
+static int armada_370_pinctrl_probe(struct device *dev)
{
struct resource *iores;
const struct of_device_id *match =
- of_match_node(armada_370_pinctrl_of_match, dev->device_node);
+ of_match_node(armada_370_pinctrl_of_match, dev->of_node);
struct mvebu_pinctrl_soc_info *soc =
(struct mvebu_pinctrl_soc_info *)match->data;
@@ -401,7 +402,7 @@ static int armada_370_pinctrl_probe(struct device_d *dev)
return mvebu_pinctrl_probe(dev, soc);
}
-static struct driver_d armada_370_pinctrl_driver = {
+static struct driver armada_370_pinctrl_driver = {
.name = "pinctrl-armada-370",
.probe = armada_370_pinctrl_probe,
.of_compatible = armada_370_pinctrl_of_match,
diff --git a/drivers/pinctrl/mvebu/armada-xp.c b/drivers/pinctrl/mvebu/armada-xp.c
index 31286c59ee..3a4b0504c8 100644
--- a/drivers/pinctrl/mvebu/armada-xp.c
+++ b/drivers/pinctrl/mvebu/armada-xp.c
@@ -366,12 +366,13 @@ static struct of_device_id armada_xp_pinctrl_of_match[] = {
{ .compatible = "marvell,mv78460-pinctrl", .data = (void *)V_MV78460, },
{ },
};
+MODULE_DEVICE_TABLE(of, armada_xp_pinctrl_of_match);
-static int armada_xp_pinctrl_probe(struct device_d *dev)
+static int armada_xp_pinctrl_probe(struct device *dev)
{
struct resource *iores;
const struct of_device_id *match =
- of_match_node(armada_xp_pinctrl_of_match, dev->device_node);
+ of_match_node(armada_xp_pinctrl_of_match, dev->of_node);
struct mvebu_pinctrl_soc_info *soc = &armada_xp_pinctrl_info;
iores = dev_request_mem_resource(dev, 0);
@@ -391,7 +392,7 @@ static int armada_xp_pinctrl_probe(struct device_d *dev)
return mvebu_pinctrl_probe(dev, soc);
}
-static struct driver_d armada_xp_pinctrl_driver = {
+static struct driver armada_xp_pinctrl_driver = {
.name = "pinctrl-armada-xp",
.probe = armada_xp_pinctrl_probe,
.of_compatible = armada_xp_pinctrl_of_match,
diff --git a/drivers/pinctrl/mvebu/common.c b/drivers/pinctrl/mvebu/common.c
index 265e57aaa3..e41ab0a40d 100644
--- a/drivers/pinctrl/mvebu/common.c
+++ b/drivers/pinctrl/mvebu/common.c
@@ -57,8 +57,7 @@ static int mvebu_pinctrl_set_state(struct pinctrl_device *pdev,
ret = of_property_read_string(np, "marvell,function", &function);
if (ret) {
- dev_err(pdev->dev, "missing marvell,function in node %s\n",
- np->full_name);
+ dev_err(pdev->dev, "missing marvell,function in node %pOF\n", np);
return -EINVAL;
}
@@ -93,7 +92,7 @@ static struct pinctrl_ops mvebu_pinctrl_ops = {
.set_state = mvebu_pinctrl_set_state,
};
-int mvebu_pinctrl_probe(struct device_d *dev,
+int mvebu_pinctrl_probe(struct device *dev,
struct mvebu_pinctrl_soc_info *soc)
{
struct mvebu_pinctrl *pctl;
diff --git a/drivers/pinctrl/mvebu/common.h b/drivers/pinctrl/mvebu/common.h
index 079a47679e..203b0530d8 100644
--- a/drivers/pinctrl/mvebu/common.h
+++ b/drivers/pinctrl/mvebu/common.h
@@ -136,7 +136,7 @@ static inline int default_mpp_ctrl_set(void __iomem *base, unsigned int pid,
return 0;
}
-int mvebu_pinctrl_probe(struct device_d *dev,
+int mvebu_pinctrl_probe(struct device *dev,
struct mvebu_pinctrl_soc_info *soc);
#endif
diff --git a/drivers/pinctrl/mvebu/dove.c b/drivers/pinctrl/mvebu/dove.c
index 49553f1b28..964ce22ef8 100644
--- a/drivers/pinctrl/mvebu/dove.c
+++ b/drivers/pinctrl/mvebu/dove.c
@@ -689,12 +689,13 @@ static struct of_device_id dove_pinctrl_of_match[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, dove_pinctrl_of_match);
-static int dove_pinctrl_probe(struct device_d *dev)
+static int dove_pinctrl_probe(struct device *dev)
{
struct resource *iores;
const struct of_device_id *match =
- of_match_node(dove_pinctrl_of_match, dev->device_node);
+ of_match_node(dove_pinctrl_of_match, dev->of_node);
struct mvebu_pinctrl_soc_info *soc =
(struct mvebu_pinctrl_soc_info *)match->data;
struct device_node *np;
@@ -728,7 +729,7 @@ static int dove_pinctrl_probe(struct device_d *dev)
return mvebu_pinctrl_probe(dev, soc);
}
-static struct driver_d dove_pinctrl_driver = {
+static struct driver dove_pinctrl_driver = {
.name = "pinctrl-dove",
.probe = dove_pinctrl_probe,
.of_compatible = dove_pinctrl_of_match,
diff --git a/drivers/pinctrl/mvebu/kirkwood.c b/drivers/pinctrl/mvebu/kirkwood.c
index 1df51e443d..6c9e63575e 100644
--- a/drivers/pinctrl/mvebu/kirkwood.c
+++ b/drivers/pinctrl/mvebu/kirkwood.c
@@ -425,12 +425,13 @@ static struct of_device_id kirkwood_pinctrl_of_match[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, kirkwood_pinctrl_of_match);
-static int kirkwood_pinctrl_probe(struct device_d *dev)
+static int kirkwood_pinctrl_probe(struct device *dev)
{
struct resource *iores;
const struct of_device_id *match =
- of_match_node(kirkwood_pinctrl_of_match, dev->device_node);
+ of_match_node(kirkwood_pinctrl_of_match, dev->of_node);
struct mvebu_pinctrl_soc_info *soc =
(struct mvebu_pinctrl_soc_info *)match->data;
@@ -442,7 +443,7 @@ static int kirkwood_pinctrl_probe(struct device_d *dev)
return mvebu_pinctrl_probe(dev, soc);
}
-static struct driver_d kirkwood_pinctrl_driver = {
+static struct driver kirkwood_pinctrl_driver = {
.name = "pinctrl-kirkwood",
.probe = kirkwood_pinctrl_probe,
.of_compatible = kirkwood_pinctrl_of_match,
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index c1eb4518dc..f6d40b392f 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -11,7 +11,7 @@
#include <pinctrl.h>
#include <malloc.h>
#include <gpio.h>
-#include <mach/gpio.h>
+#include <mach/at91/gpio.h>
#include <linux/clk.h>
#include <dt-bindings/pinctrl/at91.h>
@@ -96,8 +96,7 @@ static int __pinctrl_at91_pio4_set_state(struct pinctrl_device *pdev,
npins /= sizeof(__be32);
if (!npins) {
- dev_err(pdev->dev, "Invalid pinmux property in %s\n",
- np->full_name);
+ dev_err(pdev->dev, "Invalid pinmux property in %pOF\n", np);
return -EINVAL;
}
@@ -226,7 +225,7 @@ static struct gpio_ops at91_gpio4_ops = {
.set = at91_gpio4_set,
};
-static int pinctrl_at91_pio4_gpiochip_add(struct device_d *dev,
+static int pinctrl_at91_pio4_gpiochip_add(struct device *dev,
struct pinctrl_at91_pio4 *pinctrl)
{
const struct at91_pinctrl_data *drvdata;
@@ -269,9 +268,9 @@ static struct pinctrl_ops pinctrl_at91_pio4_ops = {
.set_state = pinctrl_at91_pio4_set_state,
};
-static int pinctrl_at91_pio4_probe(struct device_d *dev)
+static int pinctrl_at91_pio4_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct pinctrl_at91_pio4 *pinctrl;
struct resource *io;
int ret;
@@ -306,8 +305,9 @@ static __maybe_unused struct of_device_id pinctrl_at91_pio4_dt_ids[] = {
{ .compatible = "atmel,sama5d2-pinctrl", .data = &sama5d2_pinctrl_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, pinctrl_at91_pio4_dt_ids);
-static struct driver_d pinctrl_at91_pio4_driver = {
+static struct driver pinctrl_at91_pio4_driver = {
.name = "pinctrl-at91-pio4",
.probe = pinctrl_at91_pio4_probe,
.of_compatible = DRV_OF_COMPAT(pinctrl_at91_pio4_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 0295d928cf..a3372a5035 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -16,10 +16,11 @@
#include <init.h>
#include <driver.h>
#include <getopt.h>
+#include <deep-probe.h>
-#include <mach/at91_pio.h>
-#include <mach/gpio.h>
-#include <mach/iomux.h>
+#include <mach/at91/at91_pio.h>
+#include <mach/at91/gpio.h>
+#include <mach/at91/iomux.h>
#include <pinctrl.h>
@@ -47,17 +48,25 @@ struct at91_gpio_chip {
#define DEBOUNCE_VAL_SHIFT 17
#define DEBOUNCE_VAL (0x3fff << DEBOUNCE_VAL_SHIFT)
-static int gpio_banks;
-
static struct at91_gpio_chip gpio_chip[MAX_GPIO_BANKS];
static inline struct at91_gpio_chip *pin_to_controller(unsigned pin)
{
+ struct at91_gpio_chip *chip;
+
pin /= MAX_NB_GPIO_PER_BANK;
- if (likely(pin < gpio_banks))
- return &gpio_chip[pin];
+ if (unlikely(pin >= MAX_GPIO_BANKS))
+ return NULL;
+
+ chip = &gpio_chip[pin];
+
+ if (!chip->regbase && deep_probe_is_supported()) {
+ char alias[] = "gpioX";
+ scnprintf(alias, sizeof(alias), "gpio%u", pin);
+ of_device_ensure_probed_by_alias(alias);
+ }
- return NULL;
+ return chip;
}
/**
@@ -96,7 +105,7 @@ int at91_mux_pin(unsigned pin, enum at91_mux mux, int use_pullup)
{
struct at91_gpio_chip *at91_gpio = pin_to_controller(pin);
void __iomem *pio;
- struct device_d *dev;
+ struct device *dev;
unsigned mask = pin_to_mask(pin);
int bank = pin_to_bank(pin);
@@ -370,15 +379,16 @@ static struct of_device_id at91_pinctrl_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, at91_pinctrl_dt_ids);
-static struct at91_pinctrl_mux_ops *at91_pinctrl_get_driver_data(struct device_d *dev)
+static struct at91_pinctrl_mux_ops *at91_pinctrl_get_driver_data(struct device *dev)
{
struct at91_pinctrl_mux_ops *ops_data = NULL;
int rc;
- if (dev->device_node) {
+ if (dev->of_node) {
const struct of_device_id *match;
- match = of_match_node(at91_pinctrl_dt_ids, dev->device_node);
+ match = of_match_node(at91_pinctrl_dt_ids, dev->of_node);
if (!match)
ops_data = NULL;
else
@@ -469,7 +479,7 @@ static struct pinctrl_ops at91_pinctrl_ops = {
.set_state = at91_pinctrl_set_state,
};
-static int at91_pinctrl_probe(struct device_d *dev)
+static int at91_pinctrl_probe(struct device *dev)
{
struct at91_pinctrl *info;
int ret;
@@ -509,7 +519,7 @@ static struct platform_device_id at91_pinctrl_ids[] = {
},
};
-static struct driver_d at91_pinctrl_driver = {
+static struct driver at91_pinctrl_driver = {
.name = "pinctrl-at91",
.probe = at91_pinctrl_probe,
.id_table = at91_pinctrl_ids,
@@ -615,16 +625,17 @@ static struct of_device_id at91_gpio_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, at91_gpio_dt_ids);
-static int at91_gpio_probe(struct device_d *dev)
+static int at91_gpio_probe(struct device *dev)
{
struct at91_gpio_chip *at91_gpio;
struct clk *clk;
int ret;
int alias_idx;
- if (dev->device_node)
- alias_idx = of_alias_get_id(dev->device_node, "gpio");
+ if (dev->of_node)
+ alias_idx = of_alias_get_id(dev->of_node, "gpio");
else
alias_idx = dev->id;
@@ -652,7 +663,6 @@ static int at91_gpio_probe(struct device_d *dev)
return ret;
}
- gpio_banks = max(gpio_banks, alias_idx + 1);
at91_gpio->regbase = dev_request_mem_region_err_null(dev, 0);
if (!at91_gpio->regbase)
return -ENOENT;
@@ -685,7 +695,7 @@ static struct platform_device_id at91_gpio_ids[] = {
},
};
-static struct driver_d at91_gpio_driver = {
+static struct driver at91_gpio_driver = {
.name = "gpio-at91",
.probe = at91_gpio_probe,
.id_table = at91_gpio_ids,
diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c
index 933445294f..57c1aee3af 100644
--- a/drivers/pinctrl/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/pinctrl-bcm2835.c
@@ -141,7 +141,7 @@ static struct pinctrl_ops bcm2835_pinctrl_ops = {
.set_state = bcm2835_pinctrl_set_state,
};
-static int bcm2835_gpio_probe(struct device_d *dev)
+static int bcm2835_gpio_probe(struct device *dev)
{
const struct plat_data *plat_data;
struct resource *iores;
@@ -208,8 +208,9 @@ static __maybe_unused struct of_device_id bcm2835_gpio_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, bcm2835_gpio_dt_ids);
-static struct driver_d bcm2835_gpio_driver = {
+static struct driver bcm2835_gpio_driver = {
.name = "bcm2835-gpio",
.probe = bcm2835_gpio_probe,
.of_compatible = DRV_OF_COMPAT(bcm2835_gpio_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c
index acee8178a5..6a2b22145e 100644
--- a/drivers/pinctrl/pinctrl-mxs.c
+++ b/drivers/pinctrl/pinctrl-mxs.c
@@ -33,15 +33,15 @@ static int mxs_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node
int ret;
int ma_present = 0, vol_present = 0, pull_present = 0;
- dev_dbg(iomux->pinctrl.dev, "set state: %s\n", np->full_name);
+ dev_dbg(iomux->pinctrl.dev, "set state: %pOF\n", np);
list = of_get_property(np, "fsl,pinmux-ids", &size);
if (!list)
return -EINVAL;
if (!size || size % 4) {
- dev_err(iomux->pinctrl.dev, "Invalid fsl,pinmux-ids property in %s\n",
- np->full_name);
+ dev_err(iomux->pinctrl.dev, "Invalid fsl,pinmux-ids property in %pOF\n",
+ np);
return -EINVAL;
}
@@ -119,7 +119,7 @@ static struct pinctrl_ops mxs_pinctrl_ops = {
.set_state = mxs_pinctrl_set_state,
};
-static int mxs_pinctrl_probe(struct device_d *dev)
+static int mxs_pinctrl_probe(struct device *dev)
{
struct mxs_pinctrl *iomux;
int ret = 0;
@@ -145,8 +145,9 @@ static __maybe_unused struct of_device_id mxs_pinctrl_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mxs_pinctrl_dt_ids);
-static struct driver_d mxs_pinctrl_driver = {
+static struct driver mxs_pinctrl_driver = {
.name = "mxs-pinctrl",
.probe = mxs_pinctrl_probe,
.of_compatible = DRV_OF_COMPAT(mxs_pinctrl_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index c99074407f..ca9939c9e9 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -16,6 +16,7 @@
#include <init.h>
#include <malloc.h>
#include <mfd/syscon.h>
+#include <linux/regmap.h>
#include <of.h>
#include <of_address.h>
#include <pinctrl.h>
@@ -25,19 +26,9 @@
#include <linux/clk.h>
#include <linux/err.h>
-enum rockchip_pinctrl_type {
- RK2928,
- RK3066B,
- RK3188,
- RK3568,
-};
-
-enum rockchip_pin_bank_type {
- COMMON_BANK,
- RK3188_BANK0,
-};
+#include "pinctrl-rockchip.h"
-/**
+/*
* Generate a bitmask for setting a value (v) with a write mask bit in hiword
* register 31:16 area.
*/
@@ -53,76 +44,19 @@ enum rockchip_pin_bank_type {
#define IOMUX_UNROUTED BIT(3)
#define IOMUX_WIDTH_3BIT BIT(4)
#define IOMUX_WIDTH_2BIT BIT(5)
-
-/**
- * struct rockchip_iomux
- * @type: iomux variant using IOMUX_* constants
- * @offset: if initialized to -1 it will be autocalculated, by specifying
- * an initial offset value the relevant source offset can be reset
- * to a new value for autocalculating the following iomux registers.
- */
-struct rockchip_iomux {
- int type;
- int offset;
-};
-
-/*
- * enum type index corresponding to rockchip_perpin_drv_list arrays index.
- */
-enum rockchip_pin_drv_type {
- DRV_TYPE_IO_DEFAULT = 0,
- DRV_TYPE_IO_1V8_OR_3V0,
- DRV_TYPE_IO_1V8_ONLY,
- DRV_TYPE_IO_1V8_3V0_AUTO,
- DRV_TYPE_IO_3V3_ONLY,
- DRV_TYPE_MAX
-};
-
-/*
- * enum type index corresponding to rockchip_pull_list arrays index.
- */
-enum rockchip_pin_pull_type {
- PULL_TYPE_IO_DEFAULT = 0,
- PULL_TYPE_IO_1V8_ONLY,
- PULL_TYPE_MAX
-};
-
-/**
- * struct rockchip_drv
- * @drv_type: drive strength variant using rockchip_perpin_drv_type
- * @offset: if initialized to -1 it will be autocalculated, by specifying
- * an initial offset value the relevant source offset can be reset
- * to a new value for autocalculating the following drive strength
- * registers. if used chips own cal_drv func instead to calculate
- * registers offset, the variant could be ignored.
- */
-struct rockchip_drv {
- enum rockchip_pin_drv_type drv_type;
- int offset;
-};
-
-struct rockchip_pin_bank {
- void __iomem *reg_base;
- struct clk *clk;
- u32 pin_base;
- u8 nr_pins;
- char *name;
- u8 bank_num;
- struct rockchip_iomux iomux[4];
- struct rockchip_drv drv[4];
- enum rockchip_pin_bank_type bank_type;
- bool valid;
- struct device_node *of_node;
- struct rockchip_pinctrl *drvdata;
- struct bgpio_chip bgpio_chip;
- u32 route_mask;
-};
+#define IOMUX_L_SOURCE_PMU BIT(6)
#define PIN_BANK(id, pins, label) \
{ \
.bank_num = id, \
.nr_pins = pins, \
.name = label, \
+ .iomux = { \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ }, \
}
#define PIN_BANK_IOMUX_FLAGS(id, pins, label, iom0, iom1, iom2, iom3) \
@@ -138,6 +72,134 @@ struct rockchip_pin_bank {
}, \
}
+#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ .iomux = { \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ }, \
+ .drv = { \
+ { .drv_type = type0, .offset = -1 }, \
+ { .drv_type = type1, .offset = -1 }, \
+ { .drv_type = type2, .offset = -1 }, \
+ { .drv_type = type3, .offset = -1 }, \
+ }, \
+ }
+
+#define PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(id, pins, label, iom0, iom1, \
+ iom2, iom3, pull0, pull1, \
+ pull2, pull3) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ .iomux = { \
+ { .type = iom0, .offset = -1 }, \
+ { .type = iom1, .offset = -1 }, \
+ { .type = iom2, .offset = -1 }, \
+ { .type = iom3, .offset = -1 }, \
+ }, \
+ .pull_type[0] = pull0, \
+ .pull_type[1] = pull1, \
+ .pull_type[2] = pull2, \
+ .pull_type[3] = pull3, \
+ }
+
+#define PIN_BANK_DRV_FLAGS_PULL_FLAGS(id, pins, label, drv0, drv1, \
+ drv2, drv3, pull0, pull1, \
+ pull2, pull3) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ .iomux = { \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ { .offset = -1 }, \
+ }, \
+ .drv = { \
+ { .drv_type = drv0, .offset = -1 }, \
+ { .drv_type = drv1, .offset = -1 }, \
+ { .drv_type = drv2, .offset = -1 }, \
+ { .drv_type = drv3, .offset = -1 }, \
+ }, \
+ .pull_type[0] = pull0, \
+ .pull_type[1] = pull1, \
+ .pull_type[2] = pull2, \
+ .pull_type[3] = pull3, \
+ }
+
+#define PIN_BANK_IOMUX_FLAGS_OFFSET(id, pins, label, iom0, iom1, iom2, \
+ iom3, offset0, offset1, offset2, \
+ offset3) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ .iomux = { \
+ { .type = iom0, .offset = offset0 }, \
+ { .type = iom1, .offset = offset1 }, \
+ { .type = iom2, .offset = offset2 }, \
+ { .type = iom3, .offset = offset3 }, \
+ }, \
+ }
+
+#define PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(id, pins, label, iom0, iom1, \
+ iom2, iom3, drv0, drv1, drv2, \
+ drv3, offset0, offset1, \
+ offset2, offset3) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ .iomux = { \
+ { .type = iom0, .offset = -1 }, \
+ { .type = iom1, .offset = -1 }, \
+ { .type = iom2, .offset = -1 }, \
+ { .type = iom3, .offset = -1 }, \
+ }, \
+ .drv = { \
+ { .drv_type = drv0, .offset = offset0 }, \
+ { .drv_type = drv1, .offset = offset1 }, \
+ { .drv_type = drv2, .offset = offset2 }, \
+ { .drv_type = drv3, .offset = offset3 }, \
+ }, \
+ }
+
+#define PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET_PULL_FLAGS(id, pins, \
+ label, iom0, iom1, iom2, \
+ iom3, drv0, drv1, drv2, \
+ drv3, offset0, offset1, \
+ offset2, offset3, pull0, \
+ pull1, pull2, pull3) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ .iomux = { \
+ { .type = iom0, .offset = -1 }, \
+ { .type = iom1, .offset = -1 }, \
+ { .type = iom2, .offset = -1 }, \
+ { .type = iom3, .offset = -1 }, \
+ }, \
+ .drv = { \
+ { .drv_type = drv0, .offset = offset0 }, \
+ { .drv_type = drv1, .offset = offset1 }, \
+ { .drv_type = drv2, .offset = offset2 }, \
+ { .drv_type = drv3, .offset = offset3 }, \
+ }, \
+ .pull_type[0] = pull0, \
+ .pull_type[1] = pull1, \
+ .pull_type[2] = pull2, \
+ .pull_type[3] = pull3, \
+ }
+
#define PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, FLAG) \
{ \
.bank_num = ID, \
@@ -157,297 +219,1240 @@ struct rockchip_pin_bank {
#define RK_MUXROUTE_PMU(ID, PIN, FUNC, REG, VAL) \
PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_PMU)
-enum rockchip_mux_route_location {
- ROCKCHIP_ROUTE_SAME = 0,
- ROCKCHIP_ROUTE_PMU,
- ROCKCHIP_ROUTE_GRF,
+#define RK3588_PIN_BANK_FLAGS(ID, PIN, LABEL, M, P) \
+ PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(ID, PIN, LABEL, M, M, M, M, P, P, P, P)
+
+enum {
+ RK_BIAS_DISABLE = 0,
+ RK_BIAS_PULL_UP,
+ RK_BIAS_PULL_DOWN,
+ RK_BIAS_BUS_HOLD,
};
-/**
- * struct rockchip_mux_recalced_data: represent a pin iomux data.
- * @bank_num: bank number.
- * @pin: index at register or used to calc index.
- * @func: the min pin.
- * @route_location: the mux route location (same, pmu, grf).
- * @route_offset: the max pin.
- * @route_val: the register offset.
+static struct rockchip_pinctrl *to_rockchip_pinctrl(struct pinctrl_device *pdev)
+{
+ return container_of(pdev, struct rockchip_pinctrl, pctl_dev);
+}
+
+static int parse_bias_config(struct device_node *np)
+{
+ u32 val;
+
+ if (of_property_read_u32(np, "bias-pull-up", &val) != -EINVAL)
+ return RK_BIAS_PULL_UP;
+ else if (of_property_read_u32(np, "bias-pull-down", &val) != -EINVAL)
+ return RK_BIAS_PULL_DOWN;
+ else if (of_property_read_u32(np, "bias-bus-hold", &val) != -EINVAL)
+ return RK_BIAS_BUS_HOLD;
+ else
+ return RK_BIAS_DISABLE;
+}
+
+static struct rockchip_pin_bank *bank_num_to_bank(
+ struct rockchip_pinctrl *info,
+ unsigned num)
+{
+ struct rockchip_pin_bank *b = info->ctrl->pin_banks;
+ int i;
+
+ for (i = 0; i < info->ctrl->nr_banks; i++, b++) {
+ if (b->bank_num == num)
+ return b;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Hardware access
*/
-struct rockchip_mux_route_data {
- u8 bank_num;
- u8 pin;
- u8 func;
- enum rockchip_mux_route_location route_location;
- u32 route_offset;
- u32 route_val;
+
+static struct rockchip_mux_recalced_data rv1108_mux_recalced_data[] = {
+ {
+ .num = 1,
+ .pin = 0,
+ .reg = 0x418,
+ .bit = 0,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 1,
+ .reg = 0x418,
+ .bit = 2,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 2,
+ .reg = 0x418,
+ .bit = 4,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 3,
+ .reg = 0x418,
+ .bit = 6,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 4,
+ .reg = 0x418,
+ .bit = 8,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 5,
+ .reg = 0x418,
+ .bit = 10,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 6,
+ .reg = 0x418,
+ .bit = 12,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 7,
+ .reg = 0x418,
+ .bit = 14,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 8,
+ .reg = 0x41c,
+ .bit = 0,
+ .mask = 0x3
+ }, {
+ .num = 1,
+ .pin = 9,
+ .reg = 0x41c,
+ .bit = 2,
+ .mask = 0x3
+ },
+};
+
+static struct rockchip_mux_recalced_data rv1126_mux_recalced_data[] = {
+ {
+ .num = 0,
+ .pin = 20,
+ .reg = 0x10000,
+ .bit = 0,
+ .mask = 0xf
+ },
+ {
+ .num = 0,
+ .pin = 21,
+ .reg = 0x10000,
+ .bit = 4,
+ .mask = 0xf
+ },
+ {
+ .num = 0,
+ .pin = 22,
+ .reg = 0x10000,
+ .bit = 8,
+ .mask = 0xf
+ },
+ {
+ .num = 0,
+ .pin = 23,
+ .reg = 0x10000,
+ .bit = 12,
+ .mask = 0xf
+ },
};
-struct rockchip_pin_ctrl {
- struct rockchip_pin_bank *pin_banks;
- u32 nr_banks;
- u32 nr_pins;
- char *label;
- enum rockchip_pinctrl_type type;
- int grf_mux_offset;
- int pmu_mux_offset;
- int grf_drv_offset;
- int pmu_drv_offset;
- struct rockchip_mux_route_data *iomux_routes;
- u32 niomux_routes;
- void (*pull_calc_reg)(struct rockchip_pin_bank *bank, int pin_num,
- void __iomem **reg, u8 *bit);
- void (*drv_calc_reg)(struct rockchip_pin_bank *bank,
- int pin_num, void __iomem **reg, u8 *bit);
+static struct rockchip_mux_recalced_data rk3128_mux_recalced_data[] = {
+ {
+ .num = 2,
+ .pin = 20,
+ .reg = 0xe8,
+ .bit = 0,
+ .mask = 0x7
+ }, {
+ .num = 2,
+ .pin = 21,
+ .reg = 0xe8,
+ .bit = 4,
+ .mask = 0x7
+ }, {
+ .num = 2,
+ .pin = 22,
+ .reg = 0xe8,
+ .bit = 8,
+ .mask = 0x7
+ }, {
+ .num = 2,
+ .pin = 23,
+ .reg = 0xe8,
+ .bit = 12,
+ .mask = 0x7
+ }, {
+ .num = 2,
+ .pin = 24,
+ .reg = 0xd4,
+ .bit = 12,
+ .mask = 0x7
+ },
};
-struct rockchip_pinctrl {
- void __iomem *reg_base;
- void __iomem *reg_pmu;
- struct pinctrl_device pctl_dev;
- struct rockchip_pin_ctrl *ctrl;
+static struct rockchip_mux_recalced_data rk3308_mux_recalced_data[] = {
+ {
+ /* gpio1b6_sel */
+ .num = 1,
+ .pin = 14,
+ .reg = 0x28,
+ .bit = 12,
+ .mask = 0xf
+ }, {
+ /* gpio1b7_sel */
+ .num = 1,
+ .pin = 15,
+ .reg = 0x2c,
+ .bit = 0,
+ .mask = 0x3
+ }, {
+ /* gpio1c2_sel */
+ .num = 1,
+ .pin = 18,
+ .reg = 0x30,
+ .bit = 4,
+ .mask = 0xf
+ }, {
+ /* gpio1c3_sel */
+ .num = 1,
+ .pin = 19,
+ .reg = 0x30,
+ .bit = 8,
+ .mask = 0xf
+ }, {
+ /* gpio1c4_sel */
+ .num = 1,
+ .pin = 20,
+ .reg = 0x30,
+ .bit = 12,
+ .mask = 0xf
+ }, {
+ /* gpio1c5_sel */
+ .num = 1,
+ .pin = 21,
+ .reg = 0x34,
+ .bit = 0,
+ .mask = 0xf
+ }, {
+ /* gpio1c6_sel */
+ .num = 1,
+ .pin = 22,
+ .reg = 0x34,
+ .bit = 4,
+ .mask = 0xf
+ }, {
+ /* gpio1c7_sel */
+ .num = 1,
+ .pin = 23,
+ .reg = 0x34,
+ .bit = 8,
+ .mask = 0xf
+ }, {
+ /* gpio2a2_sel */
+ .num = 2,
+ .pin = 2,
+ .reg = 0x40,
+ .bit = 4,
+ .mask = 0x3
+ }, {
+ /* gpio2a3_sel */
+ .num = 2,
+ .pin = 3,
+ .reg = 0x40,
+ .bit = 6,
+ .mask = 0x3
+ }, {
+ /* gpio2c0_sel */
+ .num = 2,
+ .pin = 16,
+ .reg = 0x50,
+ .bit = 0,
+ .mask = 0x3
+ }, {
+ /* gpio3b2_sel */
+ .num = 3,
+ .pin = 10,
+ .reg = 0x68,
+ .bit = 4,
+ .mask = 0x3
+ }, {
+ /* gpio3b3_sel */
+ .num = 3,
+ .pin = 11,
+ .reg = 0x68,
+ .bit = 6,
+ .mask = 0x3
+ }, {
+ /* gpio3b4_sel */
+ .num = 3,
+ .pin = 12,
+ .reg = 0x68,
+ .bit = 8,
+ .mask = 0xf
+ }, {
+ /* gpio3b5_sel */
+ .num = 3,
+ .pin = 13,
+ .reg = 0x68,
+ .bit = 12,
+ .mask = 0xf
+ },
};
-enum {
- RK_BIAS_DISABLE = 0,
- RK_BIAS_PULL_UP,
- RK_BIAS_PULL_DOWN,
- RK_BIAS_BUS_HOLD,
+static struct rockchip_mux_recalced_data rk3328_mux_recalced_data[] = {
+ {
+ .num = 2,
+ .pin = 12,
+ .reg = 0x24,
+ .bit = 8,
+ .mask = 0x3
+ }, {
+ .num = 2,
+ .pin = 15,
+ .reg = 0x28,
+ .bit = 0,
+ .mask = 0x7
+ }, {
+ .num = 2,
+ .pin = 23,
+ .reg = 0x30,
+ .bit = 14,
+ .mask = 0x3
+ },
};
-/* GPIO registers */
-enum {
- RK_GPIO_SWPORT_DR = 0x00,
- RK_GPIO_SWPORT_DDR = 0x04,
- RK_GPIO_EXT_PORT = 0x50,
+static void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin,
+ int *reg, u8 *bit, int *mask)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct rockchip_mux_recalced_data *data = NULL;
+ int i;
+
+ for (i = 0; i < ctrl->niomux_recalced; i++) {
+ data = &ctrl->iomux_recalced[i];
+ if (data->num == bank->bank_num &&
+ data->pin == pin)
+ break;
+ }
+
+ if (i >= ctrl->niomux_recalced)
+ return;
+
+ *reg = data->reg;
+ *mask = data->mask;
+ *bit = data->bit;
+}
+
+static struct rockchip_mux_route_data px30_mux_route_data[] = {
+ RK_MUXROUTE_SAME(2, RK_PB4, 1, 0x184, BIT(16 + 7)), /* cif-d0m0 */
+ RK_MUXROUTE_SAME(3, RK_PA1, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d0m1 */
+ RK_MUXROUTE_SAME(2, RK_PB6, 1, 0x184, BIT(16 + 7)), /* cif-d1m0 */
+ RK_MUXROUTE_SAME(3, RK_PA2, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d1m1 */
+ RK_MUXROUTE_SAME(2, RK_PA0, 1, 0x184, BIT(16 + 7)), /* cif-d2m0 */
+ RK_MUXROUTE_SAME(3, RK_PA3, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d2m1 */
+ RK_MUXROUTE_SAME(2, RK_PA1, 1, 0x184, BIT(16 + 7)), /* cif-d3m0 */
+ RK_MUXROUTE_SAME(3, RK_PA5, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d3m1 */
+ RK_MUXROUTE_SAME(2, RK_PA2, 1, 0x184, BIT(16 + 7)), /* cif-d4m0 */
+ RK_MUXROUTE_SAME(3, RK_PA7, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d4m1 */
+ RK_MUXROUTE_SAME(2, RK_PA3, 1, 0x184, BIT(16 + 7)), /* cif-d5m0 */
+ RK_MUXROUTE_SAME(3, RK_PB0, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d5m1 */
+ RK_MUXROUTE_SAME(2, RK_PA4, 1, 0x184, BIT(16 + 7)), /* cif-d6m0 */
+ RK_MUXROUTE_SAME(3, RK_PB1, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d6m1 */
+ RK_MUXROUTE_SAME(2, RK_PA5, 1, 0x184, BIT(16 + 7)), /* cif-d7m0 */
+ RK_MUXROUTE_SAME(3, RK_PB4, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d7m1 */
+ RK_MUXROUTE_SAME(2, RK_PA6, 1, 0x184, BIT(16 + 7)), /* cif-d8m0 */
+ RK_MUXROUTE_SAME(3, RK_PB6, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d8m1 */
+ RK_MUXROUTE_SAME(2, RK_PA7, 1, 0x184, BIT(16 + 7)), /* cif-d9m0 */
+ RK_MUXROUTE_SAME(3, RK_PB7, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d9m1 */
+ RK_MUXROUTE_SAME(2, RK_PB7, 1, 0x184, BIT(16 + 7)), /* cif-d10m0 */
+ RK_MUXROUTE_SAME(3, RK_PC6, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d10m1 */
+ RK_MUXROUTE_SAME(2, RK_PC0, 1, 0x184, BIT(16 + 7)), /* cif-d11m0 */
+ RK_MUXROUTE_SAME(3, RK_PC7, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d11m1 */
+ RK_MUXROUTE_SAME(2, RK_PB0, 1, 0x184, BIT(16 + 7)), /* cif-vsyncm0 */
+ RK_MUXROUTE_SAME(3, RK_PD1, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-vsyncm1 */
+ RK_MUXROUTE_SAME(2, RK_PB1, 1, 0x184, BIT(16 + 7)), /* cif-hrefm0 */
+ RK_MUXROUTE_SAME(3, RK_PD2, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-hrefm1 */
+ RK_MUXROUTE_SAME(2, RK_PB2, 1, 0x184, BIT(16 + 7)), /* cif-clkinm0 */
+ RK_MUXROUTE_SAME(3, RK_PD3, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-clkinm1 */
+ RK_MUXROUTE_SAME(2, RK_PB3, 1, 0x184, BIT(16 + 7)), /* cif-clkoutm0 */
+ RK_MUXROUTE_SAME(3, RK_PD0, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-clkoutm1 */
+ RK_MUXROUTE_SAME(3, RK_PC6, 2, 0x184, BIT(16 + 8)), /* pdm-m0 */
+ RK_MUXROUTE_SAME(2, RK_PC6, 1, 0x184, BIT(16 + 8) | BIT(8)), /* pdm-m1 */
+ RK_MUXROUTE_SAME(3, RK_PD3, 2, 0x184, BIT(16 + 8)), /* pdm-sdi0m0 */
+ RK_MUXROUTE_SAME(2, RK_PC5, 2, 0x184, BIT(16 + 8) | BIT(8)), /* pdm-sdi0m1 */
+ RK_MUXROUTE_SAME(1, RK_PD3, 2, 0x184, BIT(16 + 10)), /* uart2-rxm0 */
+ RK_MUXROUTE_SAME(2, RK_PB6, 2, 0x184, BIT(16 + 10) | BIT(10)), /* uart2-rxm1 */
+ RK_MUXROUTE_SAME(1, RK_PD2, 2, 0x184, BIT(16 + 10)), /* uart2-txm0 */
+ RK_MUXROUTE_SAME(2, RK_PB4, 2, 0x184, BIT(16 + 10) | BIT(10)), /* uart2-txm1 */
+ RK_MUXROUTE_SAME(0, RK_PC1, 2, 0x184, BIT(16 + 9)), /* uart3-rxm0 */
+ RK_MUXROUTE_SAME(1, RK_PB7, 2, 0x184, BIT(16 + 9) | BIT(9)), /* uart3-rxm1 */
+ RK_MUXROUTE_SAME(0, RK_PC0, 2, 0x184, BIT(16 + 9)), /* uart3-txm0 */
+ RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x184, BIT(16 + 9) | BIT(9)), /* uart3-txm1 */
+ RK_MUXROUTE_SAME(0, RK_PC2, 2, 0x184, BIT(16 + 9)), /* uart3-ctsm0 */
+ RK_MUXROUTE_SAME(1, RK_PB4, 2, 0x184, BIT(16 + 9) | BIT(9)), /* uart3-ctsm1 */
+ RK_MUXROUTE_SAME(0, RK_PC3, 2, 0x184, BIT(16 + 9)), /* uart3-rtsm0 */
+ RK_MUXROUTE_SAME(1, RK_PB5, 2, 0x184, BIT(16 + 9) | BIT(9)), /* uart3-rtsm1 */
};
-/* GPIO registers */
-enum {
- RK_GPIOV2_DR_L = 0x00,
- RK_GPIOV2_DR_H = 0x04,
- RK_GPIOV2_DDR_L = 0x08,
- RK_GPIOV2_DDR_H = 0x0c,
+static struct rockchip_mux_route_data rv1126_mux_route_data[] = {
+ RK_MUXROUTE_GRF(3, RK_PD2, 1, 0x10260, WRITE_MASK_VAL(0, 0, 0)), /* I2S0_MCLK_M0 */
+ RK_MUXROUTE_GRF(3, RK_PB0, 3, 0x10260, WRITE_MASK_VAL(0, 0, 1)), /* I2S0_MCLK_M1 */
+
+ RK_MUXROUTE_GRF(0, RK_PD4, 4, 0x10260, WRITE_MASK_VAL(3, 2, 0)), /* I2S1_MCLK_M0 */
+ RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x10260, WRITE_MASK_VAL(3, 2, 1)), /* I2S1_MCLK_M1 */
+ RK_MUXROUTE_GRF(2, RK_PC7, 6, 0x10260, WRITE_MASK_VAL(3, 2, 2)), /* I2S1_MCLK_M2 */
+
+ RK_MUXROUTE_GRF(1, RK_PD0, 1, 0x10260, WRITE_MASK_VAL(4, 4, 0)), /* I2S2_MCLK_M0 */
+ RK_MUXROUTE_GRF(2, RK_PB3, 2, 0x10260, WRITE_MASK_VAL(4, 4, 1)), /* I2S2_MCLK_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PD4, 2, 0x10260, WRITE_MASK_VAL(12, 12, 0)), /* PDM_CLK0_M0 */
+ RK_MUXROUTE_GRF(3, RK_PC0, 3, 0x10260, WRITE_MASK_VAL(12, 12, 1)), /* PDM_CLK0_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PC6, 1, 0x10264, WRITE_MASK_VAL(0, 0, 0)), /* CIF_CLKOUT_M0 */
+ RK_MUXROUTE_GRF(2, RK_PD1, 3, 0x10264, WRITE_MASK_VAL(0, 0, 1)), /* CIF_CLKOUT_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PA4, 5, 0x10264, WRITE_MASK_VAL(5, 4, 0)), /* I2C3_SCL_M0 */
+ RK_MUXROUTE_GRF(2, RK_PD4, 7, 0x10264, WRITE_MASK_VAL(5, 4, 1)), /* I2C3_SCL_M1 */
+ RK_MUXROUTE_GRF(1, RK_PD6, 3, 0x10264, WRITE_MASK_VAL(5, 4, 2)), /* I2C3_SCL_M2 */
+
+ RK_MUXROUTE_GRF(3, RK_PA0, 7, 0x10264, WRITE_MASK_VAL(6, 6, 0)), /* I2C4_SCL_M0 */
+ RK_MUXROUTE_GRF(4, RK_PA0, 4, 0x10264, WRITE_MASK_VAL(6, 6, 1)), /* I2C4_SCL_M1 */
+
+ RK_MUXROUTE_GRF(2, RK_PA5, 7, 0x10264, WRITE_MASK_VAL(9, 8, 0)), /* I2C5_SCL_M0 */
+ RK_MUXROUTE_GRF(3, RK_PB0, 5, 0x10264, WRITE_MASK_VAL(9, 8, 1)), /* I2C5_SCL_M1 */
+ RK_MUXROUTE_GRF(1, RK_PD0, 4, 0x10264, WRITE_MASK_VAL(9, 8, 2)), /* I2C5_SCL_M2 */
+
+ RK_MUXROUTE_GRF(3, RK_PC0, 5, 0x10264, WRITE_MASK_VAL(11, 10, 0)), /* SPI1_CLK_M0 */
+ RK_MUXROUTE_GRF(1, RK_PC6, 3, 0x10264, WRITE_MASK_VAL(11, 10, 1)), /* SPI1_CLK_M1 */
+ RK_MUXROUTE_GRF(2, RK_PD5, 6, 0x10264, WRITE_MASK_VAL(11, 10, 2)), /* SPI1_CLK_M2 */
+
+ RK_MUXROUTE_GRF(3, RK_PC0, 2, 0x10264, WRITE_MASK_VAL(12, 12, 0)), /* RGMII_CLK_M0 */
+ RK_MUXROUTE_GRF(2, RK_PB7, 2, 0x10264, WRITE_MASK_VAL(12, 12, 1)), /* RGMII_CLK_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PA1, 3, 0x10264, WRITE_MASK_VAL(13, 13, 0)), /* CAN_TXD_M0 */
+ RK_MUXROUTE_GRF(3, RK_PA7, 5, 0x10264, WRITE_MASK_VAL(13, 13, 1)), /* CAN_TXD_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PA4, 6, 0x10268, WRITE_MASK_VAL(0, 0, 0)), /* PWM8_M0 */
+ RK_MUXROUTE_GRF(2, RK_PD7, 5, 0x10268, WRITE_MASK_VAL(0, 0, 1)), /* PWM8_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PA5, 6, 0x10268, WRITE_MASK_VAL(2, 2, 0)), /* PWM9_M0 */
+ RK_MUXROUTE_GRF(2, RK_PD6, 5, 0x10268, WRITE_MASK_VAL(2, 2, 1)), /* PWM9_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PA6, 6, 0x10268, WRITE_MASK_VAL(4, 4, 0)), /* PWM10_M0 */
+ RK_MUXROUTE_GRF(2, RK_PD5, 5, 0x10268, WRITE_MASK_VAL(4, 4, 1)), /* PWM10_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PA7, 6, 0x10268, WRITE_MASK_VAL(6, 6, 0)), /* PWM11_IR_M0 */
+ RK_MUXROUTE_GRF(3, RK_PA1, 5, 0x10268, WRITE_MASK_VAL(6, 6, 1)), /* PWM11_IR_M1 */
+
+ RK_MUXROUTE_GRF(1, RK_PA5, 3, 0x10268, WRITE_MASK_VAL(8, 8, 0)), /* UART2_TX_M0 */
+ RK_MUXROUTE_GRF(3, RK_PA2, 1, 0x10268, WRITE_MASK_VAL(8, 8, 1)), /* UART2_TX_M1 */
+
+ RK_MUXROUTE_GRF(3, RK_PC6, 3, 0x10268, WRITE_MASK_VAL(11, 10, 0)), /* UART3_TX_M0 */
+ RK_MUXROUTE_GRF(1, RK_PA7, 2, 0x10268, WRITE_MASK_VAL(11, 10, 1)), /* UART3_TX_M1 */
+ RK_MUXROUTE_GRF(3, RK_PA0, 4, 0x10268, WRITE_MASK_VAL(11, 10, 2)), /* UART3_TX_M2 */
+
+ RK_MUXROUTE_GRF(3, RK_PA4, 4, 0x10268, WRITE_MASK_VAL(13, 12, 0)), /* UART4_TX_M0 */
+ RK_MUXROUTE_GRF(2, RK_PA6, 4, 0x10268, WRITE_MASK_VAL(13, 12, 1)), /* UART4_TX_M1 */
+ RK_MUXROUTE_GRF(1, RK_PD5, 3, 0x10268, WRITE_MASK_VAL(13, 12, 2)), /* UART4_TX_M2 */
+
+ RK_MUXROUTE_GRF(3, RK_PA6, 4, 0x10268, WRITE_MASK_VAL(15, 14, 0)), /* UART5_TX_M0 */
+ RK_MUXROUTE_GRF(2, RK_PB0, 4, 0x10268, WRITE_MASK_VAL(15, 14, 1)), /* UART5_TX_M1 */
+ RK_MUXROUTE_GRF(2, RK_PA0, 3, 0x10268, WRITE_MASK_VAL(15, 14, 2)), /* UART5_TX_M2 */
+
+ RK_MUXROUTE_PMU(0, RK_PB6, 3, 0x0114, WRITE_MASK_VAL(0, 0, 0)), /* PWM0_M0 */
+ RK_MUXROUTE_PMU(2, RK_PB3, 5, 0x0114, WRITE_MASK_VAL(0, 0, 1)), /* PWM0_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PB7, 3, 0x0114, WRITE_MASK_VAL(2, 2, 0)), /* PWM1_M0 */
+ RK_MUXROUTE_PMU(2, RK_PB2, 5, 0x0114, WRITE_MASK_VAL(2, 2, 1)), /* PWM1_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PC0, 3, 0x0114, WRITE_MASK_VAL(4, 4, 0)), /* PWM2_M0 */
+ RK_MUXROUTE_PMU(2, RK_PB1, 5, 0x0114, WRITE_MASK_VAL(4, 4, 1)), /* PWM2_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PC1, 3, 0x0114, WRITE_MASK_VAL(6, 6, 0)), /* PWM3_IR_M0 */
+ RK_MUXROUTE_PMU(2, RK_PB0, 5, 0x0114, WRITE_MASK_VAL(6, 6, 1)), /* PWM3_IR_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PC2, 3, 0x0114, WRITE_MASK_VAL(8, 8, 0)), /* PWM4_M0 */
+ RK_MUXROUTE_PMU(2, RK_PA7, 5, 0x0114, WRITE_MASK_VAL(8, 8, 1)), /* PWM4_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PC3, 3, 0x0114, WRITE_MASK_VAL(10, 10, 0)), /* PWM5_M0 */
+ RK_MUXROUTE_PMU(2, RK_PA6, 5, 0x0114, WRITE_MASK_VAL(10, 10, 1)), /* PWM5_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PB2, 3, 0x0114, WRITE_MASK_VAL(12, 12, 0)), /* PWM6_M0 */
+ RK_MUXROUTE_PMU(2, RK_PD4, 5, 0x0114, WRITE_MASK_VAL(12, 12, 1)), /* PWM6_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PB1, 3, 0x0114, WRITE_MASK_VAL(14, 14, 0)), /* PWM7_IR_M0 */
+ RK_MUXROUTE_PMU(3, RK_PA0, 5, 0x0114, WRITE_MASK_VAL(14, 14, 1)), /* PWM7_IR_M1 */
+
+ RK_MUXROUTE_PMU(0, RK_PB0, 1, 0x0118, WRITE_MASK_VAL(1, 0, 0)), /* SPI0_CLK_M0 */
+ RK_MUXROUTE_PMU(2, RK_PA1, 1, 0x0118, WRITE_MASK_VAL(1, 0, 1)), /* SPI0_CLK_M1 */
+ RK_MUXROUTE_PMU(2, RK_PB2, 6, 0x0118, WRITE_MASK_VAL(1, 0, 2)), /* SPI0_CLK_M2 */
+
+ RK_MUXROUTE_PMU(0, RK_PB6, 2, 0x0118, WRITE_MASK_VAL(2, 2, 0)), /* UART1_TX_M0 */
+ RK_MUXROUTE_PMU(1, RK_PD0, 5, 0x0118, WRITE_MASK_VAL(2, 2, 1)), /* UART1_TX_M1 */
+};
+
+static struct rockchip_mux_route_data rk3128_mux_route_data[] = {
+ RK_MUXROUTE_SAME(1, RK_PB2, 1, 0x144, BIT(16 + 3) | BIT(16 + 4)), /* spi-0 */
+ RK_MUXROUTE_SAME(1, RK_PD3, 3, 0x144, BIT(16 + 3) | BIT(16 + 4) | BIT(3)), /* spi-1 */
+ RK_MUXROUTE_SAME(0, RK_PB5, 2, 0x144, BIT(16 + 3) | BIT(16 + 4) | BIT(4)), /* spi-2 */
+ RK_MUXROUTE_SAME(1, RK_PA5, 1, 0x144, BIT(16 + 5)), /* i2s-0 */
+ RK_MUXROUTE_SAME(0, RK_PB6, 1, 0x144, BIT(16 + 5) | BIT(5)), /* i2s-1 */
+ RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x144, BIT(16 + 6)), /* emmc-0 */
+ RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x144, BIT(16 + 6) | BIT(6)), /* emmc-1 */
+};
+
+static struct rockchip_mux_route_data rk3188_mux_route_data[] = {
+ RK_MUXROUTE_SAME(0, RK_PD0, 1, 0xa0, BIT(16 + 11)), /* non-iomuxed emmc/flash pins on flash-dqs */
+ RK_MUXROUTE_SAME(0, RK_PD0, 2, 0xa0, BIT(16 + 11) | BIT(11)), /* non-iomuxed emmc/flash pins on emmc-clk */
+};
+
+static struct rockchip_mux_route_data rk3228_mux_route_data[] = {
+ RK_MUXROUTE_SAME(0, RK_PD2, 1, 0x50, BIT(16)), /* pwm0-0 */
+ RK_MUXROUTE_SAME(3, RK_PC5, 1, 0x50, BIT(16) | BIT(0)), /* pwm0-1 */
+ RK_MUXROUTE_SAME(0, RK_PD3, 1, 0x50, BIT(16 + 1)), /* pwm1-0 */
+ RK_MUXROUTE_SAME(0, RK_PD6, 2, 0x50, BIT(16 + 1) | BIT(1)), /* pwm1-1 */
+ RK_MUXROUTE_SAME(0, RK_PD4, 1, 0x50, BIT(16 + 2)), /* pwm2-0 */
+ RK_MUXROUTE_SAME(1, RK_PB4, 2, 0x50, BIT(16 + 2) | BIT(2)), /* pwm2-1 */
+ RK_MUXROUTE_SAME(3, RK_PD2, 1, 0x50, BIT(16 + 3)), /* pwm3-0 */
+ RK_MUXROUTE_SAME(1, RK_PB3, 2, 0x50, BIT(16 + 3) | BIT(3)), /* pwm3-1 */
+ RK_MUXROUTE_SAME(1, RK_PA1, 1, 0x50, BIT(16 + 4)), /* sdio-0_d0 */
+ RK_MUXROUTE_SAME(3, RK_PA2, 1, 0x50, BIT(16 + 4) | BIT(4)), /* sdio-1_d0 */
+ RK_MUXROUTE_SAME(0, RK_PB5, 2, 0x50, BIT(16 + 5)), /* spi-0_rx */
+ RK_MUXROUTE_SAME(2, RK_PA0, 2, 0x50, BIT(16 + 5) | BIT(5)), /* spi-1_rx */
+ RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x50, BIT(16 + 7)), /* emmc-0_cmd */
+ RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x50, BIT(16 + 7) | BIT(7)), /* emmc-1_cmd */
+ RK_MUXROUTE_SAME(1, RK_PC3, 2, 0x50, BIT(16 + 8)), /* uart2-0_rx */
+ RK_MUXROUTE_SAME(1, RK_PB2, 2, 0x50, BIT(16 + 8) | BIT(8)), /* uart2-1_rx */
+ RK_MUXROUTE_SAME(1, RK_PB2, 1, 0x50, BIT(16 + 11)), /* uart1-0_rx */
+ RK_MUXROUTE_SAME(3, RK_PB5, 1, 0x50, BIT(16 + 11) | BIT(11)), /* uart1-1_rx */
+};
+
+static struct rockchip_mux_route_data rk3288_mux_route_data[] = {
+ RK_MUXROUTE_SAME(7, RK_PC0, 2, 0x264, BIT(16 + 12) | BIT(12)), /* edphdmi_cecinoutt1 */
+ RK_MUXROUTE_SAME(7, RK_PC7, 4, 0x264, BIT(16 + 12)), /* edphdmi_cecinout */
+};
+
+static struct rockchip_mux_route_data rk3308_mux_route_data[] = {
+ RK_MUXROUTE_SAME(0, RK_PC3, 1, 0x314, BIT(16 + 0) | BIT(0)), /* rtc_clk */
+ RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x314, BIT(16 + 2) | BIT(16 + 3)), /* uart2_rxm0 */
+ RK_MUXROUTE_SAME(4, RK_PD2, 2, 0x314, BIT(16 + 2) | BIT(16 + 3) | BIT(2)), /* uart2_rxm1 */
+ RK_MUXROUTE_SAME(0, RK_PB7, 2, 0x608, BIT(16 + 8) | BIT(16 + 9)), /* i2c3_sdam0 */
+ RK_MUXROUTE_SAME(3, RK_PB4, 2, 0x608, BIT(16 + 8) | BIT(16 + 9) | BIT(8)), /* i2c3_sdam1 */
+ RK_MUXROUTE_SAME(2, RK_PA0, 3, 0x608, BIT(16 + 8) | BIT(16 + 9) | BIT(9)), /* i2c3_sdam2 */
+ RK_MUXROUTE_SAME(1, RK_PA3, 2, 0x308, BIT(16 + 3)), /* i2s-8ch-1-sclktxm0 */
+ RK_MUXROUTE_SAME(1, RK_PA4, 2, 0x308, BIT(16 + 3)), /* i2s-8ch-1-sclkrxm0 */
+ RK_MUXROUTE_SAME(1, RK_PB5, 2, 0x308, BIT(16 + 3) | BIT(3)), /* i2s-8ch-1-sclktxm1 */
+ RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x308, BIT(16 + 3) | BIT(3)), /* i2s-8ch-1-sclkrxm1 */
+ RK_MUXROUTE_SAME(1, RK_PA4, 3, 0x308, BIT(16 + 12) | BIT(16 + 13)), /* pdm-clkm0 */
+ RK_MUXROUTE_SAME(1, RK_PB6, 4, 0x308, BIT(16 + 12) | BIT(16 + 13) | BIT(12)), /* pdm-clkm1 */
+ RK_MUXROUTE_SAME(2, RK_PA6, 2, 0x308, BIT(16 + 12) | BIT(16 + 13) | BIT(13)), /* pdm-clkm2 */
+ RK_MUXROUTE_SAME(2, RK_PA4, 3, 0x600, BIT(16 + 2) | BIT(2)), /* pdm-clkm-m2 */
+ RK_MUXROUTE_SAME(3, RK_PB2, 3, 0x314, BIT(16 + 9)), /* spi1_miso */
+ RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x314, BIT(16 + 9) | BIT(9)), /* spi1_miso_m1 */
+ RK_MUXROUTE_SAME(0, RK_PB3, 3, 0x314, BIT(16 + 10) | BIT(16 + 11)), /* owire_m0 */
+ RK_MUXROUTE_SAME(1, RK_PC6, 7, 0x314, BIT(16 + 10) | BIT(16 + 11) | BIT(10)), /* owire_m1 */
+ RK_MUXROUTE_SAME(2, RK_PA2, 5, 0x314, BIT(16 + 10) | BIT(16 + 11) | BIT(11)), /* owire_m2 */
+ RK_MUXROUTE_SAME(0, RK_PB3, 2, 0x314, BIT(16 + 12) | BIT(16 + 13)), /* can_rxd_m0 */
+ RK_MUXROUTE_SAME(1, RK_PC6, 5, 0x314, BIT(16 + 12) | BIT(16 + 13) | BIT(12)), /* can_rxd_m1 */
+ RK_MUXROUTE_SAME(2, RK_PA2, 4, 0x314, BIT(16 + 12) | BIT(16 + 13) | BIT(13)), /* can_rxd_m2 */
+ RK_MUXROUTE_SAME(1, RK_PC4, 3, 0x314, BIT(16 + 14)), /* mac_rxd0_m0 */
+ RK_MUXROUTE_SAME(4, RK_PA2, 2, 0x314, BIT(16 + 14) | BIT(14)), /* mac_rxd0_m1 */
+ RK_MUXROUTE_SAME(3, RK_PB4, 4, 0x314, BIT(16 + 15)), /* uart3_rx */
+ RK_MUXROUTE_SAME(0, RK_PC1, 3, 0x314, BIT(16 + 15) | BIT(15)), /* uart3_rx_m1 */
+};
+
+static struct rockchip_mux_route_data rk3328_mux_route_data[] = {
+ RK_MUXROUTE_SAME(1, RK_PA1, 2, 0x50, BIT(16) | BIT(16 + 1)), /* uart2dbg_rxm0 */
+ RK_MUXROUTE_SAME(2, RK_PA1, 1, 0x50, BIT(16) | BIT(16 + 1) | BIT(0)), /* uart2dbg_rxm1 */
+ RK_MUXROUTE_SAME(1, RK_PB3, 2, 0x50, BIT(16 + 2) | BIT(2)), /* gmac-m1_rxd0 */
+ RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x50, BIT(16 + 10) | BIT(10)), /* gmac-m1-optimized_rxd3 */
+ RK_MUXROUTE_SAME(2, RK_PC3, 2, 0x50, BIT(16 + 3)), /* pdm_sdi0m0 */
+ RK_MUXROUTE_SAME(1, RK_PC7, 3, 0x50, BIT(16 + 3) | BIT(3)), /* pdm_sdi0m1 */
+ RK_MUXROUTE_SAME(3, RK_PA2, 4, 0x50, BIT(16 + 4) | BIT(16 + 5) | BIT(5)), /* spi_rxdm2 */
+ RK_MUXROUTE_SAME(1, RK_PD0, 1, 0x50, BIT(16 + 6)), /* i2s2_sdim0 */
+ RK_MUXROUTE_SAME(3, RK_PA2, 6, 0x50, BIT(16 + 6) | BIT(6)), /* i2s2_sdim1 */
+ RK_MUXROUTE_SAME(2, RK_PC6, 3, 0x50, BIT(16 + 7) | BIT(7)), /* card_iom1 */
+ RK_MUXROUTE_SAME(2, RK_PC0, 3, 0x50, BIT(16 + 8) | BIT(8)), /* tsp_d5m1 */
+ RK_MUXROUTE_SAME(2, RK_PC0, 4, 0x50, BIT(16 + 9) | BIT(9)), /* cif_data5m1 */
+};
+
+static struct rockchip_mux_route_data rk3399_mux_route_data[] = {
+ RK_MUXROUTE_SAME(4, RK_PB0, 2, 0xe21c, BIT(16 + 10) | BIT(16 + 11)), /* uart2dbga_rx */
+ RK_MUXROUTE_SAME(4, RK_PC0, 2, 0xe21c, BIT(16 + 10) | BIT(16 + 11) | BIT(10)), /* uart2dbgb_rx */
+ RK_MUXROUTE_SAME(4, RK_PC3, 1, 0xe21c, BIT(16 + 10) | BIT(16 + 11) | BIT(11)), /* uart2dbgc_rx */
+ RK_MUXROUTE_SAME(2, RK_PD2, 2, 0xe21c, BIT(16 + 14)), /* pcie_clkreqn */
+ RK_MUXROUTE_SAME(4, RK_PD0, 1, 0xe21c, BIT(16 + 14) | BIT(14)), /* pcie_clkreqnb */
+};
+
+static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
+ RK_MUXROUTE_PMU(0, RK_PB7, 1, 0x0110, WRITE_MASK_VAL(1, 0, 0)), /* PWM0 IO mux M0 */
+ RK_MUXROUTE_PMU(0, RK_PC7, 2, 0x0110, WRITE_MASK_VAL(1, 0, 1)), /* PWM0 IO mux M1 */
+ RK_MUXROUTE_PMU(0, RK_PC0, 1, 0x0110, WRITE_MASK_VAL(3, 2, 0)), /* PWM1 IO mux M0 */
+ RK_MUXROUTE_PMU(0, RK_PB5, 4, 0x0110, WRITE_MASK_VAL(3, 2, 1)), /* PWM1 IO mux M1 */
+ RK_MUXROUTE_PMU(0, RK_PC1, 1, 0x0110, WRITE_MASK_VAL(5, 4, 0)), /* PWM2 IO mux M0 */
+ RK_MUXROUTE_PMU(0, RK_PB6, 4, 0x0110, WRITE_MASK_VAL(5, 4, 1)), /* PWM2 IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PB3, 2, 0x0300, WRITE_MASK_VAL(0, 0, 0)), /* CAN0 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PA1, 4, 0x0300, WRITE_MASK_VAL(0, 0, 1)), /* CAN0 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PA1, 3, 0x0300, WRITE_MASK_VAL(2, 2, 0)), /* CAN1 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC3, 3, 0x0300, WRITE_MASK_VAL(2, 2, 1)), /* CAN1 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PB5, 3, 0x0300, WRITE_MASK_VAL(4, 4, 0)), /* CAN2 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PB2, 4, 0x0300, WRITE_MASK_VAL(4, 4, 1)), /* CAN2 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PC4, 1, 0x0300, WRITE_MASK_VAL(6, 6, 0)), /* HPDIN IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PC2, 2, 0x0300, WRITE_MASK_VAL(6, 6, 1)), /* HPDIN IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB1, 3, 0x0300, WRITE_MASK_VAL(8, 8, 0)), /* GMAC1 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PA7, 3, 0x0300, WRITE_MASK_VAL(8, 8, 1)), /* GMAC1 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PD1, 1, 0x0300, WRITE_MASK_VAL(10, 10, 0)), /* HDMITX IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PC7, 1, 0x0300, WRITE_MASK_VAL(10, 10, 1)), /* HDMITX IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PB6, 1, 0x0300, WRITE_MASK_VAL(14, 14, 0)), /* I2C2 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PB4, 1, 0x0300, WRITE_MASK_VAL(14, 14, 1)), /* I2C2 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PA0, 1, 0x0304, WRITE_MASK_VAL(0, 0, 0)), /* I2C3 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PB6, 4, 0x0304, WRITE_MASK_VAL(0, 0, 1)), /* I2C3 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PB2, 1, 0x0304, WRITE_MASK_VAL(2, 2, 0)), /* I2C4 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PB1, 2, 0x0304, WRITE_MASK_VAL(2, 2, 1)), /* I2C4 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB4, 4, 0x0304, WRITE_MASK_VAL(4, 4, 0)), /* I2C5 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PD0, 2, 0x0304, WRITE_MASK_VAL(4, 4, 1)), /* I2C5 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB1, 5, 0x0304, WRITE_MASK_VAL(14, 14, 0)), /* PWM8 IO mux M0 */
+ RK_MUXROUTE_GRF(1, RK_PD5, 4, 0x0304, WRITE_MASK_VAL(14, 14, 1)), /* PWM8 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB2, 5, 0x0308, WRITE_MASK_VAL(0, 0, 0)), /* PWM9 IO mux M0 */
+ RK_MUXROUTE_GRF(1, RK_PD6, 4, 0x0308, WRITE_MASK_VAL(0, 0, 1)), /* PWM9 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB5, 5, 0x0308, WRITE_MASK_VAL(2, 2, 0)), /* PWM10 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PA1, 2, 0x0308, WRITE_MASK_VAL(2, 2, 1)), /* PWM10 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB6, 5, 0x0308, WRITE_MASK_VAL(4, 4, 0)), /* PWM11 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC0, 3, 0x0308, WRITE_MASK_VAL(4, 4, 1)), /* PWM11 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PB7, 2, 0x0308, WRITE_MASK_VAL(6, 6, 0)), /* PWM12 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC5, 1, 0x0308, WRITE_MASK_VAL(6, 6, 1)), /* PWM12 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PC0, 2, 0x0308, WRITE_MASK_VAL(8, 8, 0)), /* PWM13 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC6, 1, 0x0308, WRITE_MASK_VAL(8, 8, 1)), /* PWM13 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PC4, 1, 0x0308, WRITE_MASK_VAL(10, 10, 0)), /* PWM14 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC2, 1, 0x0308, WRITE_MASK_VAL(10, 10, 1)), /* PWM14 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PC5, 1, 0x0308, WRITE_MASK_VAL(12, 12, 0)), /* PWM15 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC3, 1, 0x0308, WRITE_MASK_VAL(12, 12, 1)), /* PWM15 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PD2, 3, 0x0308, WRITE_MASK_VAL(14, 14, 0)), /* SDMMC2 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PA5, 5, 0x0308, WRITE_MASK_VAL(14, 14, 1)), /* SDMMC2 IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PB5, 2, 0x030c, WRITE_MASK_VAL(0, 0, 0)), /* SPI0 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PD3, 3, 0x030c, WRITE_MASK_VAL(0, 0, 1)), /* SPI0 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PB5, 3, 0x030c, WRITE_MASK_VAL(2, 2, 0)), /* SPI1 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PC3, 3, 0x030c, WRITE_MASK_VAL(2, 2, 1)), /* SPI1 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PC1, 4, 0x030c, WRITE_MASK_VAL(4, 4, 0)), /* SPI2 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PA0, 3, 0x030c, WRITE_MASK_VAL(4, 4, 1)), /* SPI2 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PB3, 4, 0x030c, WRITE_MASK_VAL(6, 6, 0)), /* SPI3 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC2, 2, 0x030c, WRITE_MASK_VAL(6, 6, 1)), /* SPI3 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PB4, 2, 0x030c, WRITE_MASK_VAL(8, 8, 0)), /* UART1 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PD6, 4, 0x030c, WRITE_MASK_VAL(8, 8, 1)), /* UART1 IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(10, 10, 0)), /* UART2 IO mux M0 */
+ RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x030c, WRITE_MASK_VAL(10, 10, 1)), /* UART2 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PA1, 2, 0x030c, WRITE_MASK_VAL(12, 12, 0)), /* UART3 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PB7, 4, 0x030c, WRITE_MASK_VAL(12, 12, 1)), /* UART3 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PA6, 2, 0x030c, WRITE_MASK_VAL(14, 14, 0)), /* UART4 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PB2, 4, 0x030c, WRITE_MASK_VAL(14, 14, 1)), /* UART4 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PA2, 3, 0x0310, WRITE_MASK_VAL(0, 0, 0)), /* UART5 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PC2, 4, 0x0310, WRITE_MASK_VAL(0, 0, 1)), /* UART5 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PA4, 3, 0x0310, WRITE_MASK_VAL(2, 2, 0)), /* UART6 IO mux M0 */
+ RK_MUXROUTE_GRF(1, RK_PD5, 3, 0x0310, WRITE_MASK_VAL(2, 2, 1)), /* UART6 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PA6, 3, 0x0310, WRITE_MASK_VAL(5, 4, 0)), /* UART7 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PC4, 4, 0x0310, WRITE_MASK_VAL(5, 4, 1)), /* UART7 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(5, 4, 2)), /* UART7 IO mux M2 */
+ RK_MUXROUTE_GRF(2, RK_PC5, 3, 0x0310, WRITE_MASK_VAL(6, 6, 0)), /* UART8 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PD7, 4, 0x0310, WRITE_MASK_VAL(6, 6, 1)), /* UART8 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PB0, 3, 0x0310, WRITE_MASK_VAL(9, 8, 0)), /* UART9 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC5, 4, 0x0310, WRITE_MASK_VAL(9, 8, 1)), /* UART9 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PA4, 4, 0x0310, WRITE_MASK_VAL(9, 8, 2)), /* UART9 IO mux M2 */
+ RK_MUXROUTE_GRF(1, RK_PA2, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PC6, 4, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 IO mux M1 */
+ RK_MUXROUTE_GRF(2, RK_PD0, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 IO mux M2 */
+ RK_MUXROUTE_GRF(2, RK_PC1, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PB6, 5, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(14, 14, 0)), /* I2S3 IO mux M0 */
+ RK_MUXROUTE_GRF(4, RK_PC2, 5, 0x0310, WRITE_MASK_VAL(14, 14, 1)), /* I2S3 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
+ RK_MUXROUTE_GRF(1, RK_PA6, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PD6, 5, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PA0, 4, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
+ RK_MUXROUTE_GRF(3, RK_PC4, 5, 0x0314, WRITE_MASK_VAL(1, 0, 2)), /* PDM IO mux M2 */
+ RK_MUXROUTE_GRF(0, RK_PA5, 3, 0x0314, WRITE_MASK_VAL(3, 2, 0)), /* PCIE20 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PD0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 1)), /* PCIE20 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PB0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 2)), /* PCIE20 IO mux M2 */
+ RK_MUXROUTE_GRF(0, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(5, 4, 0)), /* PCIE30X1 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PD2, 4, 0x0314, WRITE_MASK_VAL(5, 4, 1)), /* PCIE30X1 IO mux M1 */
+ RK_MUXROUTE_GRF(1, RK_PA5, 4, 0x0314, WRITE_MASK_VAL(5, 4, 2)), /* PCIE30X1 IO mux M2 */
+ RK_MUXROUTE_GRF(0, RK_PA6, 2, 0x0314, WRITE_MASK_VAL(7, 6, 0)), /* PCIE30X2 IO mux M0 */
+ RK_MUXROUTE_GRF(2, RK_PD4, 4, 0x0314, WRITE_MASK_VAL(7, 6, 1)), /* PCIE30X2 IO mux M1 */
+ RK_MUXROUTE_GRF(4, RK_PC2, 4, 0x0314, WRITE_MASK_VAL(7, 6, 2)), /* PCIE30X2 IO mux M2 */
};
-static struct rockchip_pin_bank *gc_to_rockchip_pinctrl(struct gpio_chip *gc)
+static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin,
+ int mux, u32 *loc, u32 *reg, u32 *value)
{
- struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct rockchip_mux_route_data *data = NULL;
+ int i;
+
+ for (i = 0; i < ctrl->niomux_routes; i++) {
+ data = &ctrl->iomux_routes[i];
+ if ((data->bank_num == bank->bank_num) &&
+ (data->pin == pin) && (data->func == mux))
+ break;
+ }
- return container_of(bgc, struct rockchip_pin_bank, bgpio_chip);
+ if (i >= ctrl->niomux_routes)
+ return false;
+
+ *loc = data->route_location;
+ *reg = data->route_offset;
+ *value = data->route_val;
+
+ return true;
}
-static int rockchip_gpiov2_direction_input(struct gpio_chip *gc, unsigned int gpio)
+static int rockchip_verify_mux(struct rockchip_pin_bank *bank,
+ int pin, int mux)
{
- struct rockchip_pin_bank *bank = gc_to_rockchip_pinctrl(gc);
- u32 mask;
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct device *dev = info->dev;
+ int iomux_num = (pin / 8);
- mask = 1 << (16 + (gpio % 16));
+ if (iomux_num > 3)
+ return -EINVAL;
- if (gpio < 16)
- writel(mask, bank->reg_base + RK_GPIOV2_DDR_L);
- else
- writel(mask, bank->reg_base + RK_GPIOV2_DDR_H);
+ if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) {
+ dev_err(dev, "pin %d is unrouted\n", pin);
+ return -EINVAL;
+ }
+
+ if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) {
+ if (mux != RK_FUNC_GPIO) {
+ dev_err(dev, "pin %d only supports a gpio mux\n", pin);
+ return -ENOTSUPP;
+ }
+ }
return 0;
}
-static int rockchip_gpiov2_get_direction(struct gpio_chip *gc, unsigned int gpio)
+/*
+ * Set a new mux function for a pin.
+ *
+ * The register is divided into the upper and lower 16 bit. When changing
+ * a value, the previous register value is not read and changed. Instead
+ * it seems the changed bits are marked in the upper 16 bit, while the
+ * changed value gets set in the same offset in the lower 16 bit.
+ * All pin settings seem to be 2 bit wide in both the upper and lower
+ * parts.
+ * @bank: pin bank to change
+ * @pin: pin to change
+ * @mux: new mux function to set
+ */
+static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
{
- struct rockchip_pin_bank *bank = gc_to_rockchip_pinctrl(gc);
- u32 r;
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct device *dev = info->dev;
+ int iomux_num = (pin / 8);
+ struct regmap *regmap;
+ int reg, ret, mask, mux_type;
+ u8 bit;
+ u32 data, rmask, route_location, route_reg, route_val;
+
+ ret = rockchip_verify_mux(bank, pin, mux);
+ if (ret < 0)
+ return ret;
- if (gpio < 16)
- r = readl(bank->reg_base + RK_GPIOV2_DDR_L);
+ if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY)
+ return 0;
+
+ dev_dbg(dev, "setting mux of GPIO%d-%d to %d\n", bank->bank_num, pin, mux);
+
+ if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
+ regmap = info->regmap_pmu;
+ else if (bank->iomux[iomux_num].type & IOMUX_L_SOURCE_PMU)
+ regmap = (pin % 8 < 4) ? info->regmap_pmu : info->regmap_base;
else
- r = readl(bank->reg_base + RK_GPIOV2_DDR_H);
+ regmap = info->regmap_base;
+
+ /* get basic quadrupel of mux registers and the correct reg inside */
+ mux_type = bank->iomux[iomux_num].type;
+ reg = bank->iomux[iomux_num].offset;
+ if (mux_type & IOMUX_WIDTH_4BIT) {
+ if ((pin % 8) >= 4)
+ reg += 0x4;
+ bit = (pin % 4) * 4;
+ mask = 0xf;
+ } else if (mux_type & IOMUX_WIDTH_3BIT) {
+ if ((pin % 8) >= 5)
+ reg += 0x4;
+ bit = (pin % 8 % 5) * 3;
+ mask = 0x7;
+ } else {
+ bit = (pin % 8) * 2;
+ mask = 0x3;
+ }
+
+ if (bank->recalced_mask & BIT(pin))
+ rockchip_get_recalced_mux(bank, pin, &reg, &bit, &mask);
+
+ if (ctrl->type == RK3588) {
+ if (bank->bank_num == 0) {
+ if ((pin >= RK_PB4) && (pin <= RK_PD7)) {
+ if (mux < 8) {
+ reg += 0x4000 - 0xC; /* PMU2_IOC_BASE */
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= (mux & mask) << bit;
+ ret = regmap_update_bits(regmap, reg, rmask, data);
+ } else {
+ u32 reg0 = 0;
+
+ reg0 = reg + 0x4000 - 0xC; /* PMU2_IOC_BASE */
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= 8 << bit;
+ ret = regmap_update_bits(regmap, reg0, rmask, data);
+
+ reg0 = reg + 0x8000; /* BUS_IOC_BASE */
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= mux << bit;
+ regmap = info->regmap_base;
+ ret |= regmap_update_bits(regmap, reg0, rmask, data);
+ }
+ } else {
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= (mux & mask) << bit;
+ ret = regmap_update_bits(regmap, reg, rmask, data);
+ }
+ return ret;
+ } else if (bank->bank_num > 0) {
+ reg += 0x8000; /* BUS_IOC_BASE */
+ }
+ }
+
+ if (mux > mask)
+ return -EINVAL;
+
+ if (bank->route_mask & BIT(pin)) {
+ if (rockchip_get_mux_route(bank, pin, mux, &route_location,
+ &route_reg, &route_val)) {
+ struct regmap *route_regmap = regmap;
+
+ /* handle special locations */
+ switch (route_location) {
+ case ROCKCHIP_ROUTE_PMU:
+ route_regmap = info->regmap_pmu;
+ break;
+ case ROCKCHIP_ROUTE_GRF:
+ route_regmap = info->regmap_base;
+ break;
+ }
- return r & BIT(gpio % 16) ? GPIOF_DIR_OUT : GPIOF_DIR_IN;
+ ret = regmap_write(route_regmap, route_reg, route_val);
+ if (ret)
+ return ret;
+ }
+ }
+
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= (mux & mask) << bit;
+ ret = regmap_update_bits(regmap, reg, rmask, data);
+
+ return ret;
}
-static void rockchip_gpiov2_set_value(struct gpio_chip *gc, unsigned int gpio,
- int val)
+#define PX30_PULL_PMU_OFFSET 0x10
+#define PX30_PULL_GRF_OFFSET 0x60
+#define PX30_PULL_BITS_PER_PIN 2
+#define PX30_PULL_PINS_PER_REG 8
+#define PX30_PULL_BANK_STRIDE 16
+
+static int px30_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
- struct rockchip_pin_bank *bank = gc_to_rockchip_pinctrl(gc);
- u32 mask, vval = 0;
+ struct rockchip_pinctrl *info = bank->drvdata;
- mask = 1 << (16 + (gpio % 16));
- if (val)
- vval = 1 << (gpio % 16);
+ /* The first 32 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = PX30_PULL_PMU_OFFSET;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = PX30_PULL_GRF_OFFSET;
- if (gpio < 16)
- writel(mask | vval, bank->reg_base + RK_GPIOV2_DR_L);
- else
- writel(mask | vval, bank->reg_base + RK_GPIOV2_DR_H);
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * PX30_PULL_BANK_STRIDE;
+ }
+
+ *reg += ((pin_num / PX30_PULL_PINS_PER_REG) * 4);
+ *bit = (pin_num % PX30_PULL_PINS_PER_REG);
+ *bit *= PX30_PULL_BITS_PER_PIN;
+
+ return 0;
}
-static int rockchip_gpiov2_direction_output(struct gpio_chip *gc,
- unsigned int gpio, int val)
+#define PX30_DRV_PMU_OFFSET 0x20
+#define PX30_DRV_GRF_OFFSET 0xf0
+#define PX30_DRV_BITS_PER_PIN 2
+#define PX30_DRV_PINS_PER_REG 8
+#define PX30_DRV_BANK_STRIDE 16
+
+static int px30_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
- struct rockchip_pin_bank *bank = gc_to_rockchip_pinctrl(gc);
- u32 mask, out, vval = 0;
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 32 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = PX30_DRV_PMU_OFFSET;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = PX30_DRV_GRF_OFFSET;
+
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * PX30_DRV_BANK_STRIDE;
+ }
+
+ *reg += ((pin_num / PX30_DRV_PINS_PER_REG) * 4);
+ *bit = (pin_num % PX30_DRV_PINS_PER_REG);
+ *bit *= PX30_DRV_BITS_PER_PIN;
- mask = 1 << (16 + (gpio % 16));
- out = 1 << (gpio % 16);
- if (val)
- vval = 1 << (gpio % 16);
+ return 0;
+}
+
+#define PX30_SCHMITT_PMU_OFFSET 0x38
+#define PX30_SCHMITT_GRF_OFFSET 0xc0
+#define PX30_SCHMITT_PINS_PER_PMU_REG 16
+#define PX30_SCHMITT_BANK_STRIDE 16
+#define PX30_SCHMITT_PINS_PER_GRF_REG 8
- if (gpio < 16) {
- writel(mask | vval, bank->reg_base + RK_GPIOV2_DR_L);
- writel(mask | out, bank->reg_base + RK_GPIOV2_DDR_L);
+static int px30_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ int pins_per_reg;
+
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = PX30_SCHMITT_PMU_OFFSET;
+ pins_per_reg = PX30_SCHMITT_PINS_PER_PMU_REG;
} else {
- writel(mask | vval, bank->reg_base + RK_GPIOV2_DR_H);
- writel(mask | out, bank->reg_base + RK_GPIOV2_DDR_H);
+ *regmap = info->regmap_base;
+ *reg = PX30_SCHMITT_GRF_OFFSET;
+ pins_per_reg = PX30_SCHMITT_PINS_PER_GRF_REG;
+ *reg += (bank->bank_num - 1) * PX30_SCHMITT_BANK_STRIDE;
}
+ *reg += ((pin_num / pins_per_reg) * 4);
+ *bit = pin_num % pins_per_reg;
+
return 0;
}
-static int rockchip_gpiov2_get_value(struct gpio_chip *gc, unsigned int gpio)
+#define RV1108_PULL_PMU_OFFSET 0x10
+#define RV1108_PULL_OFFSET 0x110
+#define RV1108_PULL_PINS_PER_REG 8
+#define RV1108_PULL_BITS_PER_PIN 2
+#define RV1108_PULL_BANK_STRIDE 16
+
+static int rv1108_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
- struct rockchip_pin_bank *bank = gc_to_rockchip_pinctrl(gc);
- u32 mask, r;
+ struct rockchip_pinctrl *info = bank->drvdata;
- mask = 1 << (gpio % 16);
+ /* The first 24 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RV1108_PULL_PMU_OFFSET;
+ } else {
+ *reg = RV1108_PULL_OFFSET;
+ *regmap = info->regmap_base;
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * RV1108_PULL_BANK_STRIDE;
+ }
- if (gpio < 16)
- r = readl(bank->reg_base + RK_GPIOV2_DR_L);
- else
- r = readl(bank->reg_base + RK_GPIOV2_DR_L);
+ *reg += ((pin_num / RV1108_PULL_PINS_PER_REG) * 4);
+ *bit = (pin_num % RV1108_PULL_PINS_PER_REG);
+ *bit *= RV1108_PULL_BITS_PER_PIN;
- return r & mask ? 1 : 0;
+ return 0;
}
-static struct gpio_ops rockchip_gpio_ops = {
- .direction_input = rockchip_gpiov2_direction_input,
- .direction_output = rockchip_gpiov2_direction_output,
- .get = rockchip_gpiov2_get_value,
- .set = rockchip_gpiov2_set_value,
- .get_direction = rockchip_gpiov2_get_direction,
-};
+#define RV1108_DRV_PMU_OFFSET 0x20
+#define RV1108_DRV_GRF_OFFSET 0x210
+#define RV1108_DRV_BITS_PER_PIN 2
+#define RV1108_DRV_PINS_PER_REG 8
+#define RV1108_DRV_BANK_STRIDE 16
-static int rockchip_gpio_probe(struct device_d *dev)
+static int rv1108_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
- struct rockchip_pinctrl *info = dev->parent->priv;
- struct rockchip_pin_ctrl *ctrl = info->ctrl;
- struct rockchip_pin_bank *bank;
- struct gpio_chip *gpio;
- void __iomem *reg_base;
- int ret, bankno;
-
- bankno = of_alias_get_id(dev->device_node, "gpio");
- if (bankno >= ctrl->nr_banks)
- bankno = -EINVAL;
- if (bankno < 0)
- return bankno;
-
- bank = &ctrl->pin_banks[bankno];
- gpio = &bank->bgpio_chip.gc;
-
- if (!bank->valid)
- dev_warn(dev, "bank %s is not valid\n", bank->name);
-
- reg_base = bank->reg_base;
-
- if (ctrl->type == RK3568) {
- gpio->ngpio = 32;
- gpio->dev = dev;
- gpio->ops = &rockchip_gpio_ops;
- gpio->base = bankno;
- if (gpio->base < 0)
- return -EINVAL;
- gpio->base *= 32;
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 24 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RV1108_DRV_PMU_OFFSET;
} else {
- ret = bgpio_init(&bank->bgpio_chip, dev, 4,
- reg_base + RK_GPIO_EXT_PORT,
- reg_base + RK_GPIO_SWPORT_DR, NULL,
- reg_base + RK_GPIO_SWPORT_DDR, NULL, 0);
- if (ret)
- return ret;
+ *regmap = info->regmap_base;
+ *reg = RV1108_DRV_GRF_OFFSET;
+
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * RV1108_DRV_BANK_STRIDE;
}
- bank->bgpio_chip.gc.dev = dev;
+ *reg += ((pin_num / RV1108_DRV_PINS_PER_REG) * 4);
+ *bit = pin_num % RV1108_DRV_PINS_PER_REG;
+ *bit *= RV1108_DRV_BITS_PER_PIN;
- bank->bgpio_chip.gc.ngpio = bank->nr_pins;
- ret = gpiochip_add(&bank->bgpio_chip.gc);
- if (ret) {
- dev_err(dev, "failed to register gpio_chip %s, error code: %d\n",
- bank->name, ret);
- return ret;
+ return 0;
+}
+
+#define RV1108_SCHMITT_PMU_OFFSET 0x30
+#define RV1108_SCHMITT_GRF_OFFSET 0x388
+#define RV1108_SCHMITT_BANK_STRIDE 8
+#define RV1108_SCHMITT_PINS_PER_GRF_REG 16
+#define RV1108_SCHMITT_PINS_PER_PMU_REG 8
+
+static int rv1108_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ int pins_per_reg;
+
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RV1108_SCHMITT_PMU_OFFSET;
+ pins_per_reg = RV1108_SCHMITT_PINS_PER_PMU_REG;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RV1108_SCHMITT_GRF_OFFSET;
+ pins_per_reg = RV1108_SCHMITT_PINS_PER_GRF_REG;
+ *reg += (bank->bank_num - 1) * RV1108_SCHMITT_BANK_STRIDE;
}
+ *reg += ((pin_num / pins_per_reg) * 4);
+ *bit = pin_num % pins_per_reg;
return 0;
}
-static struct rockchip_pinctrl *to_rockchip_pinctrl(struct pinctrl_device *pdev)
+#define RV1126_PULL_PMU_OFFSET 0x40
+#define RV1126_PULL_GRF_GPIO1A0_OFFSET 0x10108
+#define RV1126_PULL_PINS_PER_REG 8
+#define RV1126_PULL_BITS_PER_PIN 2
+#define RV1126_PULL_BANK_STRIDE 16
+#define RV1126_GPIO_C4_D7(p) (p >= 20 && p <= 31) /* GPIO0_C4 ~ GPIO0_D7 */
+
+static int rv1126_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
- return container_of(pdev, struct rockchip_pinctrl, pctl_dev);
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 24 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ if (RV1126_GPIO_C4_D7(pin_num)) {
+ *regmap = info->regmap_base;
+ *reg = RV1126_PULL_GRF_GPIO1A0_OFFSET;
+ *reg -= (((31 - pin_num) / RV1126_PULL_PINS_PER_REG + 1) * 4);
+ *bit = pin_num % RV1126_PULL_PINS_PER_REG;
+ *bit *= RV1126_PULL_BITS_PER_PIN;
+ return 0;
+ }
+ *regmap = info->regmap_pmu;
+ *reg = RV1126_PULL_PMU_OFFSET;
+ } else {
+ *reg = RV1126_PULL_GRF_GPIO1A0_OFFSET;
+ *regmap = info->regmap_base;
+ *reg += (bank->bank_num - 1) * RV1126_PULL_BANK_STRIDE;
+ }
+
+ *reg += ((pin_num / RV1126_PULL_PINS_PER_REG) * 4);
+ *bit = (pin_num % RV1126_PULL_PINS_PER_REG);
+ *bit *= RV1126_PULL_BITS_PER_PIN;
+
+ return 0;
}
-static struct rockchip_pin_bank *bank_num_to_bank(struct rockchip_pinctrl *info,
- unsigned num)
+#define RV1126_DRV_PMU_OFFSET 0x20
+#define RV1126_DRV_GRF_GPIO1A0_OFFSET 0x10090
+#define RV1126_DRV_BITS_PER_PIN 4
+#define RV1126_DRV_PINS_PER_REG 4
+#define RV1126_DRV_BANK_STRIDE 32
+
+static int rv1126_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
- struct rockchip_pin_bank *b = info->ctrl->pin_banks;
- int i;
+ struct rockchip_pinctrl *info = bank->drvdata;
- for (i = 0; i < info->ctrl->nr_banks; i++, b++) {
- if (b->bank_num == num)
- return b;
+ /* The first 24 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ if (RV1126_GPIO_C4_D7(pin_num)) {
+ *regmap = info->regmap_base;
+ *reg = RV1126_DRV_GRF_GPIO1A0_OFFSET;
+ *reg -= (((31 - pin_num) / RV1126_DRV_PINS_PER_REG + 1) * 4);
+ *reg -= 0x4;
+ *bit = pin_num % RV1126_DRV_PINS_PER_REG;
+ *bit *= RV1126_DRV_BITS_PER_PIN;
+ return 0;
+ }
+ *regmap = info->regmap_pmu;
+ *reg = RV1126_DRV_PMU_OFFSET;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RV1126_DRV_GRF_GPIO1A0_OFFSET;
+ *reg += (bank->bank_num - 1) * RV1126_DRV_BANK_STRIDE;
}
- return ERR_PTR(-EINVAL);
+ *reg += ((pin_num / RV1126_DRV_PINS_PER_REG) * 4);
+ *bit = pin_num % RV1126_DRV_PINS_PER_REG;
+ *bit *= RV1126_DRV_BITS_PER_PIN;
+
+ return 0;
}
-static int parse_bias_config(struct device_node *np)
+#define RV1126_SCHMITT_PMU_OFFSET 0x60
+#define RV1126_SCHMITT_GRF_GPIO1A0_OFFSET 0x10188
+#define RV1126_SCHMITT_BANK_STRIDE 16
+#define RV1126_SCHMITT_PINS_PER_GRF_REG 8
+#define RV1126_SCHMITT_PINS_PER_PMU_REG 8
+
+static int rv1126_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
{
- u32 val;
+ struct rockchip_pinctrl *info = bank->drvdata;
+ int pins_per_reg;
- if (of_property_read_u32(np, "bias-pull-up", &val) != -EINVAL)
- return RK_BIAS_PULL_UP;
- else if (of_property_read_u32(np, "bias-pull-down", &val) != -EINVAL)
- return RK_BIAS_PULL_DOWN;
- else if (of_property_read_u32(np, "bias-bus-hold", &val) != -EINVAL)
- return RK_BIAS_BUS_HOLD;
- else
- return RK_BIAS_DISABLE;
+ if (bank->bank_num == 0) {
+ if (RV1126_GPIO_C4_D7(pin_num)) {
+ *regmap = info->regmap_base;
+ *reg = RV1126_SCHMITT_GRF_GPIO1A0_OFFSET;
+ *reg -= (((31 - pin_num) / RV1126_SCHMITT_PINS_PER_GRF_REG + 1) * 4);
+ *bit = pin_num % RV1126_SCHMITT_PINS_PER_GRF_REG;
+ return 0;
+ }
+ *regmap = info->regmap_pmu;
+ *reg = RV1126_SCHMITT_PMU_OFFSET;
+ pins_per_reg = RV1126_SCHMITT_PINS_PER_PMU_REG;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RV1126_SCHMITT_GRF_GPIO1A0_OFFSET;
+ pins_per_reg = RV1126_SCHMITT_PINS_PER_GRF_REG;
+ *reg += (bank->bank_num - 1) * RV1126_SCHMITT_BANK_STRIDE;
+ }
+ *reg += ((pin_num / pins_per_reg) * 4);
+ *bit = pin_num % pins_per_reg;
+
+ return 0;
}
+#define RK3308_SCHMITT_PINS_PER_REG 8
+#define RK3308_SCHMITT_BANK_STRIDE 16
+#define RK3308_SCHMITT_GRF_OFFSET 0x1a0
+
+static int rk3308_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3308_SCHMITT_GRF_OFFSET;
+
+ *reg += bank->bank_num * RK3308_SCHMITT_BANK_STRIDE;
+ *reg += ((pin_num / RK3308_SCHMITT_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3308_SCHMITT_PINS_PER_REG;
+
+ return 0;
+}
#define RK2928_PULL_OFFSET 0x118
#define RK2928_PULL_PINS_PER_REG 16
#define RK2928_PULL_BANK_STRIDE 8
-static void rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
- int pin_num, void __iomem **reg,
- u8 *bit)
+static int rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
- *reg = info->reg_base + RK2928_PULL_OFFSET;
+ *regmap = info->regmap_base;
+ *reg = RK2928_PULL_OFFSET;
*reg += bank->bank_num * RK2928_PULL_BANK_STRIDE;
*reg += (pin_num / RK2928_PULL_PINS_PER_REG) * 4;
*bit = pin_num % RK2928_PULL_PINS_PER_REG;
+
+ return 0;
};
+#define RK3128_PULL_OFFSET 0x118
+
+static int rk3128_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3128_PULL_OFFSET;
+ *reg += bank->bank_num * RK2928_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK2928_PULL_PINS_PER_REG) * 4);
+
+ *bit = pin_num % RK2928_PULL_PINS_PER_REG;
+
+ return 0;
+}
+
#define RK3188_PULL_OFFSET 0x164
#define RK3188_PULL_BITS_PER_PIN 2
#define RK3188_PULL_PINS_PER_REG 8
#define RK3188_PULL_BANK_STRIDE 16
#define RK3188_PULL_PMU_OFFSET 0x64
-static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
- int pin_num, void __iomem **reg,
- u8 *bit)
+static int rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
/* The first 12 pins of the first bank are located elsewhere */
- if (bank->bank_type == RK3188_BANK0 && pin_num < 12) {
- *reg = info->reg_pmu + RK3188_PULL_PMU_OFFSET +
- ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+ if (bank->bank_num == 0 && pin_num < 12) {
+ *regmap = info->regmap_pmu ? info->regmap_pmu
+ : bank->regmap_pull;
+ *reg = info->regmap_pmu ? RK3188_PULL_PMU_OFFSET : 0;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
*bit = pin_num % RK3188_PULL_PINS_PER_REG;
*bit *= RK3188_PULL_BITS_PER_PIN;
} else {
- *reg = info->reg_base + RK3188_PULL_OFFSET - 4;
+ *regmap = info->regmap_pull ? info->regmap_pull
+ : info->regmap_base;
+ *reg = info->regmap_pull ? 0 : RK3188_PULL_OFFSET;
+
+ /* correct the offset, as it is the 2nd pull register */
+ *reg -= 4;
*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
@@ -459,6 +1464,276 @@ static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
*bit = 7 - (pin_num % RK3188_PULL_PINS_PER_REG);
*bit *= RK3188_PULL_BITS_PER_PIN;
}
+
+ return 0;
+}
+
+#define RK3288_PULL_OFFSET 0x140
+static int rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 24 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RK3188_PULL_PMU_OFFSET;
+
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3188_PULL_PINS_PER_REG;
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RK3288_PULL_OFFSET;
+
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ }
+
+ return 0;
+}
+
+#define RK3288_DRV_PMU_OFFSET 0x70
+#define RK3288_DRV_GRF_OFFSET 0x1c0
+#define RK3288_DRV_BITS_PER_PIN 2
+#define RK3288_DRV_PINS_PER_REG 8
+#define RK3288_DRV_BANK_STRIDE 16
+
+static int rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 24 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RK3288_DRV_PMU_OFFSET;
+
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3288_DRV_PINS_PER_REG;
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RK3288_DRV_GRF_OFFSET;
+
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE;
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3288_DRV_PINS_PER_REG);
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+ }
+
+ return 0;
+}
+
+#define RK3228_PULL_OFFSET 0x100
+
+static int rk3228_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3228_PULL_OFFSET;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+
+ return 0;
+}
+
+#define RK3228_DRV_GRF_OFFSET 0x200
+
+static int rk3228_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3228_DRV_GRF_OFFSET;
+ *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE;
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3288_DRV_PINS_PER_REG);
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+
+ return 0;
+}
+
+#define RK3308_PULL_OFFSET 0xa0
+
+static int rk3308_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3308_PULL_OFFSET;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+
+ return 0;
+}
+
+#define RK3308_DRV_GRF_OFFSET 0x100
+
+static int rk3308_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3308_DRV_GRF_OFFSET;
+ *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE;
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3288_DRV_PINS_PER_REG);
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+
+ return 0;
+}
+
+#define RK3368_PULL_GRF_OFFSET 0x100
+#define RK3368_PULL_PMU_OFFSET 0x10
+
+static int rk3368_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 32 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RK3368_PULL_PMU_OFFSET;
+
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3188_PULL_PINS_PER_REG;
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RK3368_PULL_GRF_OFFSET;
+
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ }
+
+ return 0;
+}
+
+#define RK3368_DRV_PMU_OFFSET 0x20
+#define RK3368_DRV_GRF_OFFSET 0x200
+
+static int rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 32 pins of the first bank are located in PMU */
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RK3368_DRV_PMU_OFFSET;
+
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3288_DRV_PINS_PER_REG;
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RK3368_DRV_GRF_OFFSET;
+
+ /* correct the offset, as we're starting with the 2nd bank */
+ *reg -= 0x10;
+ *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE;
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3288_DRV_PINS_PER_REG);
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+ }
+
+ return 0;
+}
+
+#define RK3399_PULL_GRF_OFFSET 0xe040
+#define RK3399_PULL_PMU_OFFSET 0x40
+#define RK3399_DRV_3BITS_PER_PIN 3
+
+static int rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The bank0:16 and bank1:32 pins are located in PMU */
+ if ((bank->bank_num == 0) || (bank->bank_num == 1)) {
+ *regmap = info->regmap_pmu;
+ *reg = RK3399_PULL_PMU_OFFSET;
+
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3188_PULL_PINS_PER_REG;
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RK3399_PULL_GRF_OFFSET;
+
+ /* correct the offset, as we're starting with the 3rd bank */
+ *reg -= 0x20;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ }
+
+ return 0;
+}
+
+static int rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ int drv_num = (pin_num / 8);
+
+ /* The bank0:16 and bank1:32 pins are located in PMU */
+ if ((bank->bank_num == 0) || (bank->bank_num == 1))
+ *regmap = info->regmap_pmu;
+ else
+ *regmap = info->regmap_base;
+
+ *reg = bank->drv[drv_num].offset;
+ if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+ (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY))
+ *bit = (pin_num % 8) * 3;
+ else
+ *bit = (pin_num % 8) * 2;
+
+ return 0;
}
#define RK3568_PULL_PMU_OFFSET 0x20
@@ -467,27 +1742,31 @@ static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
#define RK3568_PULL_PINS_PER_REG 8
#define RK3568_PULL_BANK_STRIDE 0x10
-static void rk3568_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
- int pin_num, void __iomem **reg,
- u8 *bit)
+static int rk3568_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
if (bank->bank_num == 0) {
- *reg = info->reg_pmu + RK3568_PULL_PMU_OFFSET;
+ *regmap = info->regmap_pmu;
+ *reg = RK3568_PULL_PMU_OFFSET;
*reg += bank->bank_num * RK3568_PULL_BANK_STRIDE;
*reg += ((pin_num / RK3568_PULL_PINS_PER_REG) * 4);
*bit = pin_num % RK3568_PULL_PINS_PER_REG;
*bit *= RK3568_PULL_BITS_PER_PIN;
} else {
- *reg = info->reg_base + RK3568_PULL_GRF_OFFSET;
+ *regmap = info->regmap_base;
+ *reg = RK3568_PULL_GRF_OFFSET;
*reg += (bank->bank_num - 1) * RK3568_PULL_BANK_STRIDE;
*reg += ((pin_num / RK3568_PULL_PINS_PER_REG) * 4);
*bit = (pin_num % RK3568_PULL_PINS_PER_REG);
*bit *= RK3568_PULL_BITS_PER_PIN;
}
+
+ return 0;
}
#define RK3568_DRV_PMU_OFFSET 0x70
@@ -496,168 +1775,212 @@ static void rk3568_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
#define RK3568_DRV_PINS_PER_REG 2
#define RK3568_DRV_BANK_STRIDE 0x40
-static void rk3568_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
- int pin_num, void __iomem **reg, u8 *bit)
+static int rk3568_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
/* The first 32 pins of the first bank are located in PMU */
if (bank->bank_num == 0) {
- *reg = info->reg_pmu + RK3568_DRV_PMU_OFFSET;
+ *regmap = info->regmap_pmu;
+ *reg = RK3568_DRV_PMU_OFFSET;
*reg += ((pin_num / RK3568_DRV_PINS_PER_REG) * 4);
*bit = pin_num % RK3568_DRV_PINS_PER_REG;
*bit *= RK3568_DRV_BITS_PER_PIN;
} else {
- *reg = info->reg_base + RK3568_DRV_GRF_OFFSET;
+ *regmap = info->regmap_base;
+ *reg = RK3568_DRV_GRF_OFFSET;
*reg += (bank->bank_num - 1) * RK3568_DRV_BANK_STRIDE;
*reg += ((pin_num / RK3568_DRV_PINS_PER_REG) * 4);
*bit = (pin_num % RK3568_DRV_PINS_PER_REG);
*bit *= RK3568_DRV_BITS_PER_PIN;
}
+
+ return 0;
}
-static struct rockchip_mux_route_data rk3188_mux_route_data[] = {
- RK_MUXROUTE_SAME(0, RK_PD0, 1, 0xa0, BIT(16 + 11)), /* non-iomuxed emmc/flash pins on flash-dqs */
- RK_MUXROUTE_SAME(0, RK_PD0, 2, 0xa0, BIT(16 + 11) | BIT(11)), /* non-iomuxed emmc/flash pins on emmc-clk */
+#define RK3588_PMU1_IOC_REG (0x0000)
+#define RK3588_PMU2_IOC_REG (0x4000)
+#define RK3588_BUS_IOC_REG (0x8000)
+#define RK3588_VCCIO1_4_IOC_REG (0x9000)
+#define RK3588_VCCIO3_5_IOC_REG (0xA000)
+#define RK3588_VCCIO2_IOC_REG (0xB000)
+#define RK3588_VCCIO6_IOC_REG (0xC000)
+#define RK3588_EMMC_IOC_REG (0xD000)
+
+static const u32 rk3588_ds_regs[][2] = {
+ {RK_GPIO0_A0, RK3588_PMU1_IOC_REG + 0x0010},
+ {RK_GPIO0_A4, RK3588_PMU1_IOC_REG + 0x0014},
+ {RK_GPIO0_B0, RK3588_PMU1_IOC_REG + 0x0018},
+ {RK_GPIO0_B4, RK3588_PMU2_IOC_REG + 0x0014},
+ {RK_GPIO0_C0, RK3588_PMU2_IOC_REG + 0x0018},
+ {RK_GPIO0_C4, RK3588_PMU2_IOC_REG + 0x001C},
+ {RK_GPIO0_D0, RK3588_PMU2_IOC_REG + 0x0020},
+ {RK_GPIO0_D4, RK3588_PMU2_IOC_REG + 0x0024},
+ {RK_GPIO1_A0, RK3588_VCCIO1_4_IOC_REG + 0x0020},
+ {RK_GPIO1_A4, RK3588_VCCIO1_4_IOC_REG + 0x0024},
+ {RK_GPIO1_B0, RK3588_VCCIO1_4_IOC_REG + 0x0028},
+ {RK_GPIO1_B4, RK3588_VCCIO1_4_IOC_REG + 0x002C},
+ {RK_GPIO1_C0, RK3588_VCCIO1_4_IOC_REG + 0x0030},
+ {RK_GPIO1_C4, RK3588_VCCIO1_4_IOC_REG + 0x0034},
+ {RK_GPIO1_D0, RK3588_VCCIO1_4_IOC_REG + 0x0038},
+ {RK_GPIO1_D4, RK3588_VCCIO1_4_IOC_REG + 0x003C},
+ {RK_GPIO2_A0, RK3588_EMMC_IOC_REG + 0x0040},
+ {RK_GPIO2_A4, RK3588_VCCIO3_5_IOC_REG + 0x0044},
+ {RK_GPIO2_B0, RK3588_VCCIO3_5_IOC_REG + 0x0048},
+ {RK_GPIO2_B4, RK3588_VCCIO3_5_IOC_REG + 0x004C},
+ {RK_GPIO2_C0, RK3588_VCCIO3_5_IOC_REG + 0x0050},
+ {RK_GPIO2_C4, RK3588_VCCIO3_5_IOC_REG + 0x0054},
+ {RK_GPIO2_D0, RK3588_EMMC_IOC_REG + 0x0058},
+ {RK_GPIO2_D4, RK3588_EMMC_IOC_REG + 0x005C},
+ {RK_GPIO3_A0, RK3588_VCCIO3_5_IOC_REG + 0x0060},
+ {RK_GPIO3_A4, RK3588_VCCIO3_5_IOC_REG + 0x0064},
+ {RK_GPIO3_B0, RK3588_VCCIO3_5_IOC_REG + 0x0068},
+ {RK_GPIO3_B4, RK3588_VCCIO3_5_IOC_REG + 0x006C},
+ {RK_GPIO3_C0, RK3588_VCCIO3_5_IOC_REG + 0x0070},
+ {RK_GPIO3_C4, RK3588_VCCIO3_5_IOC_REG + 0x0074},
+ {RK_GPIO3_D0, RK3588_VCCIO3_5_IOC_REG + 0x0078},
+ {RK_GPIO3_D4, RK3588_VCCIO3_5_IOC_REG + 0x007C},
+ {RK_GPIO4_A0, RK3588_VCCIO6_IOC_REG + 0x0080},
+ {RK_GPIO4_A4, RK3588_VCCIO6_IOC_REG + 0x0084},
+ {RK_GPIO4_B0, RK3588_VCCIO6_IOC_REG + 0x0088},
+ {RK_GPIO4_B4, RK3588_VCCIO6_IOC_REG + 0x008C},
+ {RK_GPIO4_C0, RK3588_VCCIO6_IOC_REG + 0x0090},
+ {RK_GPIO4_C2, RK3588_VCCIO3_5_IOC_REG + 0x0090},
+ {RK_GPIO4_C4, RK3588_VCCIO3_5_IOC_REG + 0x0094},
+ {RK_GPIO4_D0, RK3588_VCCIO2_IOC_REG + 0x0098},
+ {RK_GPIO4_D4, RK3588_VCCIO2_IOC_REG + 0x009C},
};
-static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin,
- int mux, u32 *loc, u32 *reg, u32 *value)
+static const u32 rk3588_p_regs[][2] = {
+ {RK_GPIO0_A0, RK3588_PMU1_IOC_REG + 0x0020},
+ {RK_GPIO0_B0, RK3588_PMU1_IOC_REG + 0x0024},
+ {RK_GPIO0_B5, RK3588_PMU2_IOC_REG + 0x0028},
+ {RK_GPIO0_C0, RK3588_PMU2_IOC_REG + 0x002C},
+ {RK_GPIO0_D0, RK3588_PMU2_IOC_REG + 0x0030},
+ {RK_GPIO1_A0, RK3588_VCCIO1_4_IOC_REG + 0x0110},
+ {RK_GPIO1_B0, RK3588_VCCIO1_4_IOC_REG + 0x0114},
+ {RK_GPIO1_C0, RK3588_VCCIO1_4_IOC_REG + 0x0118},
+ {RK_GPIO1_D0, RK3588_VCCIO1_4_IOC_REG + 0x011C},
+ {RK_GPIO2_A0, RK3588_EMMC_IOC_REG + 0x0120},
+ {RK_GPIO2_A6, RK3588_VCCIO3_5_IOC_REG + 0x0120},
+ {RK_GPIO2_B0, RK3588_VCCIO3_5_IOC_REG + 0x0124},
+ {RK_GPIO2_C0, RK3588_VCCIO3_5_IOC_REG + 0x0128},
+ {RK_GPIO2_D0, RK3588_EMMC_IOC_REG + 0x012C},
+ {RK_GPIO3_A0, RK3588_VCCIO3_5_IOC_REG + 0x0130},
+ {RK_GPIO3_B0, RK3588_VCCIO3_5_IOC_REG + 0x0134},
+ {RK_GPIO3_C0, RK3588_VCCIO3_5_IOC_REG + 0x0138},
+ {RK_GPIO3_D0, RK3588_VCCIO3_5_IOC_REG + 0x013C},
+ {RK_GPIO4_A0, RK3588_VCCIO6_IOC_REG + 0x0140},
+ {RK_GPIO4_B0, RK3588_VCCIO6_IOC_REG + 0x0144},
+ {RK_GPIO4_C0, RK3588_VCCIO6_IOC_REG + 0x0148},
+ {RK_GPIO4_C2, RK3588_VCCIO3_5_IOC_REG + 0x0148},
+ {RK_GPIO4_D0, RK3588_VCCIO2_IOC_REG + 0x014C},
+};
+
+static const u32 rk3588_smt_regs[][2] = {
+ {RK_GPIO0_A0, RK3588_PMU1_IOC_REG + 0x0030},
+ {RK_GPIO0_B0, RK3588_PMU1_IOC_REG + 0x0034},
+ {RK_GPIO0_B5, RK3588_PMU2_IOC_REG + 0x0040},
+ {RK_GPIO0_C0, RK3588_PMU2_IOC_REG + 0x0044},
+ {RK_GPIO0_D0, RK3588_PMU2_IOC_REG + 0x0048},
+ {RK_GPIO1_A0, RK3588_VCCIO1_4_IOC_REG + 0x0210},
+ {RK_GPIO1_B0, RK3588_VCCIO1_4_IOC_REG + 0x0214},
+ {RK_GPIO1_C0, RK3588_VCCIO1_4_IOC_REG + 0x0218},
+ {RK_GPIO1_D0, RK3588_VCCIO1_4_IOC_REG + 0x021C},
+ {RK_GPIO2_A0, RK3588_EMMC_IOC_REG + 0x0220},
+ {RK_GPIO2_A6, RK3588_VCCIO3_5_IOC_REG + 0x0220},
+ {RK_GPIO2_B0, RK3588_VCCIO3_5_IOC_REG + 0x0224},
+ {RK_GPIO2_C0, RK3588_VCCIO3_5_IOC_REG + 0x0228},
+ {RK_GPIO2_D0, RK3588_EMMC_IOC_REG + 0x022C},
+ {RK_GPIO3_A0, RK3588_VCCIO3_5_IOC_REG + 0x0230},
+ {RK_GPIO3_B0, RK3588_VCCIO3_5_IOC_REG + 0x0234},
+ {RK_GPIO3_C0, RK3588_VCCIO3_5_IOC_REG + 0x0238},
+ {RK_GPIO3_D0, RK3588_VCCIO3_5_IOC_REG + 0x023C},
+ {RK_GPIO4_A0, RK3588_VCCIO6_IOC_REG + 0x0240},
+ {RK_GPIO4_B0, RK3588_VCCIO6_IOC_REG + 0x0244},
+ {RK_GPIO4_C0, RK3588_VCCIO6_IOC_REG + 0x0248},
+ {RK_GPIO4_C2, RK3588_VCCIO3_5_IOC_REG + 0x0248},
+ {RK_GPIO4_D0, RK3588_VCCIO2_IOC_REG + 0x024C},
+};
+
+#define RK3588_PULL_BITS_PER_PIN 2
+#define RK3588_PULL_PINS_PER_REG 8
+
+static int rk3588_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
- struct rockchip_pin_ctrl *ctrl = info->ctrl;
- struct rockchip_mux_route_data *data;
+ u8 bank_num = bank->bank_num;
+ u32 pin = bank_num * 32 + pin_num;
int i;
- for (i = 0; i < ctrl->niomux_routes; i++) {
- data = &ctrl->iomux_routes[i];
- if ((data->bank_num == bank->bank_num) &&
- (data->pin == pin) && (data->func == mux))
- break;
+ for (i = ARRAY_SIZE(rk3588_p_regs) - 1; i >= 0; i--) {
+ if (pin >= rk3588_p_regs[i][0]) {
+ *reg = rk3588_p_regs[i][1];
+ *regmap = info->regmap_base;
+ *bit = pin_num % RK3588_PULL_PINS_PER_REG;
+ *bit *= RK3588_PULL_BITS_PER_PIN;
+ return 0;
+ }
}
- if (i >= ctrl->niomux_routes)
- return false;
-
- *loc = data->route_location;
- *reg = data->route_offset;
- *value = data->route_val;
-
- return true;
+ return -EINVAL;
}
-static int rockchip_pinctrl_set_func(struct rockchip_pin_bank *bank, int pin,
- int mux)
+#define RK3588_DRV_BITS_PER_PIN 4
+#define RK3588_DRV_PINS_PER_REG 4
+
+static int rk3588_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
- void __iomem *base, *reg;
- u8 bit;
- u32 data, route_location, route_reg, route_val;
- int iomux_num = (pin / 8);
- int mux_type;
- int mask;
-
- base = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
- ? info->reg_pmu : info->reg_base;
-
- /* get basic quadrupel of mux registers and the correct reg inside */
- mux_type = bank->iomux[iomux_num].type;
- reg = base + bank->iomux[iomux_num].offset;
-
- dev_dbg(info->pctl_dev.dev, "setting func of GPIO%d-%d to %d\n",
- bank->bank_num, pin, mux);
-
- if (mux_type & IOMUX_WIDTH_4BIT) {
- if ((pin % 8) >= 4)
- reg += 0x4;
- bit = (pin % 4) * 4;
- mask = 0xf;
- } else if (mux_type & IOMUX_WIDTH_3BIT) {
- if ((pin % 8) >= 5)
- reg += 0x4;
- bit = (pin % 8 % 5) * 3;
- mask = 0x7;
- } else {
- bit = (pin % 8) * 2;
- mask = 0x3;
- }
-
- if (bank->route_mask & BIT(pin)) {
- if (rockchip_get_mux_route(bank, pin, mux, &route_location,
- &route_reg, &route_val)) {
- void __iomem *route = base;
-
- /* handle special locations */
- switch (route_location) {
- case ROCKCHIP_ROUTE_PMU:
- route = info->reg_pmu;
- break;
- case ROCKCHIP_ROUTE_GRF:
- route = info->reg_base;
- break;
- }
+ u8 bank_num = bank->bank_num;
+ u32 pin = bank_num * 32 + pin_num;
+ int i;
- writel(route_val, route + route_reg);
+ for (i = ARRAY_SIZE(rk3588_ds_regs) - 1; i >= 0; i--) {
+ if (pin >= rk3588_ds_regs[i][0]) {
+ *reg = rk3588_ds_regs[i][1];
+ *regmap = info->regmap_base;
+ *bit = pin_num % RK3588_DRV_PINS_PER_REG;
+ *bit *= RK3588_DRV_BITS_PER_PIN;
+ return 0;
}
}
- data = 3 << (bit + 16);
- data |= (mux & 3) << bit;
- writel(data, reg);
-
- return 0;
+ return -EINVAL;
}
-static int rockchip_pinctrl_set_pull(struct rockchip_pin_bank *bank,
- int pin_num, int pull)
+#define RK3588_SMT_BITS_PER_PIN 1
+#define RK3588_SMT_PINS_PER_REG 8
+
+static int rk3588_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
{
struct rockchip_pinctrl *info = bank->drvdata;
- struct rockchip_pin_ctrl *ctrl = info->ctrl;
- void __iomem *reg;
- u8 bit;
- u32 data;
-
- dev_dbg(info->pctl_dev.dev, "setting pull of GPIO%d-%d to %d\n",
- bank->bank_num, pin_num, pull);
-
- /* rk3066b doesn't support any pulls */
- if (ctrl->type == RK3066B)
- return pull ? -EINVAL : 0;
-
- ctrl->pull_calc_reg(bank, pin_num, &reg, &bit);
-
- switch (ctrl->type) {
- case RK2928:
- data = BIT(bit + 16);
- if (pull == RK_BIAS_DISABLE)
- data |= BIT(bit);
- writel(data, reg);
- break;
- case RK3188:
- case RK3568:
- /*
- * In the TRM, pull-up being 1 for everything except the GPIO0_D0-D6,
- * where that pull up value becomes 3.
- */
- if (ctrl->type == RK3568 &&
- bank->bank_num == 0 &&
- pin_num >= 27 &&
- pin_num <= 30 &&
- pull == RK_BIAS_PULL_UP)
- pull = 3;
+ u8 bank_num = bank->bank_num;
+ u32 pin = bank_num * 32 + pin_num;
+ int i;
- data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16);
- data |= pull << bit;
- writel(data, reg);
- break;
- default:
- dev_err(info->pctl_dev.dev, "unsupported pinctrl type\n");
- return -EINVAL;
+ for (i = ARRAY_SIZE(rk3588_smt_regs) - 1; i >= 0; i--) {
+ if (pin >= rk3588_smt_regs[i][0]) {
+ *reg = rk3588_smt_regs[i][1];
+ *regmap = info->regmap_base;
+ *bit = pin_num % RK3588_SMT_PINS_PER_REG;
+ *bit *= RK3588_SMT_BITS_PER_PIN;
+ return 0;
+ }
}
- return 0;
+ return -EINVAL;
}
static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
@@ -668,33 +1991,40 @@ static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
{ 4, 7, 10, 13, 16, 19, 22, 26 }
};
-#define RK3288_DRV_BITS_PER_PIN 2
-#define RK3399_DRV_3BITS_PER_PIN 3
-
static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
int pin_num, int strength)
{
struct rockchip_pinctrl *info = bank->drvdata;
struct rockchip_pin_ctrl *ctrl = info->ctrl;
- int ret, i;
- void __iomem *reg;
- u32 data, rmask, rmask_bits, temp, val;
+ struct device *dev = info->dev;
+ struct regmap *regmap;
+ int reg, ret, i;
+ u32 data, rmask, rmask_bits, temp;
u8 bit;
int drv_type = bank->drv[pin_num / 8].drv_type;
- if (!ctrl->drv_calc_reg)
- return -ENOTSUPP;
-
- dev_dbg(info->pctl_dev.dev, "setting drive of GPIO%d-%d to %d\n",
+ dev_dbg(dev, "setting drive of GPIO%d-%d to %d\n",
bank->bank_num, pin_num, strength);
- ctrl->drv_calc_reg(bank, pin_num, &reg, &bit);
- if (ctrl->type == RK3568) {
+ ret = ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+ if (ret)
+ return ret;
+ if (ctrl->type == RK3588) {
+ rmask_bits = RK3588_DRV_BITS_PER_PIN;
+ ret = strength;
+ goto config;
+ } else if (ctrl->type == RK3568) {
rmask_bits = RK3568_DRV_BITS_PER_PIN;
ret = (1 << (strength + 1)) - 1;
goto config;
}
+ if (ctrl->type == RV1126) {
+ rmask_bits = RV1126_DRV_BITS_PER_PIN;
+ ret = strength;
+ goto config;
+ }
+
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
if (rockchip_perpin_drv_list[drv_type][i] == strength) {
@@ -707,8 +2037,7 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
}
if (ret < 0) {
- dev_err(info->pctl_dev.dev, "unsupported driver strength %d\n",
- strength);
+ dev_err(dev, "unsupported driver strength %d\n", strength);
return ret;
}
@@ -731,20 +2060,14 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
rmask = BIT(15) | BIT(31);
data |= BIT(31);
-
- val = readl(reg);
- val &= ~rmask;
- val |= data & rmask;
- writel(val, reg);
+ ret = regmap_update_bits(regmap, reg, rmask, data);
+ if (ret)
+ return ret;
rmask = 0x3 | (0x3 << 16);
temp |= (0x3 << 16);
reg += 0x4;
-
- val = readl(reg);
- val &= ~rmask;
- val |= temp & rmask;
- writel(val, reg);
+ ret = regmap_update_bits(regmap, reg, rmask, temp);
return ret;
case 18 ... 21:
@@ -753,7 +2076,7 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
bit -= 16;
break;
default:
- dev_err(info->pctl_dev.dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+ dev_err(dev, "unsupported bit: %d for pinctrl drive type: %d\n",
bit, drv_type);
return -EINVAL;
}
@@ -764,8 +2087,7 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
rmask_bits = RK3288_DRV_BITS_PER_PIN;
break;
default:
- dev_err(info->pctl_dev.dev, "unsupported pinctrl drive type: %d\n",
- drv_type);
+ dev_err(dev, "unsupported pinctrl drive type: %d\n", drv_type);
return -EINVAL;
}
@@ -775,136 +2097,173 @@ config:
rmask = data | (data >> 16);
data |= (ret << bit);
- val = readl(reg);
- val &= ~rmask;
- val |= data & rmask;
- writel(val, reg);
+ ret = regmap_update_bits(regmap, reg, rmask, data);
return ret;
}
-static int rockchip_pinctrl_set_state(struct pinctrl_device *pdev,
- struct device_node *np)
+static int rockchip_pull_list[PULL_TYPE_MAX][4] = {
+ {
+ RK_BIAS_DISABLE,
+ RK_BIAS_PULL_UP,
+ RK_BIAS_PULL_DOWN,
+ RK_BIAS_BUS_HOLD
+ },
+ {
+ RK_BIAS_DISABLE,
+ RK_BIAS_PULL_DOWN,
+ RK_BIAS_DISABLE,
+ RK_BIAS_PULL_UP
+ },
+};
+
+static int rockchip_set_pull(struct rockchip_pin_bank *bank,
+ int pin_num, int pull)
{
- struct rockchip_pinctrl *info = to_rockchip_pinctrl(pdev);
- const __be32 *list;
- int i, size, ret;
- int bank_num, pin_num, func;
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct device *dev = info->dev;
+ struct regmap *regmap;
+ int reg, ret, i, pull_type;
+ u8 bit;
+ u32 data, rmask;
- /*
- * the binding format is rockchip,pins = <bank pin mux CONFIG>,
- * do sanity check and calculate pins number
- */
- list = of_get_property(np, "rockchip,pins", &size);
- size /= sizeof(*list);
+ dev_dbg(dev, "setting pull of GPIO%d-%d to %d\n", bank->bank_num, pin_num, pull);
- if (!size || size % 4) {
- dev_err(pdev->dev, "wrong pins number or pins and configs should be by 4\n");
- return -EINVAL;
- }
+ /* rk3066b does support any pulls */
+ if (ctrl->type == RK3066B)
+ return pull ? -EINVAL : 0;
- for (i = 0; i < size; i += 4) {
- const __be32 *phandle;
- struct device_node *np_config;
- struct rockchip_pin_bank *bank;
- u32 drive_strength;
+ ret = ctrl->pull_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+ if (ret)
+ return ret;
- bank_num = be32_to_cpu(*list++);
- pin_num = be32_to_cpu(*list++);
- func = be32_to_cpu(*list++);
- phandle = list++;
+ switch (ctrl->type) {
+ case RK2928:
+ case RK3128:
+ data = BIT(bit + 16);
+ if (pull == RK_BIAS_DISABLE)
+ data |= BIT(bit);
+ ret = regmap_write(regmap, reg, data);
+ break;
+ case PX30:
+ case RV1108:
+ case RV1126:
+ case RK3188:
+ case RK3288:
+ case RK3308:
+ case RK3368:
+ case RK3399:
+ case RK3568:
+ case RK3588:
+ pull_type = bank->pull_type[pin_num / 8];
+ ret = -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[pull_type]); i++) {
+ if (rockchip_pull_list[pull_type][i] == pull) {
+ ret = i;
+ break;
+ }
+ }
+ /*
+ * In the TRM, pull-up being 1 for everything except the GPIO0_D3-D6,
+ * where that pull up value becomes 3.
+ */
+ if (ctrl->type == RK3568 && bank->bank_num == 0 && pin_num >= 27 && pin_num <= 30) {
+ if (ret == RK_BIAS_PULL_UP)
+ ret = 3;
+ }
- if (!phandle)
- return -EINVAL;
+ if (ret < 0) {
+ dev_err(dev, "unsupported pull setting %d\n", pull);
+ return ret;
+ }
- np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
- bank = bank_num_to_bank(info, bank_num);
- rockchip_pinctrl_set_func(bank, pin_num, func);
- rockchip_pinctrl_set_pull(bank, pin_num,
- parse_bias_config(np_config));
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (ret << bit);
- ret = of_property_read_u32(np_config, "drive-strength", &drive_strength);
- if (!ret)
- rockchip_set_drive_perpin(bank, pin_num, drive_strength);
+ ret = regmap_update_bits(regmap, reg, rmask, data);
+ break;
+ default:
+ dev_err(dev, "unsupported pinctrl type\n");
+ return -EINVAL;
}
- return 0;
+ return ret;
}
-static struct pinctrl_ops rockchip_pinctrl_ops = {
- .set_state = rockchip_pinctrl_set_state,
-};
+#define RK3328_SCHMITT_BITS_PER_PIN 1
+#define RK3328_SCHMITT_PINS_PER_REG 16
+#define RK3328_SCHMITT_BANK_STRIDE 8
+#define RK3328_SCHMITT_GRF_OFFSET 0x380
-static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
- struct device_d *dev)
+static int rk3328_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
{
- struct resource node_res, *res;
+ struct rockchip_pinctrl *info = bank->drvdata;
- if (of_address_to_resource(bank->of_node, 0, &node_res)) {
- dev_err(dev, "cannot find IO resource for bank\n");
- return -ENOENT;
- }
+ *regmap = info->regmap_base;
+ *reg = RK3328_SCHMITT_GRF_OFFSET;
- res = request_iomem_region(dev_name(dev), node_res.start, node_res.end);
- if (IS_ERR(res)) {
- dev_err(dev, "cannot request iomem region %pa\n",
- &node_res.start);
- return PTR_ERR(res);
- }
+ *reg += bank->bank_num * RK3328_SCHMITT_BANK_STRIDE;
+ *reg += ((pin_num / RK3328_SCHMITT_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3328_SCHMITT_PINS_PER_REG;
- bank->reg_base = (void __iomem *)res->start;
+ return 0;
+}
- if (of_device_is_compatible(bank->of_node,
- "rockchip,rk3188-gpio-bank0"))
- bank->bank_type = RK3188_BANK0;
- else
- bank->bank_type = COMMON_BANK;
+#define RK3568_SCHMITT_BITS_PER_PIN 2
+#define RK3568_SCHMITT_PINS_PER_REG 8
+#define RK3568_SCHMITT_BANK_STRIDE 0x10
+#define RK3568_SCHMITT_GRF_OFFSET 0xc0
+#define RK3568_SCHMITT_PMUGRF_OFFSET 0x30
- bank->clk = of_clk_get(bank->of_node, 0);
- if (IS_ERR(bank->clk))
- return PTR_ERR(bank->clk);
+static int rk3568_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ if (bank->bank_num == 0) {
+ *regmap = info->regmap_pmu;
+ *reg = RK3568_SCHMITT_PMUGRF_OFFSET;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = RK3568_SCHMITT_GRF_OFFSET;
+ *reg += (bank->bank_num - 1) * RK3568_SCHMITT_BANK_STRIDE;
+ }
- return clk_enable(bank->clk);
+ *reg += ((pin_num / RK3568_SCHMITT_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3568_SCHMITT_PINS_PER_REG;
+ *bit *= RK3568_SCHMITT_BITS_PER_PIN;
+
+ return 0;
}
-static struct of_device_id rockchip_pinctrl_dt_match[];
+/*
+ * Pinconf_ops handling
+ */
+
+static const struct of_device_id rockchip_pinctrl_dt_match[];
+/* retrieve the soc specific data */
static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
- struct rockchip_pinctrl *d, struct device_d *dev)
+ struct rockchip_pinctrl *d,
+ struct device *dev)
{
+ struct device_node *node = dev->of_node;
const struct of_device_id *match;
- struct device_node *node = dev->device_node;
- struct device_node *np;
struct rockchip_pin_ctrl *ctrl;
struct rockchip_pin_bank *bank;
int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
- int gpio = 0;
match = of_match_node(rockchip_pinctrl_dt_match, node);
ctrl = (struct rockchip_pin_ctrl *)match->data;
- for_each_child_of_node(node, np) {
- int id;
-
- if (!of_find_property(np, "gpio-controller", NULL))
- continue;
-
- id = of_alias_get_id(np, "gpio");
- if (id < 0)
- id = gpio++;
-
- bank = ctrl->pin_banks;
- for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
- if (bank->bank_num == id) {
- bank->of_node = np;
- if (!rockchip_get_bank_data(bank, dev))
- bank->valid = true;
-
- break;
- }
- }
- }
-
grf_offs = ctrl->grf_mux_offset;
pmu_offs = ctrl->pmu_mux_offset;
drv_pmu_offs = ctrl->pmu_drv_offset;
@@ -928,12 +2287,14 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
/* preset iomux offset value, set new start value */
if (iom->offset >= 0) {
- if (iom->type & IOMUX_SOURCE_PMU)
+ if ((iom->type & IOMUX_SOURCE_PMU) ||
+ (iom->type & IOMUX_L_SOURCE_PMU))
pmu_offs = iom->offset;
else
grf_offs = iom->offset;
} else { /* set current iomux offset */
- iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+ iom->offset = ((iom->type & IOMUX_SOURCE_PMU) ||
+ (iom->type & IOMUX_L_SOURCE_PMU)) ?
pmu_offs : grf_offs;
}
@@ -958,7 +2319,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
inc = (iom->type & (IOMUX_WIDTH_4BIT |
IOMUX_WIDTH_3BIT |
IOMUX_WIDTH_2BIT)) ? 8 : 4;
- if (iom->type & IOMUX_SOURCE_PMU)
+ if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU))
pmu_offs += inc;
else
grf_offs += inc;
@@ -981,6 +2342,16 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
bank_pins += 8;
}
+ /* calculate the per-bank recalced_mask */
+ for (j = 0; j < ctrl->niomux_recalced; j++) {
+ int pin = 0;
+
+ if (ctrl->iomux_recalced[j].num == bank->bank_num) {
+ pin = ctrl->iomux_recalced[j].pin;
+ bank->recalced_mask |= BIT(pin);
+ }
+ }
+
/* calculate the per-bank route_mask */
for (j = 0; j < ctrl->niomux_routes; j++) {
int pin = 0;
@@ -995,44 +2366,104 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
return ctrl;
}
-static int rockchip_pinctrl_probe(struct device_d *dev)
+static int rockchip_pinctrl_set_state(struct pinctrl_device *pdev,
+ struct device_node *np)
+{
+ struct rockchip_pinctrl *info = to_rockchip_pinctrl(pdev);
+ const __be32 *list;
+ int i, size, ret;
+ int bank_num, pin_num, func;
+
+ /*
+ * the binding format is rockchip,pins = <bank pin mux CONFIG>,
+ * do sanity check and calculate pins number
+ */
+ list = of_get_property(np, "rockchip,pins", &size);
+ size /= sizeof(*list);
+
+ if (!size || size % 4) {
+ dev_err(pdev->dev, "wrong pins number or pins and configs should be by 4\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 4) {
+ const __be32 *phandle;
+ struct device_node *np_config;
+ struct rockchip_pin_bank *bank;
+ u32 drive_strength;
+
+ bank_num = be32_to_cpu(*list++);
+ pin_num = be32_to_cpu(*list++);
+ func = be32_to_cpu(*list++);
+ phandle = list++;
+
+ if (!phandle)
+ return -EINVAL;
+
+ np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
+ bank = bank_num_to_bank(info, bank_num);
+ rockchip_set_mux(bank, pin_num, func);
+ rockchip_set_pull(bank, pin_num, parse_bias_config(np_config));
+
+ ret = of_property_read_u32(np_config, "drive-strength", &drive_strength);
+ if (!ret)
+ rockchip_set_drive_perpin(bank, pin_num, drive_strength);
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops rockchip_pinctrl_ops = {
+ .set_state = rockchip_pinctrl_set_state,
+};
+
+static int rockchip_pinctrl_probe(struct device *dev)
{
struct rockchip_pinctrl *info;
+ struct device_node *np = dev->of_node, *node;
struct rockchip_pin_ctrl *ctrl;
int ret;
- info = xzalloc(sizeof(struct rockchip_pinctrl));
+ if (!dev->of_node)
+ return dev_err_probe(dev, -ENODEV, "device tree node not found\n");
+
+ info = xzalloc(sizeof(*info));
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = dev;
ctrl = rockchip_pinctrl_get_soc_data(info, dev);
- if (!ctrl) {
- dev_err(dev, "driver data not available\n");
- return -EINVAL;
- }
+ if (!ctrl)
+ return dev_err_probe(dev, -EINVAL, "driver data not available\n");
info->ctrl = ctrl;
- info->reg_base = syscon_base_lookup_by_phandle(dev->device_node,
- "rockchip,grf");
- if (IS_ERR(info->reg_base)) {
- dev_err(dev, "Could not get grf syscon address\n");
- return -ENODEV;
- }
+ node = of_parse_phandle(np, "rockchip,grf", 0);
+ if (!node)
+ return -EINVAL;
- info->reg_pmu = syscon_base_lookup_by_phandle(dev->device_node,
- "rockchip,pmu");
- if (IS_ERR(info->reg_pmu)) {
- dev_err(dev, "Could not get pmu syscon address\n");
- return -ENODEV;
+ info->regmap_base = syscon_node_to_regmap(node);
+ of_node_put(node);
+ if (IS_ERR(info->regmap_base))
+ return PTR_ERR(info->regmap_base);
+
+ /* try to find the optional reference to the pmu syscon */
+ node = of_parse_phandle(np, "rockchip,pmu", 0);
+ if (node) {
+ info->regmap_pmu = syscon_node_to_regmap(node);
+ of_node_put(node);
+ if (IS_ERR(info->regmap_pmu))
+ return PTR_ERR(info->regmap_pmu);
}
- info->pctl_dev.dev = dev;
- info->pctl_dev.ops = &rockchip_pinctrl_ops;
-
dev->priv = info;
- of_platform_populate(dev->device_node, NULL, dev);
+ ret = of_platform_populate(np, NULL, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register gpio device\n");
- if (!IS_ENABLED(CONFIG_PINCTRL))
- return 0;
+ info->pctl_dev.dev = dev;
+ info->pctl_dev.ops = &rockchip_pinctrl_ops;
ret = pinctrl_register(&info->pctl_dev);
if (ret)
@@ -1041,22 +2472,125 @@ static int rockchip_pinctrl_probe(struct device_d *dev)
return 0;
}
-static struct rockchip_pin_bank rk2928_pin_banks[] = {
+static __maybe_unused struct rockchip_pin_bank px30_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU
+ ),
+ PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT
+ ),
+ PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT
+ ),
+ PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT
+ ),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl px30_pin_ctrl = {
+ .pin_banks = px30_pin_banks,
+ .nr_banks = ARRAY_SIZE(px30_pin_banks),
+ .label = "PX30-GPIO",
+ .type = PX30,
+ .grf_mux_offset = 0x0,
+ .pmu_mux_offset = 0x0,
+ .iomux_routes = px30_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(px30_mux_route_data),
+ .pull_calc_reg = px30_calc_pull_reg_and_bit,
+ .drv_calc_reg = px30_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = px30_calc_schmitt_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rv1108_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU),
+ PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0),
+ PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, 0),
+ PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rv1108_pin_ctrl = {
+ .pin_banks = rv1108_pin_banks,
+ .nr_banks = ARRAY_SIZE(rv1108_pin_banks),
+ .label = "RV1108-GPIO",
+ .type = RV1108,
+ .grf_mux_offset = 0x10,
+ .pmu_mux_offset = 0x0,
+ .iomux_recalced = rv1108_mux_recalced_data,
+ .niomux_recalced = ARRAY_SIZE(rv1108_mux_recalced_data),
+ .pull_calc_reg = rv1108_calc_pull_reg_and_bit,
+ .drv_calc_reg = rv1108_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = rv1108_calc_schmitt_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rv1126_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0",
+ IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+ IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+ IOMUX_WIDTH_4BIT | IOMUX_L_SOURCE_PMU,
+ IOMUX_WIDTH_4BIT),
+ PIN_BANK_IOMUX_FLAGS_OFFSET(1, 32, "gpio1",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0x10010, 0x10018, 0x10020, 0x10028),
+ PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT),
+ PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT),
+ PIN_BANK_IOMUX_FLAGS(4, 2, "gpio4",
+ IOMUX_WIDTH_4BIT, 0, 0, 0),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rv1126_pin_ctrl = {
+ .pin_banks = rv1126_pin_banks,
+ .nr_banks = ARRAY_SIZE(rv1126_pin_banks),
+ .label = "RV1126-GPIO",
+ .type = RV1126,
+ .grf_mux_offset = 0x10004, /* mux offset from GPIO0_D0 */
+ .pmu_mux_offset = 0x0,
+ .iomux_routes = rv1126_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rv1126_mux_route_data),
+ .iomux_recalced = rv1126_mux_recalced_data,
+ .niomux_recalced = ARRAY_SIZE(rv1126_mux_recalced_data),
+ .pull_calc_reg = rv1126_calc_pull_reg_and_bit,
+ .drv_calc_reg = rv1126_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = rv1126_calc_schmitt_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3036_pin_banks[] = {
PIN_BANK(0, 32, "gpio0"),
PIN_BANK(1, 32, "gpio1"),
PIN_BANK(2, 32, "gpio2"),
- PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk2928_pin_ctrl = {
- .pin_banks = rk2928_pin_banks,
- .nr_banks = ARRAY_SIZE(rk2928_pin_banks),
- .type = RK2928,
- .grf_mux_offset = 0xa8,
- .pull_calc_reg = rk2928_calc_pull_reg_and_bit,
+static __maybe_unused struct rockchip_pin_ctrl rk3036_pin_ctrl = {
+ .pin_banks = rk3036_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3036_pin_banks),
+ .label = "RK3036-GPIO",
+ .type = RK2928,
+ .grf_mux_offset = 0xa8,
+ .pull_calc_reg = rk2928_calc_pull_reg_and_bit,
};
-static struct rockchip_pin_bank rk3066a_pin_banks[] = {
+static __maybe_unused struct rockchip_pin_bank rk3066a_pin_banks[] = {
PIN_BANK(0, 32, "gpio0"),
PIN_BANK(1, 32, "gpio1"),
PIN_BANK(2, 32, "gpio2"),
@@ -1065,142 +2599,275 @@ static struct rockchip_pin_bank rk3066a_pin_banks[] = {
PIN_BANK(6, 16, "gpio6"),
};
-static struct rockchip_pin_ctrl rk3066a_pin_ctrl = {
- .pin_banks = rk3066a_pin_banks,
- .nr_banks = ARRAY_SIZE(rk3066a_pin_banks),
- .type = RK2928,
- .grf_mux_offset = 0xa8,
- .pull_calc_reg = rk2928_calc_pull_reg_and_bit,
+static __maybe_unused struct rockchip_pin_ctrl rk3066a_pin_ctrl = {
+ .pin_banks = rk3066a_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3066a_pin_banks),
+ .label = "RK3066a-GPIO",
+ .type = RK2928,
+ .grf_mux_offset = 0xa8,
+ .pull_calc_reg = rk2928_calc_pull_reg_and_bit,
};
-static struct rockchip_pin_bank rk3066b_pin_banks[] = {
+static __maybe_unused struct rockchip_pin_bank rk3128_pin_banks[] = {
PIN_BANK(0, 32, "gpio0"),
PIN_BANK(1, 32, "gpio1"),
PIN_BANK(2, 32, "gpio2"),
PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk3066b_pin_ctrl = {
- .pin_banks = rk3066b_pin_banks,
- .nr_banks = ARRAY_SIZE(rk3066b_pin_banks),
- .type = RK3066B,
- .grf_mux_offset = 0x60,
+static __maybe_unused struct rockchip_pin_ctrl rk3128_pin_ctrl = {
+ .pin_banks = rk3128_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3128_pin_banks),
+ .label = "RK3128-GPIO",
+ .type = RK3128,
+ .grf_mux_offset = 0xa8,
+ .iomux_recalced = rk3128_mux_recalced_data,
+ .niomux_recalced = ARRAY_SIZE(rk3128_mux_recalced_data),
+ .iomux_routes = rk3128_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3128_mux_route_data),
+ .pull_calc_reg = rk3128_calc_pull_reg_and_bit,
};
-static struct rockchip_pin_bank rk3188_pin_banks[] = {
+static __maybe_unused struct rockchip_pin_bank rk3188_pin_banks[] = {
PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_GPIO_ONLY, 0, 0, 0),
PIN_BANK(1, 32, "gpio1"),
PIN_BANK(2, 32, "gpio2"),
PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk3188_pin_ctrl = {
- .pin_banks = rk3188_pin_banks,
- .nr_banks = ARRAY_SIZE(rk3188_pin_banks),
- .type = RK3188,
- .grf_mux_offset = 0x60,
- .iomux_routes = rk3188_mux_route_data,
- .niomux_routes = ARRAY_SIZE(rk3188_mux_route_data),
- .pull_calc_reg = rk3188_calc_pull_reg_and_bit,
+static __maybe_unused struct rockchip_pin_ctrl rk3188_pin_ctrl = {
+ .pin_banks = rk3188_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3188_pin_banks),
+ .label = "RK3188-GPIO",
+ .type = RK3188,
+ .grf_mux_offset = 0x60,
+ .iomux_routes = rk3188_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3188_mux_route_data),
+ .pull_calc_reg = rk3188_calc_pull_reg_and_bit,
};
-static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
- RK_MUXROUTE_PMU(0, RK_PB7, 1, 0x0110, WRITE_MASK_VAL(1, 0, 0)), /* PWM0 IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PC7, 2, 0x0110, WRITE_MASK_VAL(1, 0, 1)), /* PWM0 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PC0, 1, 0x0110, WRITE_MASK_VAL(3, 2, 0)), /* PWM1 IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PB5, 4, 0x0110, WRITE_MASK_VAL(3, 2, 1)), /* PWM1 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PC1, 1, 0x0110, WRITE_MASK_VAL(5, 4, 0)), /* PWM2 IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PB6, 4, 0x0110, WRITE_MASK_VAL(5, 4, 1)), /* PWM2 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PB3, 2, 0x0300, WRITE_MASK_VAL(0, 0, 0)), /* CAN0 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PA1, 4, 0x0300, WRITE_MASK_VAL(0, 0, 1)), /* CAN0 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PA1, 3, 0x0300, WRITE_MASK_VAL(2, 2, 0)), /* CAN1 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC3, 3, 0x0300, WRITE_MASK_VAL(2, 2, 1)), /* CAN1 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PB5, 3, 0x0300, WRITE_MASK_VAL(4, 4, 0)), /* CAN2 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PB2, 4, 0x0300, WRITE_MASK_VAL(4, 4, 1)), /* CAN2 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PC4, 1, 0x0300, WRITE_MASK_VAL(6, 6, 0)), /* HPDIN IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PC2, 2, 0x0300, WRITE_MASK_VAL(6, 6, 1)), /* HPDIN IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB1, 3, 0x0300, WRITE_MASK_VAL(8, 8, 0)), /* GMAC1 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PA7, 3, 0x0300, WRITE_MASK_VAL(8, 8, 1)), /* GMAC1 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PD1, 1, 0x0300, WRITE_MASK_VAL(10, 10, 0)), /* HDMITX IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PC7, 1, 0x0300, WRITE_MASK_VAL(10, 10, 1)), /* HDMITX IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PB6, 1, 0x0300, WRITE_MASK_VAL(14, 14, 0)), /* I2C2 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PB4, 1, 0x0300, WRITE_MASK_VAL(14, 14, 1)), /* I2C2 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PA0, 1, 0x0304, WRITE_MASK_VAL(0, 0, 0)), /* I2C3 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PB6, 4, 0x0304, WRITE_MASK_VAL(0, 0, 1)), /* I2C3 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PB2, 1, 0x0304, WRITE_MASK_VAL(2, 2, 0)), /* I2C4 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PB1, 2, 0x0304, WRITE_MASK_VAL(2, 2, 1)), /* I2C4 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB4, 4, 0x0304, WRITE_MASK_VAL(4, 4, 0)), /* I2C5 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PD0, 2, 0x0304, WRITE_MASK_VAL(4, 4, 1)), /* I2C5 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB1, 5, 0x0304, WRITE_MASK_VAL(14, 14, 0)), /* PWM8 IO mux M0 */
- RK_MUXROUTE_GRF(1, RK_PD5, 4, 0x0304, WRITE_MASK_VAL(14, 14, 1)), /* PWM8 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB2, 5, 0x0308, WRITE_MASK_VAL(0, 0, 0)), /* PWM9 IO mux M0 */
- RK_MUXROUTE_GRF(1, RK_PD6, 4, 0x0308, WRITE_MASK_VAL(0, 0, 1)), /* PWM9 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB5, 5, 0x0308, WRITE_MASK_VAL(2, 2, 0)), /* PWM10 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PA1, 2, 0x0308, WRITE_MASK_VAL(2, 2, 1)), /* PWM10 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB6, 5, 0x0308, WRITE_MASK_VAL(4, 4, 0)), /* PWM11 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC0, 3, 0x0308, WRITE_MASK_VAL(4, 4, 1)), /* PWM11 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PB7, 2, 0x0308, WRITE_MASK_VAL(6, 6, 0)), /* PWM12 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC5, 1, 0x0308, WRITE_MASK_VAL(6, 6, 1)), /* PWM12 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PC0, 2, 0x0308, WRITE_MASK_VAL(8, 8, 0)), /* PWM13 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC6, 1, 0x0308, WRITE_MASK_VAL(8, 8, 1)), /* PWM13 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PC4, 1, 0x0308, WRITE_MASK_VAL(10, 10, 0)), /* PWM14 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC2, 1, 0x0308, WRITE_MASK_VAL(10, 10, 1)), /* PWM14 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PC5, 1, 0x0308, WRITE_MASK_VAL(12, 12, 0)), /* PWM15 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC3, 1, 0x0308, WRITE_MASK_VAL(12, 12, 1)), /* PWM15 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PD2, 3, 0x0308, WRITE_MASK_VAL(14, 14, 0)), /* SDMMC2 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PA5, 5, 0x0308, WRITE_MASK_VAL(14, 14, 1)), /* SDMMC2 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PB5, 2, 0x030c, WRITE_MASK_VAL(0, 0, 0)), /* SPI0 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PD3, 3, 0x030c, WRITE_MASK_VAL(0, 0, 1)), /* SPI0 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PB5, 3, 0x030c, WRITE_MASK_VAL(2, 2, 0)), /* SPI1 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PC3, 3, 0x030c, WRITE_MASK_VAL(2, 2, 1)), /* SPI1 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PC1, 4, 0x030c, WRITE_MASK_VAL(4, 4, 0)), /* SPI2 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PA0, 3, 0x030c, WRITE_MASK_VAL(4, 4, 1)), /* SPI2 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PB3, 4, 0x030c, WRITE_MASK_VAL(6, 6, 0)), /* SPI3 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC2, 2, 0x030c, WRITE_MASK_VAL(6, 6, 1)), /* SPI3 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PB4, 2, 0x030c, WRITE_MASK_VAL(8, 8, 0)), /* UART1 IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(8, 8, 1)), /* UART1 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(10, 10, 0)), /* UART2 IO mux M0 */
- RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x030c, WRITE_MASK_VAL(10, 10, 1)), /* UART2 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PA1, 2, 0x030c, WRITE_MASK_VAL(12, 12, 0)), /* UART3 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PB7, 4, 0x030c, WRITE_MASK_VAL(12, 12, 1)), /* UART3 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PA6, 2, 0x030c, WRITE_MASK_VAL(14, 14, 0)), /* UART4 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PB2, 4, 0x030c, WRITE_MASK_VAL(14, 14, 1)), /* UART4 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PA2, 3, 0x0310, WRITE_MASK_VAL(0, 0, 0)), /* UART5 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PC2, 4, 0x0310, WRITE_MASK_VAL(0, 0, 1)), /* UART5 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PA4, 3, 0x0310, WRITE_MASK_VAL(2, 2, 0)), /* UART6 IO mux M0 */
- RK_MUXROUTE_GRF(1, RK_PD5, 3, 0x0310, WRITE_MASK_VAL(2, 2, 1)), /* UART6 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PA6, 3, 0x0310, WRITE_MASK_VAL(5, 4, 0)), /* UART7 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PC4, 4, 0x0310, WRITE_MASK_VAL(5, 4, 1)), /* UART7 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(5, 4, 2)), /* UART7 IO mux M2 */
- RK_MUXROUTE_GRF(2, RK_PC5, 3, 0x0310, WRITE_MASK_VAL(6, 6, 0)), /* UART8 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PD7, 4, 0x0310, WRITE_MASK_VAL(6, 6, 1)), /* UART8 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PB0, 3, 0x0310, WRITE_MASK_VAL(9, 8, 0)), /* UART9 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC5, 4, 0x0310, WRITE_MASK_VAL(9, 8, 1)), /* UART9 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PA4, 4, 0x0310, WRITE_MASK_VAL(9, 8, 2)), /* UART9 IO mux M2 */
- RK_MUXROUTE_GRF(1, RK_PA2, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PC6, 4, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 IO mux M1 */
- RK_MUXROUTE_GRF(2, RK_PD0, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 IO mux M2 */
- RK_MUXROUTE_GRF(2, RK_PC1, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PB6, 5, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(14, 14, 0)), /* I2S3 IO mux M0 */
- RK_MUXROUTE_GRF(4, RK_PC2, 5, 0x0310, WRITE_MASK_VAL(14, 14, 1)), /* I2S3 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
- RK_MUXROUTE_GRF(1, RK_PA6, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
- RK_MUXROUTE_GRF(3, RK_PD6, 5, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PA0, 4, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
- RK_MUXROUTE_GRF(3, RK_PC4, 5, 0x0314, WRITE_MASK_VAL(1, 0, 2)), /* PDM IO mux M2 */
- RK_MUXROUTE_PMU(0, RK_PA5, 3, 0x0314, WRITE_MASK_VAL(3, 2, 0)), /* PCIE20 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PD0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 1)), /* PCIE20 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PB0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 2)), /* PCIE20 IO mux M2 */
- RK_MUXROUTE_PMU(0, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(5, 4, 0)), /* PCIE30X1 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PD2, 4, 0x0314, WRITE_MASK_VAL(5, 4, 1)), /* PCIE30X1 IO mux M1 */
- RK_MUXROUTE_GRF(1, RK_PA5, 4, 0x0314, WRITE_MASK_VAL(5, 4, 2)), /* PCIE30X1 IO mux M2 */
- RK_MUXROUTE_PMU(0, RK_PA6, 2, 0x0314, WRITE_MASK_VAL(7, 6, 0)), /* PCIE30X2 IO mux M0 */
- RK_MUXROUTE_GRF(2, RK_PD4, 4, 0x0314, WRITE_MASK_VAL(7, 6, 1)), /* PCIE30X2 IO mux M1 */
- RK_MUXROUTE_GRF(4, RK_PC2, 4, 0x0314, WRITE_MASK_VAL(7, 6, 2)), /* PCIE30X2 IO mux M2 */
+static __maybe_unused struct rockchip_pin_bank rk3228_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rk3228_pin_ctrl = {
+ .pin_banks = rk3228_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3228_pin_banks),
+ .label = "RK3228-GPIO",
+ .type = RK3288,
+ .grf_mux_offset = 0x0,
+ .iomux_routes = rk3228_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3228_mux_route_data),
+ .pull_calc_reg = rk3228_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3228_calc_drv_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3288_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_UNROUTED
+ ),
+ PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_UNROUTED,
+ IOMUX_UNROUTED,
+ IOMUX_UNROUTED,
+ 0
+ ),
+ PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, IOMUX_UNROUTED),
+ PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, IOMUX_WIDTH_4BIT),
+ PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0,
+ 0
+ ),
+ PIN_BANK_IOMUX_FLAGS(5, 32, "gpio5", IOMUX_UNROUTED,
+ 0,
+ 0,
+ IOMUX_UNROUTED
+ ),
+ PIN_BANK_IOMUX_FLAGS(6, 32, "gpio6", 0, 0, 0, IOMUX_UNROUTED),
+ PIN_BANK_IOMUX_FLAGS(7, 32, "gpio7", 0,
+ 0,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_UNROUTED
+ ),
+ PIN_BANK(8, 16, "gpio8"),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rk3288_pin_ctrl = {
+ .pin_banks = rk3288_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3288_pin_banks),
+ .label = "RK3288-GPIO",
+ .type = RK3288,
+ .grf_mux_offset = 0x0,
+ .pmu_mux_offset = 0x84,
+ .iomux_routes = rk3288_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3288_mux_route_data),
+ .pull_calc_reg = rk3288_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3288_calc_drv_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3308_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT),
+ PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT),
+ PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT),
+ PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT),
+ PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT,
+ IOMUX_WIDTH_2BIT),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rk3308_pin_ctrl = {
+ .pin_banks = rk3308_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3308_pin_banks),
+ .label = "RK3308-GPIO",
+ .type = RK3308,
+ .grf_mux_offset = 0x0,
+ .iomux_recalced = rk3308_mux_recalced_data,
+ .niomux_recalced = ARRAY_SIZE(rk3308_mux_recalced_data),
+ .iomux_routes = rk3308_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3308_mux_route_data),
+ .pull_calc_reg = rk3308_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3308_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = rk3308_calc_schmitt_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3328_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", 0, 0, 0, 0),
+ PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0),
+ PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0,
+ IOMUX_WIDTH_3BIT,
+ IOMUX_WIDTH_3BIT,
+ 0),
+ PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3",
+ IOMUX_WIDTH_3BIT,
+ IOMUX_WIDTH_3BIT,
+ 0,
+ 0),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rk3328_pin_ctrl = {
+ .pin_banks = rk3328_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3328_pin_banks),
+ .label = "RK3328-GPIO",
+ .type = RK3288,
+ .grf_mux_offset = 0x0,
+ .iomux_recalced = rk3328_mux_recalced_data,
+ .niomux_recalced = ARRAY_SIZE(rk3328_mux_recalced_data),
+ .iomux_routes = rk3328_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3328_mux_route_data),
+ .pull_calc_reg = rk3228_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3228_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = rk3328_calc_schmitt_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3368_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU
+ ),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_bank rk3568_pin_banks[] = {
+static __maybe_unused struct rockchip_pin_ctrl rk3368_pin_ctrl = {
+ .pin_banks = rk3368_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3368_pin_banks),
+ .label = "RK3368-GPIO",
+ .type = RK3368,
+ .grf_mux_offset = 0x0,
+ .pmu_mux_offset = 0x0,
+ .pull_calc_reg = rk3368_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3368_calc_drv_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3399_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET_PULL_FLAGS(0, 32, "gpio0",
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ DRV_TYPE_IO_1V8_ONLY,
+ DRV_TYPE_IO_1V8_ONLY,
+ DRV_TYPE_IO_DEFAULT,
+ DRV_TYPE_IO_DEFAULT,
+ 0x80,
+ 0x88,
+ -1,
+ -1,
+ PULL_TYPE_IO_1V8_ONLY,
+ PULL_TYPE_IO_1V8_ONLY,
+ PULL_TYPE_IO_DEFAULT,
+ PULL_TYPE_IO_DEFAULT
+ ),
+ PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ IOMUX_SOURCE_PMU,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ 0xa0,
+ 0xa8,
+ 0xb0,
+ 0xb8
+ ),
+ PIN_BANK_DRV_FLAGS_PULL_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_ONLY,
+ DRV_TYPE_IO_1V8_ONLY,
+ PULL_TYPE_IO_DEFAULT,
+ PULL_TYPE_IO_DEFAULT,
+ PULL_TYPE_IO_1V8_ONLY,
+ PULL_TYPE_IO_1V8_ONLY
+ ),
+ PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY,
+ DRV_TYPE_IO_3V3_ONLY,
+ DRV_TYPE_IO_3V3_ONLY,
+ DRV_TYPE_IO_1V8_OR_3V0
+ ),
+ PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_3V0_AUTO,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_OR_3V0
+ ),
+};
+
+static __maybe_unused struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+ .pin_banks = rk3399_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3399_pin_banks),
+ .label = "RK3399-GPIO",
+ .type = RK3399,
+ .grf_mux_offset = 0xe000,
+ .pmu_mux_offset = 0x0,
+ .grf_drv_offset = 0xe100,
+ .pmu_drv_offset = 0x80,
+ .iomux_routes = rk3399_mux_route_data,
+ .niomux_routes = ARRAY_SIZE(rk3399_mux_route_data),
+ .pull_calc_reg = rk3399_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3399_calc_drv_reg_and_bit,
+};
+
+static __maybe_unused struct rockchip_pin_bank rk3568_pin_banks[] = {
PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT,
IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT,
IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT,
@@ -1223,7 +2890,7 @@ static struct rockchip_pin_bank rk3568_pin_banks[] = {
IOMUX_WIDTH_4BIT),
};
-static struct rockchip_pin_ctrl rk3568_pin_ctrl = {
+static __maybe_unused struct rockchip_pin_ctrl rk3568_pin_ctrl = {
.pin_banks = rk3568_pin_banks,
.nr_banks = ARRAY_SIZE(rk3568_pin_banks),
.label = "RK3568-GPIO",
@@ -1236,55 +2903,101 @@ static struct rockchip_pin_ctrl rk3568_pin_ctrl = {
.niomux_routes = ARRAY_SIZE(rk3568_mux_route_data),
.pull_calc_reg = rk3568_calc_pull_reg_and_bit,
.drv_calc_reg = rk3568_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = rk3568_calc_schmitt_reg_and_bit,
};
-static struct of_device_id rockchip_pinctrl_dt_match[] = {
- {
- .compatible = "rockchip,rk2928-pinctrl",
- .data = &rk2928_pin_ctrl,
- },
- {
- .compatible = "rockchip,rk3066a-pinctrl",
- .data = &rk3066a_pin_ctrl,
- },
- {
- .compatible = "rockchip,rk3066b-pinctrl",
- .data = &rk3066b_pin_ctrl,
- },
- {
- .compatible = "rockchip,rk3188-pinctrl",
- .data = &rk3188_pin_ctrl,
- },
- {
- .compatible = "rockchip,rk3568-pinctrl",
- .data = &rk3568_pin_ctrl
- },
- {
- /* sentinel */
- }
+static __maybe_unused struct rockchip_pin_bank rk3588_pin_banks[] = {
+ RK3588_PIN_BANK_FLAGS(0, 32, "gpio0",
+ IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+ RK3588_PIN_BANK_FLAGS(1, 32, "gpio1",
+ IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+ RK3588_PIN_BANK_FLAGS(2, 32, "gpio2",
+ IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+ RK3588_PIN_BANK_FLAGS(3, 32, "gpio3",
+ IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+ RK3588_PIN_BANK_FLAGS(4, 32, "gpio4",
+ IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
};
-static struct driver_d rockchip_pinctrl_driver = {
- .name = "rockchip-pinctrl",
- .probe = rockchip_pinctrl_probe,
- .of_compatible = DRV_OF_COMPAT(rockchip_pinctrl_dt_match),
+static __maybe_unused struct rockchip_pin_ctrl rk3588_pin_ctrl = {
+ .pin_banks = rk3588_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3588_pin_banks),
+ .label = "RK3588-GPIO",
+ .type = RK3588,
+ .pull_calc_reg = rk3588_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3588_calc_drv_reg_and_bit,
+ .schmitt_calc_reg = rk3588_calc_schmitt_reg_and_bit,
};
-core_platform_driver(rockchip_pinctrl_driver);
-
-static struct of_device_id rockchip_gpio_dt_match[] = {
- {
- .compatible = "rockchip,gpio-bank",
- .data = &rk2928_pin_ctrl,
- }, {
- /* sentinel */
- }
+static const struct of_device_id rockchip_pinctrl_dt_match[] = {
+#ifdef CONFIG_ARCH_PX30
+ { .compatible = "rockchip,px30-pinctrl",
+ .data = &px30_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RV1108
+ { .compatible = "rockchip,rv1108-pinctrl",
+ .data = &rv1108_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RV1126
+ { .compatible = "rockchip,rv1126-pinctrl",
+ .data = &rv1126_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3036
+ { .compatible = "rockchip,rk3036-pinctrl",
+ .data = &rk3036_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3066A
+ { .compatible = "rockchip,rk3066a-pinctrl",
+ .data = &rk3066a_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3128
+ { .compatible = "rockchip,rk3128-pinctrl",
+ .data = (void *)&rk3128_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3188
+ { .compatible = "rockchip,rk3188-pinctrl",
+ .data = &rk3188_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3228
+ { .compatible = "rockchip,rk3228-pinctrl",
+ .data = &rk3228_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3288
+ { .compatible = "rockchip,rk3288-pinctrl",
+ .data = &rk3288_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3308
+ { .compatible = "rockchip,rk3308-pinctrl",
+ .data = &rk3308_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3328
+ { .compatible = "rockchip,rk3328-pinctrl",
+ .data = &rk3328_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3368
+ { .compatible = "rockchip,rk3368-pinctrl",
+ .data = &rk3368_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3399
+ { .compatible = "rockchip,rk3399-pinctrl",
+ .data = &rk3399_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3568
+ { .compatible = "rockchip,rk3568-pinctrl",
+ .data = &rk3568_pin_ctrl },
+#endif
+#ifdef CONFIG_ARCH_RK3588
+ { .compatible = "rockchip,rk3588-pinctrl",
+ .data = &rk3588_pin_ctrl },
+#endif
+ {},
};
+MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
-static struct driver_d rockchip_gpio_driver = {
- .name = "rockchip-gpio",
- .probe = rockchip_gpio_probe,
- .of_compatible = DRV_OF_COMPAT(rockchip_gpio_dt_match),
+static struct driver rockchip_pinctrl_driver = {
+ .name = "rockchip-pinctrl",
+ .probe = rockchip_pinctrl_probe,
+ .of_compatible = DRV_OF_COMPAT(rockchip_pinctrl_dt_match),
};
-core_platform_driver(rockchip_gpio_driver);
+core_platform_driver(rockchip_pinctrl_driver);
diff --git a/drivers/pinctrl/pinctrl-rockchip.h b/drivers/pinctrl/pinctrl-rockchip.h
new file mode 100644
index 0000000000..498f4615e1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rockchip.h
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021 Rockchip Electronics Co. Ltd.
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * With some ideas taken from pinctrl-samsung:
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * https://www.linaro.org
+ *
+ * and pinctrl-at91:
+ * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ */
+
+#ifndef _PINCTRL_ROCKCHIP_H
+#define _PINCTRL_ROCKCHIP_H
+
+#define RK_GPIO0_A0 0
+#define RK_GPIO0_A1 1
+#define RK_GPIO0_A2 2
+#define RK_GPIO0_A3 3
+#define RK_GPIO0_A4 4
+#define RK_GPIO0_A5 5
+#define RK_GPIO0_A6 6
+#define RK_GPIO0_A7 7
+#define RK_GPIO0_B0 8
+#define RK_GPIO0_B1 9
+#define RK_GPIO0_B2 10
+#define RK_GPIO0_B3 11
+#define RK_GPIO0_B4 12
+#define RK_GPIO0_B5 13
+#define RK_GPIO0_B6 14
+#define RK_GPIO0_B7 15
+#define RK_GPIO0_C0 16
+#define RK_GPIO0_C1 17
+#define RK_GPIO0_C2 18
+#define RK_GPIO0_C3 19
+#define RK_GPIO0_C4 20
+#define RK_GPIO0_C5 21
+#define RK_GPIO0_C6 22
+#define RK_GPIO0_C7 23
+#define RK_GPIO0_D0 24
+#define RK_GPIO0_D1 25
+#define RK_GPIO0_D2 26
+#define RK_GPIO0_D3 27
+#define RK_GPIO0_D4 28
+#define RK_GPIO0_D5 29
+#define RK_GPIO0_D6 30
+#define RK_GPIO0_D7 31
+
+#define RK_GPIO1_A0 32
+#define RK_GPIO1_A1 33
+#define RK_GPIO1_A2 34
+#define RK_GPIO1_A3 35
+#define RK_GPIO1_A4 36
+#define RK_GPIO1_A5 37
+#define RK_GPIO1_A6 38
+#define RK_GPIO1_A7 39
+#define RK_GPIO1_B0 40
+#define RK_GPIO1_B1 41
+#define RK_GPIO1_B2 42
+#define RK_GPIO1_B3 43
+#define RK_GPIO1_B4 44
+#define RK_GPIO1_B5 45
+#define RK_GPIO1_B6 46
+#define RK_GPIO1_B7 47
+#define RK_GPIO1_C0 48
+#define RK_GPIO1_C1 49
+#define RK_GPIO1_C2 50
+#define RK_GPIO1_C3 51
+#define RK_GPIO1_C4 52
+#define RK_GPIO1_C5 53
+#define RK_GPIO1_C6 54
+#define RK_GPIO1_C7 55
+#define RK_GPIO1_D0 56
+#define RK_GPIO1_D1 57
+#define RK_GPIO1_D2 58
+#define RK_GPIO1_D3 59
+#define RK_GPIO1_D4 60
+#define RK_GPIO1_D5 61
+#define RK_GPIO1_D6 62
+#define RK_GPIO1_D7 63
+
+#define RK_GPIO2_A0 64
+#define RK_GPIO2_A1 65
+#define RK_GPIO2_A2 66
+#define RK_GPIO2_A3 67
+#define RK_GPIO2_A4 68
+#define RK_GPIO2_A5 69
+#define RK_GPIO2_A6 70
+#define RK_GPIO2_A7 71
+#define RK_GPIO2_B0 72
+#define RK_GPIO2_B1 73
+#define RK_GPIO2_B2 74
+#define RK_GPIO2_B3 75
+#define RK_GPIO2_B4 76
+#define RK_GPIO2_B5 77
+#define RK_GPIO2_B6 78
+#define RK_GPIO2_B7 79
+#define RK_GPIO2_C0 80
+#define RK_GPIO2_C1 81
+#define RK_GPIO2_C2 82
+#define RK_GPIO2_C3 83
+#define RK_GPIO2_C4 84
+#define RK_GPIO2_C5 85
+#define RK_GPIO2_C6 86
+#define RK_GPIO2_C7 87
+#define RK_GPIO2_D0 88
+#define RK_GPIO2_D1 89
+#define RK_GPIO2_D2 90
+#define RK_GPIO2_D3 91
+#define RK_GPIO2_D4 92
+#define RK_GPIO2_D5 93
+#define RK_GPIO2_D6 94
+#define RK_GPIO2_D7 95
+
+#define RK_GPIO3_A0 96
+#define RK_GPIO3_A1 97
+#define RK_GPIO3_A2 98
+#define RK_GPIO3_A3 99
+#define RK_GPIO3_A4 100
+#define RK_GPIO3_A5 101
+#define RK_GPIO3_A6 102
+#define RK_GPIO3_A7 103
+#define RK_GPIO3_B0 104
+#define RK_GPIO3_B1 105
+#define RK_GPIO3_B2 106
+#define RK_GPIO3_B3 107
+#define RK_GPIO3_B4 108
+#define RK_GPIO3_B5 109
+#define RK_GPIO3_B6 110
+#define RK_GPIO3_B7 111
+#define RK_GPIO3_C0 112
+#define RK_GPIO3_C1 113
+#define RK_GPIO3_C2 114
+#define RK_GPIO3_C3 115
+#define RK_GPIO3_C4 116
+#define RK_GPIO3_C5 117
+#define RK_GPIO3_C6 118
+#define RK_GPIO3_C7 119
+#define RK_GPIO3_D0 120
+#define RK_GPIO3_D1 121
+#define RK_GPIO3_D2 122
+#define RK_GPIO3_D3 123
+#define RK_GPIO3_D4 124
+#define RK_GPIO3_D5 125
+#define RK_GPIO3_D6 126
+#define RK_GPIO3_D7 127
+
+#define RK_GPIO4_A0 128
+#define RK_GPIO4_A1 129
+#define RK_GPIO4_A2 130
+#define RK_GPIO4_A3 131
+#define RK_GPIO4_A4 132
+#define RK_GPIO4_A5 133
+#define RK_GPIO4_A6 134
+#define RK_GPIO4_A7 135
+#define RK_GPIO4_B0 136
+#define RK_GPIO4_B1 137
+#define RK_GPIO4_B2 138
+#define RK_GPIO4_B3 139
+#define RK_GPIO4_B4 140
+#define RK_GPIO4_B5 141
+#define RK_GPIO4_B6 142
+#define RK_GPIO4_B7 143
+#define RK_GPIO4_C0 144
+#define RK_GPIO4_C1 145
+#define RK_GPIO4_C2 146
+#define RK_GPIO4_C3 147
+#define RK_GPIO4_C4 148
+#define RK_GPIO4_C5 149
+#define RK_GPIO4_C6 150
+#define RK_GPIO4_C7 151
+#define RK_GPIO4_D0 152
+#define RK_GPIO4_D1 153
+#define RK_GPIO4_D2 154
+#define RK_GPIO4_D3 155
+#define RK_GPIO4_D4 156
+#define RK_GPIO4_D5 157
+#define RK_GPIO4_D6 158
+#define RK_GPIO4_D7 159
+
+enum rockchip_pinctrl_type {
+ PX30,
+ RV1108,
+ RV1126,
+ RK2928,
+ RK3066B,
+ RK3128,
+ RK3188,
+ RK3288,
+ RK3308,
+ RK3368,
+ RK3399,
+ RK3568,
+ RK3588,
+};
+
+/**
+ * struct rockchip_gpio_regs
+ * @port_dr: data register
+ * @port_ddr: data direction register
+ * @int_en: interrupt enable
+ * @int_mask: interrupt mask
+ * @int_type: interrupt trigger type, such as high, low, edge trriger type.
+ * @int_polarity: interrupt polarity enable register
+ * @int_bothedge: interrupt bothedge enable register
+ * @int_status: interrupt status register
+ * @int_rawstatus: int_status = int_rawstatus & int_mask
+ * @debounce: enable debounce for interrupt signal
+ * @dbclk_div_en: enable divider for debounce clock
+ * @dbclk_div_con: setting for divider of debounce clock
+ * @port_eoi: end of interrupt of the port
+ * @ext_port: port data from external
+ * @version_id: controller version register
+ */
+struct rockchip_gpio_regs {
+ u32 port_dr;
+ u32 port_ddr;
+ u32 int_en;
+ u32 int_mask;
+ u32 int_type;
+ u32 int_polarity;
+ u32 int_bothedge;
+ u32 int_status;
+ u32 int_rawstatus;
+ u32 debounce;
+ u32 dbclk_div_en;
+ u32 dbclk_div_con;
+ u32 port_eoi;
+ u32 ext_port;
+ u32 version_id;
+};
+
+/**
+ * struct rockchip_iomux
+ * @type: iomux variant using IOMUX_* constants
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ * an initial offset value the relevant source offset can be reset
+ * to a new value for autocalculating the following iomux registers.
+ */
+struct rockchip_iomux {
+ int type;
+ int offset;
+};
+
+/*
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+ DRV_TYPE_IO_DEFAULT = 0,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_ONLY,
+ DRV_TYPE_IO_1V8_3V0_AUTO,
+ DRV_TYPE_IO_3V3_ONLY,
+ DRV_TYPE_MAX
+};
+
+/*
+ * enum type index corresponding to rockchip_pull_list arrays index.
+ */
+enum rockchip_pin_pull_type {
+ PULL_TYPE_IO_DEFAULT = 0,
+ PULL_TYPE_IO_1V8_ONLY,
+ PULL_TYPE_MAX
+};
+
+/**
+ * struct rockchip_drv
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ * an initial offset value the relevant source offset can be reset
+ * to a new value for autocalculating the following drive strength
+ * registers. if used chips own cal_drv func instead to calculate
+ * registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+ enum rockchip_pin_drv_type drv_type;
+ int offset;
+};
+
+/**
+ * struct rockchip_pin_bank
+ * @dev: the pinctrl device bind to the bank
+ * @regmap_pull: optional separate register for additional pull settings
+ * @clk: clock of the gpio bank
+ * @db_clk: clock of the gpio debounce
+ * @irq: interrupt of the gpio bank
+ * @saved_masks: Saved content of GPIO_INTEN at suspend time.
+ * @pin_base: first pin number
+ * @nr_pins: number of pins in this bank
+ * @name: name of the bank
+ * @bank_num: number of the bank, to account for holes
+ * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
+ * @pull_type: array describing the 4 pull type sources of the bank
+ * @valid: is all necessary information present
+ * @of_node: dt node of this bank
+ * @drvdata: common pinctrl basedata
+ * @domain: irqdomain of the gpio bank
+ * @gpio_chip: gpiolib chip
+ * @grange: gpio range
+ * @slock: spinlock for the gpio bank
+ * @toggle_edge_mode: bit mask to toggle (falling/rising) edge mode
+ * @recalced_mask: bit mask to indicate a need to recalulate the mask
+ * @route_mask: bits describing the routing pins of per bank
+ * @deferred_output: gpio output settings to be done after gpio bank probed
+ * @deferred_lock: mutex for the deferred_output shared btw gpio and pinctrl
+ */
+struct rockchip_pin_bank {
+ struct device *dev;
+ struct regmap *regmap_pull;
+ struct clk *clk;
+ struct clk *db_clk;
+ int irq;
+ u32 saved_masks;
+ u32 pin_base;
+ u8 nr_pins;
+ char *name;
+ u8 bank_num;
+ struct rockchip_iomux iomux[4];
+ struct rockchip_drv drv[4];
+ enum rockchip_pin_pull_type pull_type[4];
+ bool valid;
+ struct device_node *of_node;
+ struct rockchip_pinctrl *drvdata;
+ struct bgpio_chip bgpio_chip;
+ u32 gpio_type;
+ u32 toggle_edge_mode;
+ u32 recalced_mask;
+ u32 route_mask;
+ struct list_head deferred_pins;
+};
+
+/**
+ * struct rockchip_mux_recalced_data: represent a pin iomux data.
+ * @num: bank number.
+ * @pin: pin number.
+ * @bit: index at register.
+ * @reg: register offset.
+ * @mask: mask bit
+ */
+struct rockchip_mux_recalced_data {
+ u8 num;
+ u8 pin;
+ u32 reg;
+ u8 bit;
+ u8 mask;
+};
+
+enum rockchip_mux_route_location {
+ ROCKCHIP_ROUTE_SAME = 0,
+ ROCKCHIP_ROUTE_PMU,
+ ROCKCHIP_ROUTE_GRF,
+};
+
+/**
+ * struct rockchip_mux_recalced_data: represent a pin iomux data.
+ * @bank_num: bank number.
+ * @pin: index at register or used to calc index.
+ * @func: the min pin.
+ * @route_location: the mux route location (same, pmu, grf).
+ * @route_offset: the max pin.
+ * @route_val: the register offset.
+ */
+struct rockchip_mux_route_data {
+ u8 bank_num;
+ u8 pin;
+ u8 func;
+ enum rockchip_mux_route_location route_location;
+ u32 route_offset;
+ u32 route_val;
+};
+
+struct rockchip_pin_ctrl {
+ struct rockchip_pin_bank *pin_banks;
+ u32 nr_banks;
+ u32 nr_pins;
+ char *label;
+ enum rockchip_pinctrl_type type;
+ int grf_mux_offset;
+ int pmu_mux_offset;
+ int grf_drv_offset;
+ int pmu_drv_offset;
+ struct rockchip_mux_recalced_data *iomux_recalced;
+ u32 niomux_recalced;
+ struct rockchip_mux_route_data *iomux_routes;
+ u32 niomux_routes;
+
+ int (*pull_calc_reg)(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit);
+ int (*drv_calc_reg)(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit);
+ int (*schmitt_calc_reg)(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit);
+};
+
+struct rockchip_pin_config {
+ unsigned int func;
+ unsigned long *configs;
+ unsigned int nconfigs;
+};
+
+struct rockchip_pin_deferred {
+ struct list_head head;
+ unsigned int pin;
+ u32 arg;
+};
+
+/**
+ * struct rockchip_pin_group: represent group of pins of a pinmux function.
+ * @name: name of the pin group, used to lookup the group.
+ * @pins: the pins included in this group.
+ * @npins: number of pins included in this group.
+ * @data: local pin configuration
+ */
+struct rockchip_pin_group {
+ const char *name;
+ unsigned int npins;
+ unsigned int *pins;
+ struct rockchip_pin_config *data;
+};
+
+/**
+ * struct rockchip_pmx_func: represent a pin function.
+ * @name: name of the pin function, used to lookup the function.
+ * @groups: one or more names of pin groups that provide this function.
+ * @ngroups: number of groups included in @groups.
+ */
+struct rockchip_pmx_func {
+ const char *name;
+ const char **groups;
+ u8 ngroups;
+};
+
+struct rockchip_pinctrl {
+ struct regmap *regmap_base;
+ int reg_size;
+ struct regmap *regmap_pull;
+ struct regmap *regmap_pmu;
+ struct device *dev;
+ struct rockchip_pin_ctrl *ctrl;
+ struct pinctrl_device pctl_dev;
+};
+
+#endif
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index edf2d046f0..0e706656b9 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -64,7 +64,7 @@ static int pcs_set_state(struct pinctrl_device *pdev, struct device_node *np)
unsigned int offset, val, rows, mask, reg, i;
const __be32 *mux;
- dev_dbg(pcs->pinctrl.dev, "set state: %s\n", np->full_name);
+ dev_dbg(pcs->pinctrl.dev, "set state: %pOF\n", np);
if (pcs->bits_per_mux) {
mux = of_get_property(np, "pinctrl-single,bits", &size);
if (size % 3 != 0)
@@ -89,8 +89,7 @@ static int pcs_set_state(struct pinctrl_device *pdev, struct device_node *np)
size /= sizeof(*mux); /* Number of elements in array */
if (!mux || !size || (size % (pcs->args_count + 1))) {
- dev_err(pcs->pinctrl.dev, "bad data for mux %s\n",
- np->full_name);
+ dev_err(pcs->pinctrl.dev, "bad data for mux %pOF\n", np);
return -EINVAL;
}
@@ -119,11 +118,11 @@ static struct pinctrl_ops pcs_ops = {
.set_state = pcs_set_state,
};
-int pinctrl_single_probe(struct device_d *dev)
+int pinctrl_single_probe(struct device *dev)
{
struct resource *iores;
struct pinctrl_single *pcs;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret = 0;
pcs = xzalloc(sizeof(*pcs));
@@ -203,8 +202,9 @@ static __maybe_unused struct of_device_id pcs_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, pcs_dt_ids);
-static struct driver_d pcs_driver = {
+static struct driver pcs_driver = {
.name = "pinctrl-single",
.probe = pinctrl_single_probe,
.of_compatible = DRV_OF_COMPAT(pcs_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-stm32.c b/drivers/pinctrl/pinctrl-stm32.c
index fc0cc78f43..63a01b6ec6 100644
--- a/drivers/pinctrl/pinctrl-stm32.c
+++ b/drivers/pinctrl/pinctrl-stm32.c
@@ -86,7 +86,8 @@ static inline u32 stm32_gpio_get_alt(u32 function)
return 0;
}
-static int __stm32_pinctrl_set_state(struct device_d *dev, struct device_node *pins)
+static int __stm32_pinctrl_set_state(struct device *dev,
+ struct device_node *pins)
{
int ret;
@@ -100,8 +101,7 @@ static int __stm32_pinctrl_set_state(struct device_d *dev, struct device_node *p
of_get_property(pins, "pinmux", &num_pins);
num_pins /= sizeof(__be32);
if (!num_pins) {
- dev_err(dev, "Invalid pinmux property in %s\n",
- pins->full_name);
+ dev_err(dev, "Invalid pinmux property in %pOF\n", pins);
return -EINVAL;
}
@@ -128,7 +128,7 @@ static int __stm32_pinctrl_set_state(struct device_d *dev, struct device_node *p
else if (of_get_property(pins, "output-high", NULL))
dir = PIN_OUTPUT_HIGH;
- dev_dbg(dev, "%s: multiplexing %d pins\n", pins->full_name, num_pins);
+ dev_dbg(dev, "%pOF: multiplexing %d pins\n", pins, num_pins);
for (i = 0; i < num_pins; i++) {
struct stm32_gpio_bank *bank = NULL;
@@ -178,7 +178,7 @@ static int __stm32_pinctrl_set_state(struct device_d *dev, struct device_node *p
static int stm32_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
{
struct stm32_pinctrl *pinctrl = to_stm32_pinctrl(pdev);
- struct device_d *dev = pdev->dev;
+ struct device *dev = pdev->dev;
struct device_node *pins;
void *prop;
int ret;
@@ -268,9 +268,9 @@ static struct gpio_ops stm32_gpio_ops = {
static int stm32_gpiochip_add(struct stm32_gpio_bank *bank,
struct device_node *np,
- struct device_d *parent)
+ struct device *parent)
{
- struct device_d *dev;
+ struct device *dev;
struct resource *iores;
enum { PINCTRL_PHANDLE, GPIOCTRL_OFFSET, PINCTRL_OFFSET, PINCOUNT, GPIO_RANGE_NCELLS };
const __be32 *gpio_ranges;
@@ -285,8 +285,7 @@ static int stm32_gpiochip_add(struct stm32_gpio_bank *bank,
gpio_ranges = of_get_property(np, "gpio-ranges", &size);
size /= sizeof(__be32);
if (!gpio_ranges || size < GPIO_RANGE_NCELLS) {
- dev_err(dev, "Couldn't read 'gpio-ranges' property in %s\n",
- np->full_name);
+ dev_err(dev, "Couldn't read 'gpio-ranges' property in %pOF\n", np);
return -EINVAL;
}
@@ -297,14 +296,13 @@ static int stm32_gpiochip_add(struct stm32_gpio_bank *bank,
bank->chip.ngpio = ngpios;
if (size > GPIO_RANGE_NCELLS) {
- dev_err(dev, "Unsupported disjunct 'gpio-ranges' in %s\n",
- np->full_name);
+ dev_err(dev, "Unsupported disjunct 'gpio-ranges' in %pOF\n", np);
return -EINVAL;
}
if (ngpios > STM32_GPIO_PINS_PER_BANK) {
- dev_err(dev, "ngpios property expected to be %u at most in %s\n",
- ngpios, np->full_name);
+ dev_err(dev, "ngpios property expected to be %u at most in %pOF\n",
+ ngpios, np);
return -EINVAL;
}
@@ -339,19 +337,14 @@ static struct pinctrl_ops stm32_pinctrl_ops = {
.set_state = stm32_pinctrl_set_state,
};
-static int stm32_pinctrl_probe(struct device_d *dev)
+static int stm32_pinctrl_probe(struct device *dev)
{
struct stm32_pinctrl *pinctrl;
unsigned nbanks = 0;
struct stm32_gpio_bank *gpio_bank;
- struct device_node *np = dev->device_node, *child;
+ struct device_node *np = dev->of_node, *child;
int ret;
- if (!of_find_property(np, "pins-are-numbered", NULL)) {
- dev_err(dev, "only pins-are-numbered format supported\n");
- return -EINVAL;
- }
-
for_each_available_child_of_node(np, child)
if (of_property_read_bool(child, "gpio-controller"))
nbanks++;
@@ -367,12 +360,6 @@ static int stm32_pinctrl_probe(struct device_d *dev)
dev_dbg(dev, "proceeding without hw spinlock support: (%d)\n",
ret);
- ret = pinctrl_register(&pinctrl->pdev);
- if (ret) {
- dev_dbg(dev, "pinctrl_register failed: (%d)\n", ret);
- return ret;
- }
-
gpio_bank = pinctrl->gpio_banks;
for_each_available_child_of_node(np, child) {
if (!of_property_read_bool(child, "gpio-controller"))
@@ -388,9 +375,7 @@ static int stm32_pinctrl_probe(struct device_d *dev)
gpio_bank++;
}
- dev_dbg(dev, "pinctrl/gpio driver registered\n");
-
- return 0;
+ return pinctrl_register(&pinctrl->pdev);
}
static __maybe_unused struct of_device_id stm32_pinctrl_dt_ids[] = {
@@ -400,10 +385,12 @@ static __maybe_unused struct of_device_id stm32_pinctrl_dt_ids[] = {
{ .compatible = "st,stm32h743-pinctrl" },
{ .compatible = "st,stm32mp157-pinctrl" },
{ .compatible = "st,stm32mp157-z-pinctrl" },
+ { .compatible = "st,stm32mp135-pinctrl" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_pinctrl_dt_ids);
-static struct driver_d stm32_pinctrl_driver = {
+static struct driver stm32_pinctrl_driver = {
.name = "stm32-pinctrl",
.probe = stm32_pinctrl_probe,
.of_compatible = DRV_OF_COMPAT(stm32_pinctrl_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
index b288689154..f3a9a0203f 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -62,7 +62,7 @@ struct tegra_xusb_padctl_lane {
};
struct tegra_xusb_padctl {
- struct device_d *dev;
+ struct device *dev;
void __iomem *regs;
struct reset_control *rst;
@@ -268,7 +268,7 @@ static const struct phy_ops sata_phy_ops = {
.power_off = sata_phy_power_off,
};
-static struct phy *tegra_xusb_padctl_xlate(struct device_d *dev,
+static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct tegra_xusb_padctl *padctl = dev->priv;
@@ -365,7 +365,7 @@ static struct pinctrl_ops pinctrl_tegra_xusb_ops = {
.set_state = pinctrl_tegra_xusb_set_state,
};
-static int pinctrl_tegra_xusb_probe(struct device_d *dev)
+static int pinctrl_tegra_xusb_probe(struct device *dev)
{
struct resource *iores;
struct tegra_xusb_padctl *padctl;
@@ -496,8 +496,9 @@ static __maybe_unused struct of_device_id pinctrl_tegra_xusb_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, pinctrl_tegra_xusb_dt_ids);
-static struct driver_d pinctrl_tegra_xusb_driver = {
+static struct driver pinctrl_tegra_xusb_driver = {
.name = "pinctrl-tegra-xusb",
.probe = pinctrl_tegra_xusb_probe,
.of_compatible = DRV_OF_COMPAT(pinctrl_tegra_xusb_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c
index 04431e3875..81644b3744 100644
--- a/drivers/pinctrl/pinctrl-tegra20.c
+++ b/drivers/pinctrl/pinctrl-tegra20.c
@@ -282,7 +282,7 @@ static struct pinctrl_ops pinctrl_tegra20_ops = {
.set_state = pinctrl_tegra20_set_state,
};
-static int pinctrl_tegra20_probe(struct device_d *dev)
+static int pinctrl_tegra20_probe(struct device *dev)
{
struct resource *iores;
struct pinctrl_tegra20 *ctrl;
@@ -316,7 +316,7 @@ static int pinctrl_tegra20_probe(struct device_d *dev)
return ret;
}
- of_pinctrl_select_state(dev->device_node, "boot");
+ of_pinctrl_select_state(dev->of_node, "boot");
return 0;
}
@@ -328,8 +328,9 @@ static __maybe_unused struct of_device_id pinctrl_tegra20_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, pinctrl_tegra20_dt_ids);
-static struct driver_d pinctrl_tegra20_driver = {
+static struct driver pinctrl_tegra20_driver = {
.name = "pinctrl-tegra20",
.probe = pinctrl_tegra20_probe,
.of_compatible = DRV_OF_COMPAT(pinctrl_tegra20_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c
index 78751f9f39..f706ae6bfd 100644
--- a/drivers/pinctrl/pinctrl-tegra30.c
+++ b/drivers/pinctrl/pinctrl-tegra30.c
@@ -857,7 +857,7 @@ static struct pinctrl_ops pinctrl_tegra30_ops = {
.set_state = pinctrl_tegra30_set_state,
};
-static int pinctrl_tegra30_probe(struct device_d *dev)
+static int pinctrl_tegra30_probe(struct device *dev)
{
struct resource *iores;
struct pinctrl_tegra30 *ctrl;
@@ -893,7 +893,7 @@ static int pinctrl_tegra30_probe(struct device_d *dev)
return ret;
}
- of_pinctrl_select_state(dev->device_node, "boot");
+ of_pinctrl_select_state(dev->of_node, "boot");
return 0;
}
@@ -913,8 +913,9 @@ static __maybe_unused struct of_device_id pinctrl_tegra30_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, pinctrl_tegra30_dt_ids);
-static struct driver_d pinctrl_tegra30_driver = {
+static struct driver pinctrl_tegra30_driver = {
.name = "pinctrl-tegra30",
.probe = pinctrl_tegra30_probe,
.of_compatible = DRV_OF_COMPAT(pinctrl_tegra30_dt_ids),
diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c
index 28474ed278..ada0e28751 100644
--- a/drivers/pinctrl/pinctrl-vf610.c
+++ b/drivers/pinctrl/pinctrl-vf610.c
@@ -12,7 +12,7 @@
#include <malloc.h>
#include <gpio.h>
-#include <mach/iomux-vf610.h>
+#include <mach/imx/iomux-vf610.h>
enum {
PINCTRL_VF610_MUX_LINE_SIZE = 20,
@@ -41,8 +41,7 @@ static int pinctrl_vf610_set_state(struct pinctrl_device *pdev,
return -EINVAL;
if (!size || size % PINCTRL_VF610_MUX_LINE_SIZE) {
- dev_err(pdev->dev, "Invalid fsl,pins property in %s\n",
- np->full_name);
+ dev_err(pdev->dev, "Invalid fsl,pins property in %pOF\n", np);
return -EINVAL;
}
@@ -113,7 +112,7 @@ static struct pinctrl_ops pinctrl_vf610_ops = {
.get_direction = pinctrl_vf610_get_direction,
};
-static int pinctrl_vf610_probe(struct device_d *dev)
+static int pinctrl_vf610_probe(struct device *dev)
{
int ret;
struct resource *io;
@@ -142,8 +141,9 @@ static __maybe_unused struct of_device_id pinctrl_vf610_dt_ids[] = {
{ .compatible = "fsl,vf610-iomuxc", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, pinctrl_vf610_dt_ids);
-static struct driver_d pinctrl_vf610_driver = {
+static struct driver pinctrl_vf610_driver = {
.name = "vf610-pinctrl",
.probe = pinctrl_vf610_probe,
.of_compatible = DRV_OF_COMPAT(pinctrl_vf610_dt_ids),
diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c
index 423040ecf3..95e7b0ea96 100644
--- a/drivers/pinctrl/pinctrl.c
+++ b/drivers/pinctrl/pinctrl.c
@@ -138,8 +138,8 @@ int of_pinctrl_select_state(struct device_node *np, const char *name)
/* Look up the pin configuration node */
np_config = of_find_node_by_phandle(phandle);
if (!np_config) {
- pr_err("prop %s %s index %i invalid phandle\n",
- np->full_name, propname, config);
+ pr_err("prop %pOF %s index %i invalid phandle\n",
+ np, propname, config);
ret = -EINVAL;
goto err;
}
@@ -161,18 +161,18 @@ int of_pinctrl_select_state_default(struct device_node *np)
return of_pinctrl_select_state(np, "default");
}
-int pinctrl_select_state(struct device_d *dev, const char *name)
+int pinctrl_select_state(struct device *dev, const char *name)
{
struct device_node *np;
- np = dev->device_node;
+ np = dev->of_node;
if (!np)
return 0;
return of_pinctrl_select_state(np, name);
}
-int pinctrl_select_state_default(struct device_d *dev)
+int pinctrl_select_state_default(struct device *dev)
{
return pinctrl_select_state(dev, "default");
}
@@ -182,11 +182,11 @@ int pinctrl_register(struct pinctrl_device *pdev)
if (!IS_ENABLED(CONFIG_PINCTRL))
return -ENOSYS;
- BUG_ON(!pdev->dev->device_node);
+ BUG_ON(!pdev->dev->of_node);
list_add_tail(&pdev->list, &pinctrl_list);
- pdev->node = pdev->dev->device_node;
+ pdev->node = pdev->dev->of_node;
pinctrl_select_state_default(pdev->dev);
diff --git a/drivers/pmdomain/Kconfig b/drivers/pmdomain/Kconfig
new file mode 100644
index 0000000000..82095ae078
--- /dev/null
+++ b/drivers/pmdomain/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "PM Domains"
+
+source "drivers/pmdomain/imx/Kconfig"
+source "drivers/pmdomain/ti/Kconfig"
+
+endmenu
diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile
new file mode 100644
index 0000000000..25983a67e1
--- /dev/null
+++ b/drivers/pmdomain/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += imx/
+obj-y += ti/
diff --git a/drivers/pmdomain/imx/Kconfig b/drivers/pmdomain/imx/Kconfig
new file mode 100644
index 0000000000..f611a69f78
--- /dev/null
+++ b/drivers/pmdomain/imx/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "i.MX PM Domains"
+
+config IMX_GPCV2_PM_DOMAINS
+ bool "i.MX GPCv2 PM domains"
+ depends on ARCH_IMX7 || ARCH_IMX8M || COMPILE_TEST
+ select PM_GENERIC_DOMAINS
+ default y if ARCH_IMX7 || ARCH_IMX8M
+
+config IMX8M_BLK_CTRL
+ bool "i.MX8MP HSIO blk-ctrl" if COMPILE_TEST
+ default ARCH_IMX8MP && IMX_GPCV2_PM_DOMAINS
+ depends on PM_GENERIC_DOMAINS
+ depends on COMMON_CLK
+
+endmenu
diff --git a/drivers/pmdomain/imx/Makefile b/drivers/pmdomain/imx/Makefile
new file mode 100644
index 0000000000..1364944d5a
--- /dev/null
+++ b/drivers/pmdomain/imx/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
+obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8mp-blk-ctrl.o
diff --git a/drivers/soc/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c
index 304f6d81eb..fc2fbdaf14 100644
--- a/drivers/soc/imx/gpcv2.c
+++ b/drivers/pmdomain/imx/gpcv2.c
@@ -10,7 +10,7 @@
#include <of_device.h>
#include <common.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <clock.h>
@@ -302,7 +302,7 @@ struct imx_pgc_domain {
const int voltage;
const bool keep_clocks;
- struct device_d *dev;
+ struct device *dev;
unsigned int pgc_sw_pup_reg;
unsigned int pgc_sw_pdn_reg;
@@ -326,12 +326,16 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
u32 reg_val, pgc;
int ret;
- if (!IS_ERR(domain->regulator)) {
+ if (!IS_ERR(domain->regulator))
ret = regulator_enable(domain->regulator);
- if (ret) {
- dev_err(domain->dev, "failed to enable regulator\n");
- return ret;
- }
+ else if (PTR_ERR(domain->regulator) == -ENOENT)
+ ret = -ENOENT;
+ else
+ ret = 0;
+
+ if (ret) {
+ dev_err(domain->dev, "failed to enable regulator\n");
+ return ret;
}
/* Enable reset clocks for all devices in the domain */
@@ -1136,7 +1140,7 @@ static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = {
.pgc_regs = &imx7_pgc_regs,
};
-static int imx_pgc_domain_probe(struct device_d *dev)
+static int imx_pgc_domain_probe(struct device *dev)
{
struct imx_pgc_domain *domain = dev->priv;
int ret;
@@ -1145,9 +1149,21 @@ static int imx_pgc_domain_probe(struct device_d *dev)
domain->regulator = regulator_get(domain->dev, "power");
if (IS_ERR(domain->regulator)) {
- if (PTR_ERR(domain->regulator) != -ENODEV)
+ /* On i.MX8M, we usually set up PMIC in early board code once
+ * and don't do dynamic voltage regulation in barebox.
+ * This means, even with deferred probe we will never succeed.
+ * Instead, let's just print an info message and continue
+ *
+ * If you actually require access to a regulator here that has
+ * a driver, enable deep probe for your board.
+ */
+ if (PTR_ERR(domain->regulator) == -EPROBE_DEFER) {
+ dev_info(domain->dev, "Failed to get domain's regulator (ignored)\n");
+ domain->regulator = ERR_PTR(-ENOENT);
+ } else if (PTR_ERR(domain->regulator) != -ENODEV) {
return dev_err_probe(domain->dev, PTR_ERR(domain->regulator),
"Failed to get domain's regulator\n");
+ }
} else if (domain->voltage) {
regulator_set_voltage(domain->regulator,
domain->voltage, domain->voltage);
@@ -1178,7 +1194,7 @@ static int imx_pgc_domain_probe(struct device_d *dev)
goto out_domain_unmap;
}
- ret = of_genpd_add_provider_simple(domain->dev->device_node,
+ ret = of_genpd_add_provider_simple(domain->dev->of_node,
&domain->genpd);
if (ret) {
dev_err(domain->dev, "Failed to add genpd provider\n");
@@ -1199,14 +1215,14 @@ static const struct platform_device_id imx_pgc_domain_id[] = {
{ },
};
-static struct driver_d imx_pgc_domain_driver = {
+static struct driver imx_pgc_domain_driver = {
.name = "imx-pgc",
.probe = imx_pgc_domain_probe,
.id_table = imx_pgc_domain_id,
};
coredevice_platform_driver(imx_pgc_domain_driver);
-static int imx_gpcv2_probe(struct device_d *dev)
+static int imx_gpcv2_probe(struct device *dev)
{
const struct imx_pgc_domain_data *domain_data =
of_device_get_match_data(dev);
@@ -1222,7 +1238,7 @@ static int imx_gpcv2_probe(struct device_d *dev)
struct regmap *regmap;
int ret, pass = 0;
- pgc_np = of_get_child_by_name(dev->device_node, "pgc");
+ pgc_np = of_get_child_by_name(dev->of_node, "pgc");
if (!pgc_np) {
dev_err(dev, "No power domains specified in DT\n");
return -EINVAL;
@@ -1247,7 +1263,7 @@ again:
for_each_child_of_node(pgc_np, np) {
bool child_domain = of_property_read_bool(np, "power-domains");
struct imx_pgc_domain *domain;
- struct device_d *pd_dev;
+ struct device *pd_dev;
u32 domain_index;
if ((pass == 0 && child_domain) || (pass == 1 && !child_domain))
@@ -1277,8 +1293,8 @@ again:
domain->genpd.power_off = imx_pgc_power_down;
pd_dev = xzalloc(sizeof(*pd_dev));
- pd_dev->device_node = np;
- pd_dev->device_node->dev = pd_dev;
+ pd_dev->of_node = np;
+ pd_dev->of_node->dev = pd_dev;
pd_dev->id = domain_index;
pd_dev->parent = dev;
pd_dev->priv = domain;
@@ -1305,8 +1321,9 @@ static const struct of_device_id imx_gpcv2_dt_ids[] = {
{ .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, },
{ }
};
+MODULE_DEVICE_TABLE(of, imx_gpcv2_dt_ids);
-static struct driver_d imx_gpcv2_driver = {
+static struct driver imx_gpcv2_driver = {
.name = "imx-gpcv2",
.probe = imx_gpcv2_probe,
.of_compatible = DRV_OF_COMPAT(imx_gpcv2_dt_ids),
diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c
new file mode 100644
index 0000000000..b71a4a7937
--- /dev/null
+++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <of.h>
+#include <of_device.h>
+#include <linux/device.h>
+#include <pm_domain.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/power/imx8mp-power.h>
+
+#define GPR_REG0 0x0
+#define PCIE_CLOCK_MODULE_EN BIT(0)
+#define USB_CLOCK_MODULE_EN BIT(1)
+#define PCIE_PHY_APB_RST BIT(4)
+#define PCIE_PHY_INIT_RST BIT(5)
+#define GPR_REG1 0x4
+#define PLL_LOCK BIT(13)
+#define GPR_REG2 0x8
+#define P_PLL_MASK GENMASK(5, 0)
+#define M_PLL_MASK GENMASK(15, 6)
+#define S_PLL_MASK GENMASK(18, 16)
+#define GPR_REG3 0xc
+#define PLL_CKE BIT(17)
+#define PLL_RST BIT(31)
+
+struct imx8mp_blk_ctrl_domain;
+
+struct imx8mp_blk_ctrl {
+ struct device *dev;
+ struct device *bus_power_dev;
+ struct regmap *regmap;
+ struct imx8mp_blk_ctrl_domain *domains;
+ struct genpd_onecell_data onecell_data;
+ void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
+ void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
+};
+
+struct imx8mp_blk_ctrl_domain_data {
+ const char *name;
+ const char * const *clk_names;
+ int num_clks;
+ const char *gpc_name;
+};
+
+#define DOMAIN_MAX_CLKS 2
+
+struct imx8mp_blk_ctrl_domain {
+ struct generic_pm_domain genpd;
+ const struct imx8mp_blk_ctrl_domain_data *data;
+ struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
+ struct device *power_dev;
+ struct imx8mp_blk_ctrl *bc;
+ int id;
+};
+
+struct imx8mp_blk_ctrl_data {
+ int max_reg;
+ int (*probe) (struct imx8mp_blk_ctrl *bc);
+ void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
+ void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
+ const struct imx8mp_blk_ctrl_domain_data *domains;
+ int num_domains;
+};
+
+static inline struct imx8mp_blk_ctrl_domain *
+to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd)
+{
+ return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd);
+}
+
+struct clk_hsio_pll {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+static inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw)
+{
+ return container_of(hw, struct clk_hsio_pll, hw);
+}
+
+static int clk_hsio_pll_prepare(struct clk_hw *hw)
+{
+ struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
+ u32 val;
+
+ /* set the PLL configuration */
+ regmap_update_bits(clk->regmap, GPR_REG2,
+ P_PLL_MASK | M_PLL_MASK | S_PLL_MASK,
+ FIELD_PREP(P_PLL_MASK, 12) |
+ FIELD_PREP(M_PLL_MASK, 800) |
+ FIELD_PREP(S_PLL_MASK, 4));
+
+ /* de-assert PLL reset */
+ regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST);
+
+ /* enable PLL */
+ regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE);
+
+ return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val,
+ val & PLL_LOCK, 100);
+}
+
+static void clk_hsio_pll_unprepare(struct clk_hw *hw)
+{
+ struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
+
+ regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0);
+}
+
+static unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 100000000;
+}
+
+static const struct clk_ops clk_hsio_pll_ops = {
+ .enable = clk_hsio_pll_prepare,
+ .disable = clk_hsio_pll_unprepare,
+ .recalc_rate = clk_hsio_pll_recalc_rate,
+};
+
+static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc)
+{
+ struct clk_hsio_pll *clk_hsio_pll;
+ struct clk_hw *hw;
+ struct clk_init_data init = {};
+ int ret;
+
+ clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL);
+ if (!clk_hsio_pll)
+ return -ENOMEM;
+
+ init.name = "hsio_pll";
+ init.ops = &clk_hsio_pll_ops;
+ init.parent_names = (const char *[]){"osc_24m"};
+ init.num_parents = 1;
+
+ clk_hsio_pll->regmap = bc->regmap;
+ clk_hsio_pll->hw.init = &init;
+
+ hw = &clk_hsio_pll->hw;
+ ret = clk_hw_register(bc->bus_power_dev, hw);
+ if (ret)
+ return ret;
+
+ return of_clk_add_hw_provider(dev_of_node(bc->dev), of_clk_hw_simple_get, hw);
+}
+
+static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
+ struct imx8mp_blk_ctrl_domain *domain)
+{
+ switch (domain->id) {
+ case IMX8MP_HSIOBLK_PD_USB:
+ regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
+ break;
+ case IMX8MP_HSIOBLK_PD_PCIE:
+ regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
+ break;
+ case IMX8MP_HSIOBLK_PD_PCIE_PHY:
+ regmap_set_bits(bc->regmap, GPR_REG0,
+ PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
+ break;
+ default:
+ break;
+ }
+}
+
+static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
+ struct imx8mp_blk_ctrl_domain *domain)
+{
+ switch (domain->id) {
+ case IMX8MP_HSIOBLK_PD_USB:
+ regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
+ break;
+ case IMX8MP_HSIOBLK_PD_PCIE:
+ regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
+ break;
+ case IMX8MP_HSIOBLK_PD_PCIE_PHY:
+ regmap_clear_bits(bc->regmap, GPR_REG0,
+ PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
+ break;
+ default:
+ break;
+ }
+}
+
+static int imx8mp_hsio_propagate_adb_handshake(struct imx8mp_blk_ctrl *bc)
+{
+ int ret;
+ struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks;
+ int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks;
+ static int once;
+
+ if (once)
+ return 0;
+
+ /*
+ * enable USB clock for a moment for the power-on ADB handshake
+ * to proceed
+ */
+ ret = clk_bulk_prepare_enable(num_clks, usb_clk);
+ if (ret)
+ return ret;
+
+ regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
+
+ udelay(5);
+
+ regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
+ clk_bulk_disable_unprepare(num_clks, usb_clk);
+
+ once++;
+
+ return 0;
+}
+
+static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = {
+ [IMX8MP_HSIOBLK_PD_USB] = {
+ .name = "hsioblk-usb",
+ .clk_names = (const char *[]){ "usb" },
+ .num_clks = 1,
+ .gpc_name = "usb",
+ },
+ [IMX8MP_HSIOBLK_PD_USB_PHY1] = {
+ .name = "hsioblk-usb-phy1",
+ .gpc_name = "usb-phy1",
+ },
+ [IMX8MP_HSIOBLK_PD_USB_PHY2] = {
+ .name = "hsioblk-usb-phy2",
+ .gpc_name = "usb-phy2",
+ },
+ [IMX8MP_HSIOBLK_PD_PCIE] = {
+ .name = "hsioblk-pcie",
+ .clk_names = (const char *[]){ "pcie" },
+ .num_clks = 1,
+ .gpc_name = "pcie",
+ },
+ [IMX8MP_HSIOBLK_PD_PCIE_PHY] = {
+ .name = "hsioblk-pcie-phy",
+ .gpc_name = "pcie-phy",
+ },
+};
+
+static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = {
+ .max_reg = 0x24,
+ .probe = imx8mp_hsio_blk_ctrl_probe,
+ .power_on = imx8mp_hsio_blk_ctrl_power_on,
+ .power_off = imx8mp_hsio_blk_ctrl_power_off,
+ .domains = imx8mp_hsio_domain_data,
+ .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data),
+};
+
+static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
+{
+ struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
+ const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
+ struct imx8mp_blk_ctrl *bc = domain->bc;
+ int ret;
+
+ /* make sure bus domain is awake */
+ ret = pm_runtime_resume_and_get_genpd(bc->bus_power_dev);
+ if (ret < 0) {
+ dev_err(bc->dev, "failed to power up bus domain\n");
+ return ret;
+ }
+
+ ret = imx8mp_hsio_propagate_adb_handshake(bc);
+ if (ret) {
+ dev_err(bc->dev, "failed to propagate adb handshake\n");
+ goto bus_put;
+ }
+
+ /* enable upstream clocks */
+ ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
+ if (ret) {
+ dev_err(bc->dev, "failed to enable clocks\n");
+ goto bus_put;
+ }
+
+ /* domain specific blk-ctrl manipulation */
+ bc->power_on(bc, domain);
+
+ /* power up upstream GPC domain */
+ ret = pm_runtime_resume_and_get_genpd(domain->power_dev);
+ if (ret < 0) {
+ dev_err(bc->dev, "failed to power up peripheral domain\n");
+ goto clk_disable;
+ }
+
+ clk_bulk_disable_unprepare(data->num_clks, domain->clks);
+
+ return 0;
+
+clk_disable:
+ clk_bulk_disable_unprepare(data->num_clks, domain->clks);
+bus_put:
+ pm_runtime_put_genpd(bc->bus_power_dev);
+
+ return ret;
+}
+
+static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd)
+{
+ struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
+ const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
+ struct imx8mp_blk_ctrl *bc = domain->bc;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
+ if (ret) {
+ dev_err(bc->dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ /* domain specific blk-ctrl manipulation */
+ bc->power_off(bc, domain);
+
+ clk_bulk_disable_unprepare(data->num_clks, domain->clks);
+
+ /* power down upstream GPC domain */
+ pm_runtime_put_genpd(domain->power_dev);
+
+ /* allow bus domain to suspend */
+ pm_runtime_put_genpd(bc->bus_power_dev);
+
+ return 0;
+}
+
+static int imx8mp_blk_ctrl_probe(struct device *dev)
+{
+ const struct imx8mp_blk_ctrl_data *bc_data;
+ struct imx8mp_blk_ctrl *bc;
+ void __iomem *base;
+ int num_domains, i, ret;
+
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+
+ bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
+ if (!bc)
+ return -ENOMEM;
+
+ bc->dev = dev;
+
+ bc_data = of_device_get_match_data(dev);
+ num_domains = bc_data->num_domains;
+
+ base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap_config.max_register = bc_data->max_reg;
+ bc->regmap = regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(bc->regmap))
+ return dev_err_probe(dev, PTR_ERR(bc->regmap),
+ "failed to init regmap\n");
+
+ bc->domains = devm_kcalloc(dev, num_domains,
+ sizeof(struct imx8mp_blk_ctrl_domain),
+ GFP_KERNEL);
+ if (!bc->domains)
+ return -ENOMEM;
+
+ bc->onecell_data.num_domains = num_domains;
+ bc->onecell_data.domains =
+ devm_kcalloc(dev, num_domains,
+ sizeof(struct generic_pm_domain *), GFP_KERNEL);
+ if (!bc->onecell_data.domains)
+ return -ENOMEM;
+
+ bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
+ if (IS_ERR(bc->bus_power_dev))
+ return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
+ "failed to attach bus power domain\n");
+
+ bc->power_off = bc_data->power_off;
+ bc->power_on = bc_data->power_on;
+
+ for (i = 0; i < num_domains; i++) {
+ const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i];
+ struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
+ int j;
+
+ domain->data = data;
+
+ for (j = 0; j < data->num_clks; j++)
+ domain->clks[j].id = data->clk_names[j];
+
+ ret = clk_bulk_get(dev, data->num_clks, domain->clks);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to get clock\n");
+ goto cleanup_pds;
+ }
+
+ domain->power_dev =
+ dev_pm_domain_attach_by_name(dev, data->gpc_name);
+ if (IS_ERR(domain->power_dev)) {
+ dev_err_probe(dev, PTR_ERR(domain->power_dev),
+ "failed to attach power domain %s\n",
+ data->gpc_name);
+ ret = PTR_ERR(domain->power_dev);
+ goto cleanup_pds;
+ }
+
+ domain->genpd.name = data->name;
+ domain->genpd.power_on = imx8mp_blk_ctrl_power_on;
+ domain->genpd.power_off = imx8mp_blk_ctrl_power_off;
+ domain->bc = bc;
+ domain->id = i;
+
+ ret = pm_genpd_init(&domain->genpd, NULL, true);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to init power domain\n");
+ dev_pm_domain_detach(domain->power_dev, true);
+ goto cleanup_pds;
+ }
+
+ bc->onecell_data.domains[i] = &domain->genpd;
+ }
+
+ ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add power domain provider\n");
+ goto cleanup_pds;
+ }
+
+ if (bc_data->probe) {
+ ret = bc_data->probe(bc);
+ if (ret)
+ goto cleanup_provider;
+ }
+
+ return 0;
+
+cleanup_provider:
+ of_genpd_del_provider(dev->of_node);
+cleanup_pds:
+ for (i--; i >= 0; i--) {
+ pm_genpd_remove(&bc->domains[i].genpd);
+ dev_pm_domain_detach(bc->domains[i].power_dev, true);
+ }
+
+ dev_pm_domain_detach(bc->bus_power_dev, true);
+
+ return ret;
+}
+
+static const struct of_device_id imx8mp_blk_ctrl_of_match[] = {
+ {
+ .compatible = "fsl,imx8mp-hsio-blk-ctrl",
+ .data = &imx8mp_hsio_blk_ctl_dev_data,
+ }, {
+ /* Sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match);
+
+static struct driver imx8mp_blk_ctrl_driver = {
+ .probe = imx8mp_blk_ctrl_probe,
+ .name = "imx8mp-blk-ctrl",
+ .of_match_table = imx8mp_blk_ctrl_of_match,
+};
+coredevice_platform_driver(imx8mp_blk_ctrl_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/pmdomain/ti/Kconfig b/drivers/pmdomain/ti/Kconfig
new file mode 100644
index 0000000000..f34a5146c1
--- /dev/null
+++ b/drivers/pmdomain/ti/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config TI_SCI_PM_DOMAINS
+ bool "TI SCI PM Domains Driver"
+ depends on TI_SCI_PROTOCOL
+ depends on PM_GENERIC_DOMAINS
+ help
+ Generic power domain implementation for TI device implementing
+ the TI SCI protocol.
diff --git a/drivers/pmdomain/ti/Makefile b/drivers/pmdomain/ti/Makefile
new file mode 100644
index 0000000000..ab582e04a8
--- /dev/null
+++ b/drivers/pmdomain/ti/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o
diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c
new file mode 100644
index 0000000000..ab0a4d3829
--- /dev/null
+++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI SCI Generic Power Domain Driver
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
+ * J Keerthy <j-keerthy@ti.com>
+ * Dave Gerlach <d-gerlach@ti.com>
+ */
+
+#include <of_device.h>
+#include <common.h>
+#include <abort.h>
+#include <malloc.h>
+#include <io.h>
+#include <init.h>
+#include <pm_domain.h>
+#include <soc/ti/ti_sci_protocol.h>
+#include <dt-bindings/soc/ti,sci_pm_domain.h>
+
+/**
+ * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data
+ * @ti_sci: handle to TI SCI protocol driver that provides ops to
+ * communicate with system control processor.
+ * @dev: pointer to dev for the driver for devm allocs
+ * @pd_list: list of all the power domains on the device
+ * @data: onecell data for genpd core
+ */
+struct ti_sci_genpd_provider {
+ const struct ti_sci_handle *ti_sci;
+ struct device *dev;
+ struct list_head pd_list;
+ struct genpd_onecell_data data;
+};
+
+/**
+ * struct ti_sci_pm_domain: TI specific data needed for power domain
+ * @idx: index of the device that identifies it with the system
+ * control processor.
+ * @exclusive: Permissions for exclusive request or shared request of the
+ * device.
+ * @pd: generic_pm_domain for use with the genpd framework
+ * @node: link for the genpd list
+ * @parent: link to the parent TI SCI genpd provider
+ */
+struct ti_sci_pm_domain {
+ int idx;
+ u8 exclusive;
+ struct generic_pm_domain pd;
+ struct list_head node;
+ struct ti_sci_genpd_provider *parent;
+};
+
+#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
+
+/*
+ * ti_sci_pd_power_off(): genpd power down hook
+ * @domain: pointer to the powerdomain to power off
+ */
+static int ti_sci_pd_power_off(struct generic_pm_domain *domain)
+{
+ struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
+ const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
+
+ return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx);
+}
+
+/*
+ * ti_sci_pd_power_on(): genpd power up hook
+ * @domain: pointer to the powerdomain to power on
+ */
+static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
+{
+ struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
+ const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
+
+ if (pd->exclusive)
+ return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci,
+ pd->idx);
+ else
+ return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx);
+}
+
+/*
+ * ti_sci_pd_xlate(): translation service for TI SCI genpds
+ * @genpdspec: DT identification data for the genpd
+ * @data: genpd core data for all the powerdomains on the device
+ */
+static struct generic_pm_domain *ti_sci_pd_xlate(
+ struct of_phandle_args *genpdspec,
+ void *data)
+{
+ struct genpd_onecell_data *genpd_data = data;
+ unsigned int idx = genpdspec->args[0];
+
+ if (genpdspec->args_count != 1 && genpdspec->args_count != 2)
+ return ERR_PTR(-EINVAL);
+
+ if (idx >= genpd_data->num_domains) {
+ pr_err("%s: invalid domain index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!genpd_data->domains[idx])
+ return ERR_PTR(-ENOENT);
+
+ genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive =
+ genpdspec->args[1];
+
+ return genpd_data->domains[idx];
+}
+
+static const struct of_device_id ti_sci_pm_domain_matches[] = {
+ { .compatible = "ti,sci-pm-domain", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
+
+static int ti_sci_pm_domain_probe(struct device *dev)
+{
+ struct ti_sci_genpd_provider *pd_provider;
+ struct ti_sci_pm_domain *pd;
+ struct device_node *np = NULL;
+ struct of_phandle_args args;
+ int ret;
+ u32 max_id = 0;
+ int index;
+
+ pd_provider = xzalloc(sizeof(*pd_provider));
+
+ pd_provider->ti_sci = ti_sci_get_handle(dev);
+ if (IS_ERR(pd_provider->ti_sci))
+ return PTR_ERR(pd_provider->ti_sci);
+
+ pd_provider->dev = dev;
+
+ INIT_LIST_HEAD(&pd_provider->pd_list);
+
+ /* Find highest device ID used for power domains */
+ while (1) {
+ np = of_find_node_with_property(np, "power-domains");
+ if (!np)
+ break;
+
+ index = 0;
+
+ while (1) {
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells",
+ index, &args);
+ if (ret)
+ break;
+
+ if (args.args_count >= 1 && args.np == dev->of_node) {
+ if (args.args[0] > max_id)
+ max_id = args.args[0];
+
+ pd = xzalloc(sizeof(*pd));
+
+ pd->pd.name = basprintf("pd:%d", args.args[0]);
+ if (!pd->pd.name)
+ return -ENOMEM;
+
+ pd->pd.power_off = ti_sci_pd_power_off;
+ pd->pd.power_on = ti_sci_pd_power_on;
+ pd->idx = args.args[0];
+ pd->parent = pd_provider;
+
+ pm_genpd_init(&pd->pd, NULL, true);
+
+ list_add(&pd->node, &pd_provider->pd_list);
+ }
+ index++;
+ }
+ }
+
+ pd_provider->data.domains =
+ xzalloc((max_id + 1) * sizeof(*pd_provider->data.domains));
+
+ pd_provider->data.num_domains = max_id + 1;
+ pd_provider->data.xlate = ti_sci_pd_xlate;
+
+ list_for_each_entry(pd, &pd_provider->pd_list, node)
+ pd_provider->data.domains[pd->idx] = &pd->pd;
+
+ return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data);
+}
+
+static struct driver ti_sci_pm_domains_driver = {
+ .probe = ti_sci_pm_domain_probe,
+ .name = "ti_sci_pm_domains",
+ .of_match_table = ti_sci_pm_domain_matches,
+};
+core_platform_driver(ti_sci_pm_domains_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
+MODULE_AUTHOR("Dave Gerlach");
diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c
index 45b0d274e7..cafa1387ce 100644
--- a/drivers/power/reset/gpio-poweroff.c
+++ b/drivers/power/reset/gpio-poweroff.c
@@ -10,14 +10,14 @@
#include <common.h>
#include <driver.h>
#include <poweroff.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#define DEFAULT_TIMEOUT_MS 3000
/*
* Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global.
*/
-static int reset_gpio;
+static struct gpio_desc *reset_gpio;
static u32 timeout = DEFAULT_TIMEOUT_MS;
static u32 active_delay = 100;
static u32 inactive_delay = 100;
@@ -25,15 +25,15 @@ static u32 inactive_delay = 100;
static void gpio_poweroff_do_poweroff(struct poweroff_handler *handler)
{
/* drive it active, also inactive->active edge */
- gpio_direction_active(reset_gpio, true);
+ gpiod_direction_output(reset_gpio, true);
mdelay(active_delay);
/* drive inactive, also active->inactive edge */
- gpio_set_active(reset_gpio, false);
+ gpiod_set_value(reset_gpio, false);
mdelay(inactive_delay);
/* drive it active, also inactive->active edge */
- gpio_set_active(reset_gpio, true);
+ gpiod_set_value(reset_gpio, true);
/* give it some time */
mdelay(timeout);
@@ -43,9 +43,9 @@ static void gpio_poweroff_do_poweroff(struct poweroff_handler *handler)
static struct poweroff_handler handler;
-static int gpio_poweroff_probe(struct device_d *dev)
+static int gpio_poweroff_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
bool input = false;
enum gpiod_flags flags;
@@ -65,8 +65,8 @@ static int gpio_poweroff_probe(struct device_d *dev)
of_property_read_u32(np, "timeout-ms", &timeout);
reset_gpio = gpiod_get(dev, NULL, flags);
- if (reset_gpio < 0)
- return reset_gpio;
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
handler.name = "gpio-poweroff";
handler.poweroff = gpio_poweroff_do_poweroff;
@@ -79,8 +79,9 @@ static const struct of_device_id of_gpio_poweroff_match[] = {
{ .compatible = "gpio-poweroff", },
{},
};
+MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match);
-static struct driver_d gpio_poweroff_driver = {
+static struct driver gpio_poweroff_driver = {
.name = "poweroff-gpio",
.of_compatible = of_gpio_poweroff_match,
.probe = gpio_poweroff_probe,
diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c
index 72c690a3cf..ed2748f910 100644
--- a/drivers/power/reset/gpio-restart.c
+++ b/drivers/power/reset/gpio-restart.c
@@ -9,10 +9,10 @@
#include <common.h>
#include <driver.h>
#include <restart.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
struct gpio_restart {
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
struct restart_handler restart_handler;
u32 active_delay_ms;
u32 inactive_delay_ms;
@@ -25,15 +25,15 @@ static void __noreturn gpio_restart_handle(struct restart_handler *this)
container_of(this, struct gpio_restart, restart_handler);
/* drive it active, also inactive->active edge */
- gpio_direction_active(gpio_restart->reset_gpio, true);
+ gpiod_direction_output(gpio_restart->reset_gpio, true);
mdelay(gpio_restart->active_delay_ms);
/* drive inactive, also active->inactive edge */
- gpio_set_active(gpio_restart->reset_gpio, false);
+ gpiod_direction_output(gpio_restart->reset_gpio, false);
mdelay(gpio_restart->inactive_delay_ms);
/* drive it active, also inactive->active edge */
- gpio_set_active(gpio_restart->reset_gpio, true);
+ gpiod_direction_output(gpio_restart->reset_gpio, true);
/* give it some time */
mdelay(gpio_restart->wait_delay_ms);
@@ -43,10 +43,11 @@ static void __noreturn gpio_restart_handle(struct restart_handler *this)
panic("Unable to restart system\n");
}
-static int gpio_restart_probe(struct device_d *dev)
+static int gpio_restart_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct gpio_restart *gpio_restart;
+ struct gpio_desc *gpiod;
bool open_source = false;
u32 property;
int ret;
@@ -55,15 +56,11 @@ static int gpio_restart_probe(struct device_d *dev)
open_source = of_property_read_bool(np, "open-source");
- gpio_restart->reset_gpio = gpiod_get(dev, NULL,
- open_source ? GPIOD_IN : GPIOD_OUT_LOW);
- ret = gpio_restart->reset_gpio;
- if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Could not get reset GPIO\n");
- return ret;
- }
+ gpiod = gpiod_get(dev, NULL, open_source ? GPIOD_IN : GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod))
+ return dev_errp_probe(dev, gpiod, "Could not get reset GPIO\n");
+ gpio_restart->reset_gpio = gpiod;
gpio_restart->restart_handler.restart = gpio_restart_handle;
gpio_restart->restart_handler.name = "gpio-restart";
gpio_restart->restart_handler.priority = 129;
@@ -71,7 +68,7 @@ static int gpio_restart_probe(struct device_d *dev)
gpio_restart->inactive_delay_ms = 100;
gpio_restart->wait_delay_ms = 3000;
- ret = of_property_read_u32(dev->device_node, "priority", &property);
+ ret = of_property_read_u32(dev->of_node, "priority", &property);
if (!ret)
gpio_restart->restart_handler.priority = property;
@@ -86,8 +83,9 @@ static const struct of_device_id of_gpio_restart_match[] = {
{ .compatible = "gpio-restart", },
{},
};
+MODULE_DEVICE_TABLE(of, of_gpio_restart_match);
-static struct driver_d gpio_restart_driver = {
+static struct driver gpio_restart_driver = {
.name = "restart-gpio",
.of_compatible = of_gpio_restart_match,
.probe = gpio_restart_probe,
diff --git a/drivers/power/reset/htif-poweroff.c b/drivers/power/reset/htif-poweroff.c
index 8df060d507..e24397f934 100644
--- a/drivers/power/reset/htif-poweroff.c
+++ b/drivers/power/reset/htif-poweroff.c
@@ -19,7 +19,7 @@ static void __noreturn riscvemu_poweroff(struct poweroff_handler *pwr)
__builtin_unreachable();
}
-static int htif_poweroff_probe(struct device_d *dev)
+static int htif_poweroff_probe(struct device *dev)
{
struct resource *iores;
@@ -37,8 +37,9 @@ static const struct of_device_id htif_poweroff_of_match[] = {
{ .compatible = "ucb,htif0" },
{}
};
+MODULE_DEVICE_TABLE(of, htif_poweroff_of_match);
-static struct driver_d htif_poweroff_driver = {
+static struct driver htif_poweroff_driver = {
.name = "htif-poweroff",
.of_compatible = htif_poweroff_of_match,
.probe = htif_poweroff_probe,
diff --git a/drivers/power/reset/nvmem-reboot-mode.c b/drivers/power/reset/nvmem-reboot-mode.c
index c2233d0f3a..1a9422800e 100644
--- a/drivers/power/reset/nvmem-reboot-mode.c
+++ b/drivers/power/reset/nvmem-reboot-mode.c
@@ -34,7 +34,7 @@ static int nvmem_reboot_mode_write(struct reboot_mode_driver *reboot,
return ret;
}
-static int nvmem_reboot_mode_probe(struct device_d *dev)
+static int nvmem_reboot_mode_probe(struct device *dev)
{
struct nvmem_reboot_mode *nvmem_rbm;
struct nvmem_cell *cell;
@@ -43,12 +43,8 @@ static int nvmem_reboot_mode_probe(struct device_d *dev)
int ret;
cell = nvmem_cell_get(dev, "reboot-mode");
- if (IS_ERR(cell)) {
- ret = PTR_ERR(cell);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get the nvmem cell reboot-mode: %pe\n", cell);
- return ret;
- }
+ if (IS_ERR(cell))
+ return dev_errp_probe(dev, cell, "getting nvmem cell 'reboot-mode'\n");
nvmem_rbm = xzalloc(sizeof(*nvmem_rbm));
@@ -82,8 +78,9 @@ static const struct of_device_id nvmem_reboot_mode_of_match[] = {
{ .compatible = "nvmem-reboot-mode" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, nvmem_reboot_mode_of_match);
-static struct driver_d nvmem_reboot_mode_driver = {
+static struct driver nvmem_reboot_mode_driver = {
.probe = nvmem_reboot_mode_probe,
.name = "nvmem-reboot-mode",
.of_compatible = nvmem_reboot_mode_of_match,
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index 5761128f8e..7f940a2d88 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -23,7 +23,7 @@ static int reboot_mode_param_set(struct param_d *p, void *priv)
return reboot->write(reboot, &reboot->magics[i]);
}
-static int reboot_mode_add_param(struct device_d *dev,
+static int reboot_mode_add_param(struct device *dev,
const char *prefix,
struct reboot_mode_driver *reboot)
{
@@ -53,7 +53,7 @@ static int of_reboot_mode_fixup(struct device_node *root, void *ctx)
struct reboot_mode_driver *reboot = ctx;
struct device_node *dstnp, *srcnp, *dstparent;
- srcnp = reboot->dev->device_node;
+ srcnp = reboot->dev->of_node;
dstnp = of_get_node_by_reproducible_name(root, srcnp);
if (dstnp) {
@@ -120,7 +120,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot,
const u32 *reboot_mode, size_t nelems)
{
struct property *prop;
- struct device_node *np = reboot->dev->device_node;
+ struct device_node *np = reboot->dev->of_node;
const char *alias;
size_t nmodes = 0;
int i = 0;
@@ -139,8 +139,13 @@ int reboot_mode_register(struct reboot_mode_driver *reboot,
reboot->nmodes = nmodes;
reboot->nelems = nelems;
- reboot->magics = xzalloc(nmodes * nelems * sizeof(u32));
- reboot->modes = xzalloc(nmodes * sizeof(const char *));
+
+ /*
+ * Allocate one entry more than necessary, because in the loop below
+ * we use an entry before we realize that the property is not valid.
+ */
+ reboot->magics = xzalloc((nmodes + 1) * nelems * sizeof(u32));
+ reboot->modes = xzalloc((nmodes + 1) * sizeof(const char *));
reboot_mode_print(reboot, "registering magic", reboot_mode);
diff --git a/drivers/power/reset/stm32-reboot.c b/drivers/power/reset/stm32-reboot.c
index 809531e713..ffe8dd96ac 100644
--- a/drivers/power/reset/stm32-reboot.c
+++ b/drivers/power/reset/stm32-reboot.c
@@ -9,6 +9,7 @@
#include <linux/err.h>
#include <restart.h>
#include <reset_source.h>
+#include <of_address.h>
#include <asm/io.h>
#include <soc/stm32/reboot.h>
@@ -108,17 +109,19 @@ static void stm32_set_reset_reason(struct stm32_reset *priv,
reset_source_to_string(type), reg);
}
-void stm32mp_system_restart_init(void __iomem *base)
+void stm32mp_system_restart_init(struct device *dev)
{
struct stm32_reset *priv;
+ struct device_node *np = dev_of_node(dev);
priv = xzalloc(sizeof(*priv));
- priv->base = base;
+ priv->base = of_iomap(np, 0);
priv->restart.name = "stm32-rcc";
priv->restart.restart = stm32mp_rcc_restart_handler;
priv->restart.priority = 200;
+ priv->restart.of_node = np;
restart_handler_register(&priv->restart);
diff --git a/drivers/power/reset/syscon-poweroff.c b/drivers/power/reset/syscon-poweroff.c
index 3664c4d8bd..321bd1fc7b 100644
--- a/drivers/power/reset/syscon-poweroff.c
+++ b/drivers/power/reset/syscon-poweroff.c
@@ -9,6 +9,7 @@
#include <common.h>
#include <init.h>
#include <poweroff.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
static struct regmap *map;
@@ -26,23 +27,23 @@ static void syscon_poweroff(struct poweroff_handler *handler)
pr_emerg("Unable to poweroff system\n");
}
-static int syscon_poweroff_probe(struct device_d *dev)
+static int syscon_poweroff_probe(struct device *dev)
{
int mask_err, value_err;
- map = syscon_regmap_lookup_by_phandle(dev->device_node, "regmap");
+ map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
if (IS_ERR(map)) {
dev_err(dev, "unable to get syscon");
return PTR_ERR(map);
}
- if (of_property_read_u32(dev->device_node, "offset", &offset)) {
+ if (of_property_read_u32(dev->of_node, "offset", &offset)) {
dev_err(dev, "unable to read 'offset'");
return -EINVAL;
}
- value_err = of_property_read_u32(dev->device_node, "value", &value);
- mask_err = of_property_read_u32(dev->device_node, "mask", &mask);
+ value_err = of_property_read_u32(dev->of_node, "value", &value);
+ mask_err = of_property_read_u32(dev->of_node, "mask", &mask);
if (value_err && mask_err) {
dev_err(dev, "unable to read 'value' and 'mask'");
return -EINVAL;
@@ -66,8 +67,9 @@ static const struct of_device_id syscon_poweroff_of_match[] = {
{ .compatible = "syscon-poweroff" },
{}
};
+MODULE_DEVICE_TABLE(of, syscon_poweroff_of_match);
-static struct driver_d syscon_poweroff_driver = {
+static struct driver syscon_poweroff_driver = {
.name = "syscon-poweroff",
.of_compatible = syscon_poweroff_of_match,
.probe = syscon_poweroff_probe,
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
index 0cbc9d0803..7d44d9c07e 100644
--- a/drivers/power/reset/syscon-reboot-mode.c
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -7,7 +7,7 @@
#include <init.h>
#include <driver.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <mfd/syscon.h>
#include <linux/reboot-mode.h>
#include <linux/overflow.h>
@@ -46,12 +46,12 @@ static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
return ret;
}
-static int syscon_reboot_mode_probe(struct device_d *dev)
+static int syscon_reboot_mode_probe(struct device *dev)
{
int ret, i, nelems;
struct syscon_reboot_mode *syscon_rbm;
struct reboot_mode_driver *reboot_template;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
u32 *magic;
nelems = of_property_count_elems_of_size(np, "offset", sizeof(__be32));
@@ -67,7 +67,7 @@ static int syscon_reboot_mode_probe(struct device_d *dev)
syscon_rbm->reboot = *reboot_template;
syscon_rbm->reboot.dev = dev;
- syscon_rbm->map = syscon_node_to_regmap(dev->parent->device_node);
+ syscon_rbm->map = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(syscon_rbm->map))
return PTR_ERR(syscon_rbm->map);
@@ -120,8 +120,9 @@ static const struct of_device_id syscon_reboot_mode_of_match[] = {
{ .compatible = "barebox,syscon-reboot-mode", .data = &reboot_nofixup },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, syscon_reboot_mode_of_match);
-static struct driver_d syscon_reboot_mode_driver = {
+static struct driver syscon_reboot_mode_driver = {
.probe = syscon_reboot_mode_probe,
.name = "syscon-reboot-mode",
.of_compatible = syscon_reboot_mode_of_match,
diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c
index 2dbb6c1ddc..61bea6169c 100644
--- a/drivers/power/reset/syscon-reboot.c
+++ b/drivers/power/reset/syscon-reboot.c
@@ -9,6 +9,7 @@
#include <init.h>
#include <restart.h>
#include <mfd/syscon.h>
+#include <linux/regmap.h>
struct syscon_reboot_context {
struct regmap *map;
@@ -32,7 +33,7 @@ static void __noreturn syscon_restart_handle(struct restart_handler *this)
panic("Unable to restart system\n");
}
-static int syscon_reboot_probe(struct device_d *dev)
+static int syscon_reboot_probe(struct device *dev)
{
struct syscon_reboot_context *ctx;
int mask_err, value_err;
@@ -42,18 +43,18 @@ static int syscon_reboot_probe(struct device_d *dev)
if (!ctx)
return -ENOMEM;
- ctx->map = syscon_regmap_lookup_by_phandle(dev->device_node, "regmap");
+ ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
if (IS_ERR(ctx->map)) {
- ctx->map = syscon_node_to_regmap(dev->parent->device_node);
+ ctx->map = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(ctx->map))
return PTR_ERR(ctx->map);
}
- if (of_property_read_u32(dev->device_node, "offset", &ctx->offset))
+ if (of_property_read_u32(dev->of_node, "offset", &ctx->offset))
return -EINVAL;
- value_err = of_property_read_u32(dev->device_node, "value", &ctx->value);
- mask_err = of_property_read_u32(dev->device_node, "mask", &ctx->mask);
+ value_err = of_property_read_u32(dev->of_node, "value", &ctx->value);
+ mask_err = of_property_read_u32(dev->of_node, "mask", &ctx->mask);
if (value_err && mask_err) {
dev_err(dev, "unable to read 'value' and 'mask'");
return -EINVAL;
@@ -71,6 +72,7 @@ static int syscon_reboot_probe(struct device_d *dev)
ctx->restart_handler.name = "syscon-reboot";
ctx->restart_handler.restart = syscon_restart_handle;
ctx->restart_handler.priority = 192;
+ ctx->restart_handler.of_node = dev->of_node;
err = restart_handler_register(&ctx->restart_handler);
if (err)
@@ -83,8 +85,9 @@ static const struct of_device_id syscon_reboot_of_match[] = {
{ .compatible = "syscon-reboot" },
{}
};
+MODULE_DEVICE_TABLE(of, syscon_reboot_of_match);
-static struct driver_d syscon_reboot_driver = {
+static struct driver syscon_reboot_driver = {
.probe = syscon_reboot_probe,
.name = "syscon-reboot",
.of_compatible = syscon_reboot_of_match,
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0b12278e80..eb04f92c6f 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -42,4 +42,11 @@ config PWM_STM32
help
This enables PWM support for STM32 MCUs and MPUs.
+config PWM_ROCKCHIP
+ tristate "Rockchip PWM support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ Generic PWM framework driver for the PWM controller found on
+ Rockchip SoCs.
+
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 9c3b10ae31..4adc083e6c 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
+obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index aba6c2a709..69724e1a5c 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -35,8 +35,8 @@ struct pwm_device {
unsigned long flags;
#define FLAG_REQUESTED 0
struct list_head node;
- struct device_d *hwdev;
- struct device_d dev;
+ struct device *hwdev;
+ struct device dev;
struct pwm_state params;
struct pwm_args args;
@@ -44,6 +44,15 @@ struct pwm_device {
static LIST_HEAD(pwm_list);
+void pwm_print(void)
+{
+ struct pwm_device *pwm;
+
+ list_for_each_entry(pwm, &pwm_list, node)
+ printf("%s\n", pwm->chip->devname);
+}
+EXPORT_SYMBOL(pwm_print);
+
static struct pwm_device *_find_pwm(const char *devname)
{
struct pwm_device *pwm;
@@ -67,7 +76,7 @@ static int set_enable(struct param_d *p, void *priv)
{
struct pwm_device *pwm = priv;
- if (pwm->params.p_enable)
+ if (pwm->params.enabled)
pwm_enable(pwm);
else
pwm_disable(pwm);
@@ -82,22 +91,25 @@ static int set_enable(struct param_d *p, void *priv)
* register a new pwm. pwm->devname must be initialized, usually
* from dev_name(dev) from the hardware driver.
*/
-int pwmchip_add(struct pwm_chip *chip, struct device_d *dev)
+int pwmchip_add(struct pwm_chip *chip)
{
struct pwm_device *pwm;
struct param_d *p;
int ret;
+ if (!chip->devname)
+ return -EINVAL;
+
if (_find_pwm(chip->devname))
return -EBUSY;
pwm = xzalloc(sizeof(*pwm));
pwm->chip = chip;
- pwm->hwdev = dev;
+ pwm->hwdev = chip->dev;
dev_set_name(&pwm->dev, chip->devname);
pwm->dev.id = DEVICE_ID_SINGLE;
- pwm->dev.parent = dev;
+ pwm->dev.parent = chip->dev;
ret = register_device(&pwm->dev);
if (ret)
@@ -106,17 +118,17 @@ int pwmchip_add(struct pwm_chip *chip, struct device_d *dev)
list_add_tail(&pwm->node, &pwm_list);
p = dev_add_param_uint32(&pwm->dev, "duty_ns", apply_params,
- NULL, &pwm->params.duty_ns, "%u", pwm);
+ NULL, &pwm->params.duty_cycle, "%u", pwm);
if (IS_ERR(p))
return PTR_ERR(p);
p = dev_add_param_uint32(&pwm->dev, "period_ns", apply_params,
- NULL, &pwm->params.period_ns, "%u", pwm);
+ NULL, &pwm->params.period, "%u", pwm);
if (IS_ERR(p))
return PTR_ERR(p);
p = dev_add_param_bool(&pwm->dev, "enable", set_enable,
- NULL, &pwm->params.p_enable, pwm);
+ NULL, &pwm->params.enabled, pwm);
if (IS_ERR(p))
return PTR_ERR(p);
@@ -194,7 +206,7 @@ static struct pwm_device *of_node_to_pwm_device(struct device_node *np, int id)
struct pwm_device *pwm;
list_for_each_entry(pwm, &pwm_list, node) {
- if (pwm->hwdev && pwm->hwdev->device_node == np &&
+ if (pwm->hwdev && pwm->hwdev->of_node == np &&
pwm->chip->id == id)
return pwm;
}
@@ -232,7 +244,7 @@ struct pwm_device *of_pwm_request(struct device_node *np, const char *con_id)
pwm->args.polarity = PWM_POLARITY_NORMAL;
if (args.args_count > 2 && args.args[2] & PWM_POLARITY_INVERTED)
- pwm->args.polarity = PWM_POLARITY_INVERTED;
+ pwm->args.polarity = PWM_POLARITY_INVERSED;
ret = __pwm_request(pwm);
if (ret)
@@ -297,9 +309,9 @@ void pwm_init_state(const struct pwm_device *pwm,
/* Then fill it with the reference config */
pwm_get_args(pwm, &args);
- state->period_ns = args.period_ns;
+ state->period = args.period_ns;
state->polarity = args.polarity;
- state->duty_ns = 0;
+ state->duty_cycle = 0;
}
EXPORT_SYMBOL_GPL(pwm_init_state);
@@ -308,13 +320,13 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
struct pwm_chip *chip = pwm->chip;
int ret = -EINVAL;
- if (state->period_ns == 0)
+ if (state->period == 0)
goto err;
- if (state->duty_ns > state->period_ns)
+ if (state->duty_cycle > state->period)
goto err;
- ret = chip->ops->apply(chip, state);
+ ret = chip->ops->apply(chip, pwm, state);
err:
if (ret == 0)
chip->state = *state;
@@ -334,18 +346,18 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
return -EINVAL;
pwm_get_state(pwm, &state);
- if (state.duty_ns == duty_ns && state.period_ns == period_ns)
+ if (state.duty_cycle == duty_ns && state.period == period_ns)
return 0;
- state.duty_ns = duty_ns;
- state.period_ns = period_ns;
+ state.duty_cycle = duty_ns;
+ state.period = period_ns;
return pwm_apply_state(pwm, &state);
}
EXPORT_SYMBOL_GPL(pwm_config);
unsigned int pwm_get_period(struct pwm_device *pwm)
{
- return pwm->chip->state.period_ns;
+ return pwm->chip->state.period;
}
/*
@@ -356,10 +368,10 @@ int pwm_enable(struct pwm_device *pwm)
struct pwm_state state;
pwm_get_state(pwm, &state);
- if (state.p_enable)
+ if (state.enabled)
return 0;
- state.p_enable = true;
+ state.enabled = true;
return pwm_apply_state(pwm, &state);
}
EXPORT_SYMBOL_GPL(pwm_enable);
@@ -372,10 +384,10 @@ void pwm_disable(struct pwm_device *pwm)
struct pwm_state state;
pwm_get_state(pwm, &state);
- if (!state.p_enable)
+ if (!state.enabled)
return;
- state.p_enable = false;
+ state.enabled = false;
pwm_apply_state(pwm, &state);
}
EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 9ec81e18b1..851676c0dd 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -77,7 +77,7 @@ struct atmel_pwm_chip {
struct pwm_chip chips[PWM_CHANNELS];
struct clk *clk;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
const struct atmel_pwm_data *data;
/*
@@ -194,7 +194,7 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
unsigned long *cprd, u32 *pres)
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
- unsigned long long cycles = state->period_ns;
+ unsigned long long cycles = state->period;
int shift;
/* Calculate the period cycles and prescale value */
@@ -227,7 +227,7 @@ static void atmel_pwm_calculate_cdty(const struct pwm_state *state,
unsigned long clkrate, unsigned long cprd,
u32 pres, unsigned long *cdty)
{
- unsigned long long cycles = state->duty_ns;
+ unsigned long long cycles = state->duty_cycle;
cycles *= clkrate;
do_div(cycles, NSEC_PER_SEC);
@@ -286,7 +286,9 @@ static void atmel_pwm_disable(struct pwm_chip *chip, bool disable_clk)
clk_disable(atmel_pwm->clk);
}
-static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
+static int atmel_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
struct pwm_state cstate;
@@ -296,12 +298,12 @@ static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
cstate = chip->state;
- if (state->p_enable) {
+ if (state->enabled) {
unsigned long clkrate = clk_get_rate(atmel_pwm->clk);
- if (cstate.p_enable &&
+ if (cstate.enabled &&
cstate.polarity == state->polarity &&
- cstate.period_ns == state->period_ns) {
+ cstate.period == state->period) {
u32 cmr = atmel_pwm_ch_readl(atmel_pwm, chip->id, PWM_CMR);
cprd = atmel_pwm_ch_readl(atmel_pwm, chip->id,
@@ -323,7 +325,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
- if (cstate.p_enable) {
+ if (cstate.enabled) {
atmel_pwm_disable(chip, false);
} else {
ret = clk_enable(atmel_pwm->clk);
@@ -343,7 +345,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
atmel_pwm_ch_writel(atmel_pwm, chip->id, PWM_CMR, val);
atmel_pwm_set_cprd_cdty(chip, cprd, cdty);
atmel_pwm_writel(atmel_pwm, PWM_ENA, 1 << chip->id);
- } else if (cstate.p_enable) {
+ } else if (cstate.enabled) {
atmel_pwm_disable(chip, true);
}
@@ -410,10 +412,11 @@ static const struct of_device_id atmel_pwm_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
static int id = -1;
-static int atmel_pwm_probe(struct device_d *dev)
+static int atmel_pwm_probe(struct device *dev)
{
const struct atmel_pwm_data *data;
struct atmel_pwm_chip *atmel_pwm;
@@ -438,7 +441,7 @@ static int atmel_pwm_probe(struct device_d *dev)
return PTR_ERR(iores);
atmel_pwm->base = IOMEM(iores->start);
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
if (!alias)
id++;
@@ -452,7 +455,8 @@ static int atmel_pwm_probe(struct device_d *dev)
chip->ops = &atmel_pwm_ops;
chip->id = i;
- ret = pwmchip_add(chip, dev);
+ chip->dev = dev;
+ ret = pwmchip_add(chip);
if (ret) {
dev_err(dev, "failed to add pwm chip %d\n", ret);
return ret;
@@ -462,7 +466,7 @@ static int atmel_pwm_probe(struct device_d *dev)
return 0;
}
-static struct driver_d atmel_pwm_driver = {
+static struct driver atmel_pwm_driver = {
.name = "atmel-pwm",
.of_compatible = atmel_pwm_dt_ids,
.probe = atmel_pwm_probe,
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index c542b72796..2a75400593 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -36,7 +36,7 @@
#define MX3_PWMCR_EN (1 << 0)
struct imx_chip {
- struct clk *clk_per;
+ struct clk *clk_per, *clk_ipg;
void __iomem *mmio_base;
@@ -93,14 +93,42 @@ static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
writel(val, imx->mmio_base + MX1_PWMC);
}
+static int imx_pwm_clk_enable_v2(struct imx_chip *imx)
+{
+ int ret;
+
+ ret = clk_enable(imx->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret) {
+ clk_disable_unprepare(imx->clk_ipg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void imx_pwm_clk_disable_v2(struct imx_chip *imx)
+{
+ clk_disable_unprepare(imx->clk_per);
+ clk_disable_unprepare(imx->clk_ipg);
+}
+
static int imx_pwm_config_v2(struct pwm_chip *chip,
int duty_ns, int period_ns)
{
struct imx_chip *imx = to_imx_chip(chip);
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
+ int ret;
u32 cr;
+ ret = imx_pwm_clk_enable_v2(imx);
+ if (ret)
+ return ret;
+
c = clk_get_rate(imx->clk_per);
c = c * period_ns;
do_div(c, 1000000000);
@@ -134,6 +162,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
writel(cr, imx->mmio_base + MX3_PWMCR);
+ if (!chip->state.enabled)
+ imx_pwm_clk_disable_v2(imx);
+
return 0;
}
@@ -141,6 +172,11 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
+ int ret;
+
+ ret = imx_pwm_clk_enable_v2(imx);
+ if (WARN_ON(ret))
+ return;
val = readl(imx->mmio_base + MX3_PWMCR);
@@ -150,26 +186,31 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
val &= ~MX3_PWMCR_EN;
writel(val, imx->mmio_base + MX3_PWMCR);
+
+ if (!enable)
+ imx_pwm_clk_disable_v2(imx);
}
-static int imx_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
+static int imx_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
{
struct imx_chip *imx = to_imx_chip(chip);
bool enabled;
int ret;
- enabled = chip->state.p_enable;
+ enabled = chip->state.enabled;
- if (enabled && !state->p_enable) {
+ if (enabled && !state->enabled) {
imx->set_enable(chip, false);
return 0;
}
- ret = imx->config(chip, state->duty_ns, state->period_ns);
+ ret = imx->config(chip, state->duty_cycle, state->period);
if (ret)
return ret;
- if (!enabled && state->p_enable)
+ if (!enabled && state->enabled)
imx->set_enable(chip, true);
return 0;
@@ -200,8 +241,9 @@ static struct of_device_id imx_pwm_dt_ids[] = {
{ .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids);
-static int imx_pwm_probe(struct device_d *dev)
+static int imx_pwm_probe(struct device *dev)
{
struct resource *iores;
const struct imx_pwm_data *data;
@@ -214,6 +256,10 @@ static int imx_pwm_probe(struct device_d *dev)
imx = xzalloc(sizeof(*imx));
+ imx->clk_ipg = clk_get_optional(dev, "ipg");
+ if (IS_ERR(imx->clk_ipg))
+ return PTR_ERR(imx->clk_ipg);
+
imx->clk_per = clk_get(dev, "per");
if (IS_ERR(imx->clk_per))
return PTR_ERR(imx->clk_per);
@@ -223,9 +269,10 @@ static int imx_pwm_probe(struct device_d *dev)
return PTR_ERR(iores);
imx->mmio_base = IOMEM(iores->start);
+ imx->chip.dev = dev;
imx->chip.ops = &imx_pwm_ops;
- if (dev->device_node) {
- imx->chip.devname = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ imx->chip.devname = of_alias_get(dev->of_node);
if (!imx->chip.devname)
imx->chip.devname = basprintf("pwm_%p",
imx->mmio_base);
@@ -236,10 +283,10 @@ static int imx_pwm_probe(struct device_d *dev)
imx->config = data->config;
imx->set_enable = data->set_enable;
- return pwmchip_add(&imx->chip, dev);;
+ return pwmchip_add(&imx->chip);
}
-static struct driver_d imx_pwm_driver = {
+static struct driver imx_pwm_driver = {
.name = "imx-pwm",
.of_compatible = imx_pwm_dt_ids,
.probe = imx_pwm_probe,
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
index 8f77ca07a6..3e3efade68 100644
--- a/drivers/pwm/pwm-mxs.c
+++ b/drivers/pwm/pwm-mxs.c
@@ -44,7 +44,9 @@ struct mxs_pwm {
#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
-static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
+static int mxs_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
{
struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
int div = 0;
@@ -53,9 +55,9 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
unsigned long long c;
bool enabled;
- enabled = chip->state.p_enable;
+ enabled = chip->state.enabled;
- if (enabled && !state->p_enable) {
+ if (enabled && !state->enabled) {
writel(1 << mxs->chip.id, mxs->mxs->base + PWM_CTRL + CLR);
return 0;
}
@@ -63,7 +65,7 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
rate = clk_get_rate(mxs->mxs->clk);
while (1) {
c = rate / cdiv[div];
- c = c * state->period_ns;
+ c = c * state->period;
do_div(c, 1000000000);
if (c < PERIOD_PERIOD_MAX)
break;
@@ -73,8 +75,8 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
}
period_cycles = c;
- c *= state->duty_ns;
- do_div(c, state->period_ns);
+ c *= state->duty_cycle;
+ do_div(c, state->period);
duty_cycles = c;
writel(duty_cycles << 16,
@@ -83,7 +85,7 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
mxs->mxs->base + PWM_PERIOD0 + mxs->chip.id * 0x20);
- if (!enabled && state->p_enable)
+ if (!enabled && state->enabled)
writel(1 << mxs->chip.id, mxs->mxs->base + PWM_CTRL + SET);
return 0;
@@ -93,10 +95,10 @@ static struct pwm_ops mxs_pwm_ops = {
.apply = mxs_pwm_apply,
};
-static int mxs_pwm_probe(struct device_d *dev)
+static int mxs_pwm_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct mxs_pwm *mxs;
int ret, i;
uint32_t npwm;
@@ -130,9 +132,10 @@ static int mxs_pwm_probe(struct device_d *dev)
mxspwm->chip.ops = &mxs_pwm_ops;
mxspwm->chip.devname = basprintf("pwm%d", i);
mxspwm->chip.id = i;
+ mxspwm->chip.dev = dev;
mxspwm->mxs = mxs;
- ret = pwmchip_add(&mxspwm->chip, dev);
+ ret = pwmchip_add(&mxspwm->chip);
if (ret < 0) {
dev_err(dev, "failed to add pwm chip %d\n", ret);
return ret;
@@ -146,8 +149,9 @@ static const struct of_device_id mxs_pwm_dt_ids[] = {
{ .compatible = "fsl,imx23-pwm", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
-static struct driver_d mxs_pwm_driver = {
+static struct driver mxs_pwm_driver = {
.name = "mxs-pwm",
.of_compatible = mxs_pwm_dt_ids,
.probe = mxs_pwm_probe,
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c
new file mode 100644
index 0000000000..960867c520
--- /dev/null
+++ b/drivers/pwm/pwm-rockchip.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PWM driver for Rockchip SoCs
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ * Copyright (C) 2014 ROCKCHIP, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <module.h>
+#include <of.h>
+#include <linux/device.h>
+#include <pwm.h>
+#include <linux/time.h>
+
+#define PWM_CTRL_TIMER_EN (1 << 0)
+#define PWM_CTRL_OUTPUT_EN (1 << 3)
+
+#define PWM_ENABLE (1 << 0)
+#define PWM_CONTINUOUS (1 << 1)
+#define PWM_DUTY_POSITIVE (1 << 3)
+#define PWM_DUTY_NEGATIVE (0 << 3)
+#define PWM_INACTIVE_NEGATIVE (0 << 4)
+#define PWM_INACTIVE_POSITIVE (1 << 4)
+#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
+#define PWM_OUTPUT_LEFT (0 << 5)
+#define PWM_LOCK_EN (1 << 6)
+#define PWM_LP_DISABLE (0 << 8)
+
+struct rockchip_pwm_chip {
+ struct pwm_chip chip;
+ struct clk *clk;
+ struct clk *pclk;
+ const struct rockchip_pwm_data *data;
+ void __iomem *base;
+};
+
+struct rockchip_pwm_regs {
+ unsigned long duty;
+ unsigned long period;
+ unsigned long cntr;
+ unsigned long ctrl;
+};
+
+struct rockchip_pwm_data {
+ struct rockchip_pwm_regs regs;
+ unsigned int prescaler;
+ bool supports_polarity;
+ bool supports_lock;
+ u32 enable_conf;
+};
+
+static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct rockchip_pwm_chip, chip);
+}
+
+static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ unsigned long period, duty;
+ u64 clk_rate, div;
+ u32 ctrl;
+
+ clk_rate = clk_get_rate(pc->clk);
+
+ /*
+ * Since period and duty cycle registers have a width of 32
+ * bits, every possible input period can be obtained using the
+ * default prescaler value for all practical clock rate values.
+ */
+ div = clk_rate * state->period;
+ period = DIV_ROUND_CLOSEST_ULL(div,
+ pc->data->prescaler * NSEC_PER_SEC);
+
+ div = clk_rate * state->duty_cycle;
+ duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
+
+ /*
+ * Lock the period and duty of previous configuration, then
+ * change the duty and period, that would not be effective.
+ */
+ ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
+ if (pc->data->supports_lock) {
+ ctrl |= PWM_LOCK_EN;
+ writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
+ }
+
+ writel(period, pc->base + pc->data->regs.period);
+ writel(duty, pc->base + pc->data->regs.duty);
+
+ if (pc->data->supports_polarity) {
+ ctrl &= ~PWM_POLARITY_MASK;
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
+ else
+ ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
+ }
+
+ /*
+ * Unlock and set polarity at the same time,
+ * the configuration of duty, period and polarity
+ * would be effective together at next period.
+ */
+ if (pc->data->supports_lock)
+ ctrl &= ~PWM_LOCK_EN;
+
+ writel(ctrl, pc->base + pc->data->regs.ctrl);
+}
+
+static int rockchip_pwm_enable(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ bool enable)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ u32 enable_conf = pc->data->enable_conf;
+ int ret;
+ u32 val;
+
+ if (enable) {
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+ }
+
+ val = readl_relaxed(pc->base + pc->data->regs.ctrl);
+
+ if (enable)
+ val |= enable_conf;
+ else
+ val &= ~enable_conf;
+
+ writel_relaxed(val, pc->base + pc->data->regs.ctrl);
+
+ if (!enable)
+ clk_disable(pc->clk);
+
+ return 0;
+}
+
+static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ struct pwm_state curstate;
+ bool enabled;
+ int ret = 0;
+
+ ret = clk_enable(pc->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+
+ pwm_get_state(pwm, &curstate);
+ enabled = curstate.enabled;
+
+ if (state->polarity != curstate.polarity && enabled &&
+ !pc->data->supports_lock) {
+ ret = rockchip_pwm_enable(chip, pwm, false);
+ if (ret)
+ goto out;
+ enabled = false;
+ }
+
+ rockchip_pwm_config(chip, pwm, state);
+ if (state->enabled != enabled) {
+ ret = rockchip_pwm_enable(chip, pwm, state->enabled);
+ if (ret)
+ goto out;
+ }
+
+out:
+ clk_disable(pc->clk);
+ clk_disable(pc->pclk);
+
+ return ret;
+}
+
+static const struct pwm_ops rockchip_pwm_ops = {
+ .apply = rockchip_pwm_apply,
+};
+
+static const struct rockchip_pwm_data pwm_data_v1 = {
+ .regs = {
+ .duty = 0x04,
+ .period = 0x08,
+ .cntr = 0x00,
+ .ctrl = 0x0c,
+ },
+ .prescaler = 2,
+ .supports_polarity = false,
+ .supports_lock = false,
+ .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
+};
+
+static const struct rockchip_pwm_data pwm_data_v2 = {
+ .regs = {
+ .duty = 0x08,
+ .period = 0x04,
+ .cntr = 0x00,
+ .ctrl = 0x0c,
+ },
+ .prescaler = 1,
+ .supports_polarity = true,
+ .supports_lock = false,
+ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+ PWM_CONTINUOUS,
+};
+
+static const struct rockchip_pwm_data pwm_data_vop = {
+ .regs = {
+ .duty = 0x08,
+ .period = 0x04,
+ .cntr = 0x0c,
+ .ctrl = 0x00,
+ },
+ .prescaler = 1,
+ .supports_polarity = true,
+ .supports_lock = false,
+ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+ PWM_CONTINUOUS,
+};
+
+static const struct rockchip_pwm_data pwm_data_v3 = {
+ .regs = {
+ .duty = 0x08,
+ .period = 0x04,
+ .cntr = 0x00,
+ .ctrl = 0x0c,
+ },
+ .prescaler = 1,
+ .supports_polarity = true,
+ .supports_lock = true,
+ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+ PWM_CONTINUOUS,
+};
+
+static const struct of_device_id rockchip_pwm_dt_ids[] = {
+ { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1},
+ { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2},
+ { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop},
+ { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);
+
+static int rockchip_pwm_probe(struct device *dev)
+{
+ struct rockchip_pwm_chip *pc;
+ u32 enable_conf, ctrl;
+ bool enabled;
+ int ret, count;
+
+ pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc)
+ return -ENOMEM;
+
+ pc->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(pc->base))
+ return PTR_ERR(pc->base);
+
+ pc->clk = clk_get(dev, "pwm");
+ if (IS_ERR(pc->clk)) {
+ pc->clk = clk_get(dev, NULL);
+ if (IS_ERR(pc->clk))
+ return dev_err_probe(dev, PTR_ERR(pc->clk),
+ "Can't get PWM clk\n");
+ }
+
+ count = of_count_phandle_with_args(dev->of_node,
+ "clocks", "#clock-cells");
+ if (count == 2)
+ pc->pclk = clk_get(dev, "pclk");
+ else
+ pc->pclk = pc->clk;
+
+ if (IS_ERR(pc->pclk))
+ return dev_err_probe(dev, PTR_ERR(pc->pclk), "Can't get APB clk\n");
+
+ ret = clk_prepare_enable(pc->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't prepare enable PWM clk\n");
+
+ ret = clk_prepare_enable(pc->pclk);
+ if (ret) {
+ dev_err_probe(dev, ret, "Can't prepare enable APB clk\n");
+ goto err_clk;
+ }
+
+ dev->priv = pc;
+
+ pc->data = device_get_match_data(dev);
+ pc->chip.dev = dev;
+ pc->chip.ops = &rockchip_pwm_ops;
+
+ enable_conf = pc->data->enable_conf;
+ ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
+ enabled = (ctrl & enable_conf) == enable_conf;
+
+ pc->chip.devname = of_alias_get(dev->of_node);
+ if (!pc->chip.devname)
+ pc->chip.devname = basprintf("pwm_%p", pc->base);
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "pwmchip_add() failed\n");
+ goto err_pclk;
+ }
+
+ /* Keep the PWM clk enabled if the PWM appears to be up and running. */
+ if (!enabled)
+ clk_disable(pc->clk);
+
+ clk_disable(pc->pclk);
+
+ return 0;
+
+err_pclk:
+ clk_disable_unprepare(pc->pclk);
+err_clk:
+ clk_disable_unprepare(pc->clk);
+
+ return ret;
+}
+
+static void rockchip_pwm_remove(struct device *dev)
+{
+ struct rockchip_pwm_chip *pc = dev->priv;
+
+ pwmchip_remove(&pc->chip);
+}
+
+static struct driver rockchip_pwm_driver = {
+ .name = "rockchip-pwm",
+ .of_match_table = rockchip_pwm_dt_ids,
+ .probe = rockchip_pwm_probe,
+ .remove = rockchip_pwm_remove,
+};
+device_platform_driver(rockchip_pwm_driver);
+
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
+MODULE_DESCRIPTION("Rockchip SoC PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
index 4e1a65207e..16f80381c2 100644
--- a/drivers/pwm/pwm-stm32.c
+++ b/drivers/pwm/pwm-stm32.c
@@ -14,6 +14,7 @@
#include <io.h>
#include <linux/bitfield.h>
#include <linux/mfd/stm32-timers.h>
+#include <linux/regmap.h>
#include <linux/math64.h>
#include <of.h>
#include <pwm.h>
@@ -197,15 +198,17 @@ static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned ch)
clk_disable(priv->clk);
}
-static int stm32_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
+static int stm32_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
{
bool enabled;
struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
int ret;
- enabled = chip->state.p_enable;
+ enabled = chip->state.enabled;
- if (enabled && !state->p_enable) {
+ if (enabled && !state->enabled) {
stm32_pwm_disable(priv, chip->id);
return 0;
}
@@ -214,11 +217,11 @@ static int stm32_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
stm32_pwm_set_polarity(priv, chip->id, state->polarity);
ret = stm32_pwm_config(priv, chip->id,
- state->duty_ns, state->period_ns);
+ state->duty_cycle, state->period);
if (ret)
return ret;
- if (!enabled && state->p_enable)
+ if (!enabled && state->enabled)
ret = stm32_pwm_enable(priv, chip->id);
return ret;
@@ -335,9 +338,9 @@ static int stm32_pwm_detect_channels(struct stm32_pwm *priv)
static int id = -1;
-static int stm32_pwm_probe(struct device_d *dev)
+static int stm32_pwm_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct stm32_timers *ddata = dev->parent->priv;
struct stm32_pwm *priv;
const char *alias;
@@ -362,7 +365,7 @@ static int stm32_pwm_probe(struct device_d *dev)
npwms = stm32_pwm_detect_channels(priv);
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
if (!alias)
id++;
@@ -376,8 +379,9 @@ static int stm32_pwm_probe(struct device_d *dev)
chip->ops = &stm32pwm_ops;
chip->id = i;
+ chip->dev = dev;
- ret = pwmchip_add(chip, dev);
+ ret = pwmchip_add(chip);
if (ret < 0) {
dev_err(dev, "failed to add pwm chip %d\n", ret);
return ret;
@@ -391,8 +395,9 @@ static const struct of_device_id stm32_pwm_of_match[] = {
{ .compatible = "st,stm32-pwm", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
-static struct driver_d stm32_pwm_driver = {
+static struct driver stm32_pwm_driver = {
.name = "stm32-pwm",
.probe = stm32_pwm_probe,
.of_compatible = stm32_pwm_of_match,
diff --git a/drivers/pwm/pxa_pwm.c b/drivers/pwm/pxa_pwm.c
index 61c4b8da43..42ccb37b9a 100644
--- a/drivers/pwm/pxa_pwm.c
+++ b/drivers/pwm/pxa_pwm.c
@@ -13,10 +13,10 @@
#include <io.h>
#include <pwm.h>
-#include <mach/hardware.h>
-#include <mach/clock.h>
-#include <mach/pxa-regs.h>
-#include <mach/regs-pwm.h>
+#include <mach/pxa/hardware.h>
+#include <mach/pxa/clock.h>
+#include <mach/pxa/pxa-regs.h>
+#include <mach/pxa/regs-pwm.h>
#include <linux/math64.h>
#include <linux/compiler.h>
@@ -77,22 +77,24 @@ static void pxa_pwm_disable(struct pxa_pwm_chip *pxa_pwm)
* duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
* PWM_CLK_RATE = 13 MHz
*/
-static int pxa_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
+static int pxa_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
{
unsigned long long c;
unsigned long period_cycles, prescale, pv, dc;
struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
bool enabled;
- enabled = chip->state.p_enable;
+ enabled = chip->state.enabled;
- if (enabled && !state->p_enable) {
+ if (enabled && !state->enabled) {
pxa_pwm_disable(pxa_pwm);
return 0;
}
c = pxa_get_pwmclk();
- c = c * state->period_ns;
+ c = c * state->period;
do_div(c, 1000000000);
period_cycles = c;
@@ -104,10 +106,10 @@ static int pxa_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
if (prescale > 63)
return -EINVAL;
- if (state->duty_ns == state->period_ns)
+ if (state->duty_cycle == state->period)
dc = PWMDCR_FD;
else
- dc = (pv + 1) * state->duty_ns / state->period_ns;
+ dc = (pv + 1) * state->duty_cycle / state->period;
/* NOTE: the clock to PWM has to be enabled first
* before writing to the registers
@@ -116,7 +118,7 @@ static int pxa_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state)
writel(dc, pxa_pwm->iobase + PWMDCR);
writel(pv, pxa_pwm->iobase + PWMPCR);
- if (!enabled && state->p_enable) {
+ if (!enabled && state->enabled) {
pxa_pwm_enable(pxa_pwm);
return 0;
}
@@ -128,7 +130,7 @@ static struct pwm_ops pxa_pwm_ops = {
.apply = pxa_pwm_apply,
};
-static int pxa_pwm_probe(struct device_d *dev)
+static int pxa_pwm_probe(struct device *dev)
{
struct resource *iores;
struct pxa_pwm_chip *chip;
@@ -141,12 +143,13 @@ static int pxa_pwm_probe(struct device_d *dev)
return PTR_ERR(iores);
chip->iobase = IOMEM(iores->start);
chip->id = dev->id;
+ chip->chip.dev = dev;
dev->priv = chip;
- return pwmchip_add(&chip->chip, dev);
+ return pwmchip_add(&chip->chip);
}
-static struct driver_d pxa_pwm_driver = {
+static struct driver pxa_pwm_driver = {
.name = "pxa_pwm",
.probe = pxa_pwm_probe,
};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 56abe3896e..17e217f0bb 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -20,7 +20,7 @@ config REGULATOR_BCM283X
config REGULATOR_PFUZE
bool "Freescale PFUZE100/200/3000 regulator driver"
depends on I2C
- depends on ARCH_IMX6 || ARCH_IMX8MQ
+ depends on ARCH_IMX || COMPILE_TEST
config REGULATOR_STM32_PWR
bool "STMicroelectronics STM32 PWR"
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index 3f8473ec07..4b4c174304 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -6,7 +6,7 @@
#include <common.h>
#include <init.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <regulator.h>
struct anatop_regulator {
@@ -33,9 +33,9 @@ static struct regulator_ops anatop_rops = {
.list_voltage = regulator_list_voltage_linear,
};
-static int anatop_regulator_probe(struct device_d *dev)
+static int anatop_regulator_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct device_node *anatop_np;
struct regulator_desc *rdesc;
struct regulator_dev *rdev;
@@ -131,15 +131,16 @@ static int anatop_regulator_probe(struct device_d *dev)
}
}
- return of_regulator_register(rdev, dev->device_node);
+ return of_regulator_register(rdev, dev->of_node);
}
static const struct of_device_id of_anatop_regulator_match_tbl[] = {
{ .compatible = "fsl,anatop-regulator", },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_anatop_regulator_match_tbl);
-static struct driver_d anatop_regulator_driver = {
+static struct driver anatop_regulator_driver = {
.name = "anatop_regulator",
.probe = anatop_regulator_probe,
.of_compatible = DRV_OF_COMPAT(of_anatop_regulator_match_tbl),
diff --git a/drivers/regulator/bcm2835.c b/drivers/regulator/bcm2835.c
index 71a1d1a55d..fa9fc47207 100644
--- a/drivers/regulator/bcm2835.c
+++ b/drivers/regulator/bcm2835.c
@@ -9,7 +9,7 @@
#include <init.h>
#include <regulator.h>
-#include <mach/mbox.h>
+#include <mach/bcm283x/mbox.h>
#define REG_DEV(_id, _name) \
{ \
@@ -110,7 +110,7 @@ const static struct regulator_ops bcm2835_ops = {
.is_enabled = regulator_bcm2835_is_enabled,
};
-static int regulator_bcm2835_probe(struct device_d *dev)
+static int regulator_bcm2835_probe(struct device *dev)
{
struct regulator_bcm2835 *rb;
int ret, i;
@@ -122,7 +122,7 @@ static int regulator_bcm2835_probe(struct device_d *dev)
rb->rdev.desc = &rb->rdesc;
rb->rdev.dev = dev;
- ret = dev_regulator_register(&rb->rdev, rb->devname, NULL);
+ ret = dev_regulator_register(&rb->rdev, rb->devname);
if (ret)
return ret;
}
@@ -130,7 +130,7 @@ static int regulator_bcm2835_probe(struct device_d *dev)
return 0;
}
-static struct driver_d regulator_bcm2835_driver = {
+static struct driver regulator_bcm2835_driver = {
.name = "regulator-bcm2835",
.probe = regulator_bcm2835_probe,
};
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 4876f0f44b..bbba3b0b57 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -13,41 +13,39 @@
static LIST_HEAD(regulator_list);
-struct regulator_internal {
- struct list_head list;
- struct device_node *node;
- struct regulator_dev *rdev;
- int enable_count;
- int enable_time_us;
- int min_uv;
- int max_uv;
- char *name;
- const char *supply;
- struct list_head consumer_list;
-};
-
struct regulator {
- struct regulator_internal *ri;
+ struct regulator_dev *rdev;
+ struct regulator_dev *rdev_consumer;
struct list_head list;
- struct device_d *dev;
+ struct device *dev;
};
+const char *rdev_get_name(struct regulator_dev *rdev)
+{
+ if (rdev->name)
+ return rdev->name;
+
+ return "";
+}
+
static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV,
int max_uV)
{
if (rdev->desc->ops->list_voltage == regulator_list_voltage_linear)
return regulator_map_voltage_linear(rdev, min_uV, max_uV);
- return -ENOSYS;
+ if (rdev->desc->ops->list_voltage == regulator_list_voltage_linear_range)
+ return regulator_map_voltage_linear_range(rdev, min_uV, max_uV);
+
+ return regulator_map_voltage_iterate(rdev, min_uV, max_uV);
}
-static int regulator_enable_internal(struct regulator_internal *ri)
+static int regulator_enable_rdev(struct regulator_dev *rdev)
{
- struct regulator_dev *rdev = ri->rdev;
int ret;
- if (ri->enable_count) {
- ri->enable_count++;
+ if (rdev->enable_count) {
+ rdev->enable_count++;
return 0;
}
@@ -59,30 +57,29 @@ static int regulator_enable_internal(struct regulator_internal *ri)
if (ret)
return ret;
- ret = rdev->desc->ops->enable(ri->rdev);
+ ret = rdev->desc->ops->enable(rdev);
if (ret) {
regulator_disable(rdev->supply);
return ret;
}
- if (ri->enable_time_us)
- udelay(ri->enable_time_us);
+ if (rdev->enable_time_us)
+ udelay(rdev->enable_time_us);
- ri->enable_count++;
+ rdev->enable_count++;
return 0;
}
-static int regulator_disable_internal(struct regulator_internal *ri)
+static int regulator_disable_rdev(struct regulator_dev *rdev)
{
- struct regulator_dev *rdev = ri->rdev;
int ret;
- if (!ri->enable_count)
+ if (!rdev->enable_count)
return -EINVAL;
- if (ri->enable_count > 1) {
- ri->enable_count--;
+ if (rdev->enable_count > 1) {
+ rdev->enable_count--;
return 0;
}
@@ -97,15 +94,14 @@ static int regulator_disable_internal(struct regulator_internal *ri)
if (ret)
return ret;
- ri->enable_count--;
+ rdev->enable_count--;
- return regulator_disable(ri->rdev->supply);
+ return regulator_disable(rdev->supply);
}
-static int regulator_set_voltage_internal(struct regulator_internal *ri,
+static int regulator_set_voltage_rdev(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
- struct regulator_dev *rdev = ri->rdev;
const struct regulator_ops *ops = rdev->desc->ops;
unsigned int selector;
int best_val = 0;
@@ -129,6 +125,32 @@ static int regulator_set_voltage_internal(struct regulator_internal *ri,
return -ENOSYS;
}
+static int regulator_get_voltage_rdev(struct regulator_dev *rdev)
+{
+ int sel, ret;
+
+ if (rdev->desc->ops->get_voltage_sel) {
+ sel = rdev->desc->ops->get_voltage_sel(rdev);
+ if (sel < 0)
+ return sel;
+ ret = rdev->desc->ops->list_voltage(rdev, sel);
+ } else if (rdev->desc->ops->get_voltage) {
+ ret = rdev->desc->ops->get_voltage(rdev);
+ } else if (rdev->desc->ops->list_voltage) {
+ ret = rdev->desc->ops->list_voltage(rdev, 0);
+ } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
+ ret = rdev->desc->fixed_uV;
+ } else if (rdev->min_uv && rdev->min_uv == rdev->max_uv) {
+ ret = rdev->min_uv;
+ } else if (rdev->supply) {
+ ret = regulator_get_voltage(rdev->supply);
+ } else {
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static int regulator_resolve_supply(struct regulator_dev *rdev)
{
struct regulator *supply;
@@ -141,6 +163,8 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
if (!supply_name)
return 0;
+ rdev_dbg(rdev, "resolving %s\n", supply_name);
+
supply = regulator_get(rdev->dev, supply_name);
if (IS_ERR(supply)) {
if (deep_probe_is_supported())
@@ -155,107 +179,167 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
* we couldn't. If you want to get rid of this warning, consider
* migrating your platform to have deep probe support.
*/
- dev_warn(rdev->dev, "Failed to get '%s' regulator (ignored).\n",
+ rdev_warn(rdev, "Failed to get '%s' regulator (ignored).\n",
supply_name);
return 0;
}
+ if (supply)
+ supply->rdev_consumer = rdev;
+
rdev->supply = supply;
+
return 0;
}
-static struct regulator_internal * __regulator_register(struct regulator_dev *rd, const char *name)
+static int regulator_init_voltage(struct regulator_dev *rdev)
{
- struct regulator_internal *ri;
- int ret;
+ int target_min, target_max, current_uV, ret;
- ri = xzalloc(sizeof(*ri));
- ri->rdev = rd;
+ if (!rdev->min_uv || !rdev->max_uv)
+ return 0;
- INIT_LIST_HEAD(&ri->consumer_list);
+ current_uV = regulator_get_voltage_rdev(rdev);
+ if (current_uV < 0) {
+ /* This regulator can't be read and must be initialized */
+ rdev_info(rdev, "Setting %d-%duV\n", rdev->min_uv, rdev->max_uv);
+ regulator_set_voltage_rdev(rdev, rdev->min_uv, rdev->max_uv);
+ current_uV = regulator_get_voltage_rdev(rdev);
+ }
- list_add_tail(&ri->list, &regulator_list);
+ if (current_uV < 0) {
+ if (current_uV != -EPROBE_DEFER)
+ rdev_err(rdev,
+ "failed to get the current voltage: %pe\n",
+ ERR_PTR(current_uV));
+ return current_uV;
+ }
+
+ /*
+ * If we're below the minimum voltage move up to the
+ * minimum voltage, if we're above the maximum voltage
+ * then move down to the maximum.
+ */
+ target_min = current_uV;
+ target_max = current_uV;
+
+ if (current_uV < rdev->min_uv) {
+ target_min = rdev->min_uv;
+ target_max = rdev->min_uv;
+ }
+
+ if (current_uV > rdev->max_uv) {
+ target_min = rdev->max_uv;
+ target_max = rdev->max_uv;
+ }
+
+ if (target_min != current_uV || target_max != current_uV) {
+ rdev_info(rdev, "Bringing %duV into %d-%duV\n",
+ current_uV, target_min, target_max);
+ ret = regulator_set_voltage_rdev(rdev, target_min, target_max);
+ if (ret < 0) {
+ rdev_err(rdev,
+ "failed to apply %d-%duV constraint: %pe\n",
+ target_min, target_max, ERR_PTR(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int __regulator_register(struct regulator_dev *rdev, const char *name)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&rdev->consumer_list);
+
+ list_add_tail(&rdev->list, &regulator_list);
if (name)
- ri->name = xstrdup(name);
+ rdev->name = xstrdup(name);
- if (rd->boot_on || rd->always_on) {
- ret = regulator_resolve_supply(ri->rdev);
+ ret = regulator_init_voltage(rdev);
+ if (ret)
+ goto err;
+
+ if (rdev->boot_on || rdev->always_on) {
+ ret = regulator_resolve_supply(rdev);
if (ret < 0)
goto err;
- ret = regulator_enable_internal(ri);
+ ret = regulator_enable_rdev(rdev);
if (ret && ret != -ENOSYS)
goto err;
}
- return ri;
+ return 0;
err:
- list_del(&ri->list);
- free(ri->name);
- free(ri);
+ list_del(&rdev->list);
+ free((char *)rdev->name);
- return ERR_PTR(ret);
+ return ret;
}
#ifdef CONFIG_OFDEVICE
/*
* of_regulator_register - register a regulator corresponding to a device_node
- * @rd: the regulator device providing the ops
+ * rdev: the regulator device providing the ops
* @node: the device_node this regulator corresponds to
*
* Return: 0 for success or a negative error code
*/
-int of_regulator_register(struct regulator_dev *rd, struct device_node *node)
+int of_regulator_register(struct regulator_dev *rdev, struct device_node *node)
{
- struct regulator_internal *ri;
const char *name;
+ int ret;
- if (!rd || !node)
+ if (!rdev || !node)
return -EINVAL;
- rd->boot_on = of_property_read_bool(node, "regulator-boot-on");
- rd->always_on = of_property_read_bool(node, "regulator-always-on");
+ rdev->boot_on = of_property_read_bool(node, "regulator-boot-on");
+ rdev->always_on = of_property_read_bool(node, "regulator-always-on");
name = of_get_property(node, "regulator-name", NULL);
if (!name)
name = node->name;
- ri = __regulator_register(rd, name);
- if (IS_ERR(ri))
- return PTR_ERR(ri);
+ rdev->node = node;
+ node->dev = rdev->dev;
- ri->node = node;
- node->dev = rd->dev;
+ if (rdev->desc->off_on_delay)
+ rdev->enable_time_us = rdev->desc->off_on_delay;
- if (rd->desc->off_on_delay)
- ri->enable_time_us = rd->desc->off_on_delay;
-
- if (rd->desc->fixed_uV && rd->desc->n_voltages == 1)
- ri->min_uv = ri->max_uv = rd->desc->fixed_uV;
+ if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1)
+ rdev->min_uv = rdev->max_uv = rdev->desc->fixed_uV;
of_property_read_u32(node, "regulator-enable-ramp-delay",
- &ri->enable_time_us);
+ &rdev->enable_time_us);
of_property_read_u32(node, "regulator-min-microvolt",
- &ri->min_uv);
+ &rdev->min_uv);
of_property_read_u32(node, "regulator-max-microvolt",
- &ri->max_uv);
+ &rdev->max_uv);
+
+ ret = __regulator_register(rdev, name);
+ if (ret)
+ return ret;
return 0;
}
-static struct regulator_internal *of_regulator_get(struct device_d *dev, const char *supply)
+static struct regulator_dev *of_regulator_get(struct device *dev,
+ const char *supply)
{
char *propname;
- struct regulator_internal *ri;
+ struct regulator_dev *rdev;
struct device_node *node, *node_parent;
int ret;
/*
* If the device does have a device node return the dummy regulator.
*/
- if (!dev->device_node)
+ if (!dev->of_node)
return NULL;
propname = basprintf("%s-supply", supply);
@@ -264,10 +348,10 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c
* If the device node does not contain a supply property, this device doesn't
* need a regulator. Return the dummy regulator in this case.
*/
- if (!of_get_property(dev->device_node, propname, NULL)) {
+ if (!of_get_property(dev->of_node, propname, NULL)) {
dev_dbg(dev, "No %s-supply node found, using dummy regulator\n",
supply);
- ri = NULL;
+ rdev = NULL;
goto out;
}
@@ -275,16 +359,16 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c
* The device node specifies a supply, so it's mandatory. Return an error when
* something goes wrong below.
*/
- node = of_parse_phandle(dev->device_node, propname, 0);
+ node = of_parse_phandle(dev->of_node, propname, 0);
if (!node) {
dev_dbg(dev, "No %s node found\n", propname);
- ri = ERR_PTR(-EINVAL);
+ rdev = ERR_PTR(-EINVAL);
goto out;
}
ret = of_device_ensure_probed(node);
if (ret) {
- /*
+ /*
* If "barebox,allow-dummy-supply" property is set for regulator
* provider allow use of dummy regulator (NULL is returned).
* Check regulator node and its parent if this setting is set
@@ -295,18 +379,18 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c
of_get_property(node_parent, "barebox,allow-dummy-supply", NULL)) {
dev_dbg(dev, "Allow use of dummy regulator for " \
"%s-supply\n", supply);
- ri = NULL;
+ rdev = NULL;
goto out;
}
- ri = ERR_PTR(ret);
+ rdev = ERR_PTR(ret);
goto out;
}
- list_for_each_entry(ri, &regulator_list, list) {
- if (ri->node == node) {
- dev_dbg(dev, "Using %s regulator from %s\n",
- propname, node->full_name);
+ list_for_each_entry(rdev, &regulator_list, list) {
+ if (rdev->node == node) {
+ dev_dbg(dev, "Using %s regulator from %pOF\n",
+ propname, node);
goto out;
}
}
@@ -316,52 +400,54 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c
* added in future initcalls, so, instead of reporting a
* complete failure report probe deferral
*/
- ri = ERR_PTR(-EPROBE_DEFER);
+ rdev = ERR_PTR(-EPROBE_DEFER);
out:
free(propname);
- return ri;
+ return rdev;
}
#else
-static struct regulator_internal *of_regulator_get(struct device_d *dev, const char *supply)
+static struct regulator_dev *of_regulator_get(struct device *dev,
+ const char *supply)
{
return NULL;
}
#endif
-int dev_regulator_register(struct regulator_dev *rd, const char * name, const char* supply)
+int dev_regulator_register(struct regulator_dev *rdev, const char *name)
{
- struct regulator_internal *ri;
-
- ri = __regulator_register(rd, name);
+ int ret;
- ri->supply = supply;
+ ret = __regulator_register(rdev, name);
+ if (ret)
+ return ret;
return 0;
}
-static struct regulator_internal *dev_regulator_get(struct device_d *dev, const char *supply)
+static struct regulator_dev *dev_regulator_get(struct device *dev,
+ const char *supply)
{
- struct regulator_internal *ri;
- struct regulator_internal *ret = NULL;
+ struct regulator_dev *rdev;
+ struct regulator_dev *ret = NULL;
int match, best = 0;
const char *dev_id = dev ? dev_name(dev) : NULL;
- list_for_each_entry(ri, &regulator_list, list) {
+ list_for_each_entry(rdev, &regulator_list, list) {
match = 0;
- if (ri->name) {
- if (!dev_id || strcmp(ri->name, dev_id))
+ if (rdev->name) {
+ if (!dev_id || strcmp(rdev->name, dev_id))
continue;
match += 2;
}
- if (ri->supply) {
- if (!supply || strcmp(ri->supply, supply))
+ if (rdev->desc->supply_name) {
+ if (!supply || strcmp(rdev->desc->supply_name, supply))
continue;
match += 1;
}
if (match > best) {
- ret = ri;
+ ret = rdev;
if (match != 3)
best = match;
else
@@ -382,64 +468,73 @@ static struct regulator_internal *dev_regulator_get(struct device_d *dev, const
*
* Return: a regulator object or an error pointer
*/
-struct regulator *regulator_get(struct device_d *dev, const char *supply)
+struct regulator *regulator_get(struct device *dev, const char *supply)
{
- struct regulator_internal *ri = NULL;
+ struct regulator_dev *rdev = NULL;
struct regulator *r;
int ret;
- if (dev->device_node) {
- ri = of_regulator_get(dev, supply);
- if (IS_ERR(ri))
- return ERR_CAST(ri);
+ if (dev->of_node && supply) {
+ rdev = of_regulator_get(dev, supply);
+ if (IS_ERR(rdev))
+ return ERR_CAST(rdev);
}
- if (!ri) {
- ri = dev_regulator_get(dev, supply);
- if (IS_ERR(ri))
- return ERR_CAST(ri);
+ if (!rdev) {
+ rdev = dev_regulator_get(dev, supply);
+ if (IS_ERR(rdev))
+ return ERR_CAST(rdev);
}
- if (!ri)
+ if (!rdev)
return NULL;
- ret = regulator_resolve_supply(ri->rdev);
+ ret = regulator_resolve_supply(rdev);
if (ret < 0)
return ERR_PTR(ret);
r = xzalloc(sizeof(*r));
- r->ri = ri;
+ r->rdev = rdev;
r->dev = dev;
- list_add_tail(&r->list, &ri->consumer_list);
+ list_add_tail(&r->list, &rdev->consumer_list);
return r;
}
-static struct regulator_internal *regulator_by_name(const char *name)
+void regulator_put(struct regulator *r)
{
- struct regulator_internal *ri;
+ if (IS_ERR_OR_NULL(r))
+ return;
+ list_del(&r->list);
+ free(r);
+}
- list_for_each_entry(ri, &regulator_list, list)
- if (ri->name && !strcmp(ri->name, name))
- return ri;
+static struct regulator_dev *regulator_by_name(const char *name)
+{
+ struct regulator_dev *rdev;
+
+ list_for_each_entry(rdev, &regulator_list, list) {
+ if (rdev->name && !strcmp(rdev->name, name))
+ return rdev;
+ }
return NULL;
}
struct regulator *regulator_get_name(const char *name)
{
- struct regulator_internal *ri;
+ struct regulator_dev *rdev;
struct regulator *r;
- ri = regulator_by_name(name);
- if (!ri)
+ rdev = regulator_by_name(name);
+ if (!rdev)
return ERR_PTR(-ENODEV);
r = xzalloc(sizeof(*r));
- r->ri = ri;
+ r->rdev = rdev;
- list_add_tail(&r->list, &ri->consumer_list);
+ list_add_tail(&r->list, &rdev->consumer_list);
return r;
}
@@ -458,7 +553,7 @@ int regulator_enable(struct regulator *r)
if (!r)
return 0;
- return regulator_enable_internal(r->ri);
+ return regulator_enable_rdev(r->rdev);
}
/*
@@ -475,7 +570,7 @@ int regulator_disable(struct regulator *r)
if (!r)
return 0;
- return regulator_disable_internal(r->ri);
+ return regulator_disable_rdev(r->rdev);
}
int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV)
@@ -483,7 +578,7 @@ int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV)
if (!r)
return 0;
- return regulator_set_voltage_internal(r->ri, min_uV, max_uV);
+ return regulator_set_voltage_rdev(r->rdev, min_uV, max_uV);
}
/**
@@ -500,7 +595,7 @@ int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV)
* acquired then any regulators that were allocated will be freed
* before returning to the caller.
*/
-int regulator_bulk_get(struct device_d *dev, int num_consumers,
+int regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{
int i;
@@ -627,61 +722,55 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free);
int regulator_get_voltage(struct regulator *regulator)
{
- struct regulator_internal *ri;
- struct regulator_dev *rdev;
- int sel, ret;
-
if (!regulator)
return -EINVAL;
- ri = regulator->ri;
- rdev = ri->rdev;
-
- if (rdev->desc->ops->get_voltage_sel) {
- sel = rdev->desc->ops->get_voltage_sel(rdev);
- if (sel < 0)
- return sel;
- ret = rdev->desc->ops->list_voltage(rdev, sel);
- } else if (rdev->desc->ops->get_voltage) {
- ret = rdev->desc->ops->get_voltage(rdev);
- } else if (rdev->desc->ops->list_voltage) {
- ret = rdev->desc->ops->list_voltage(rdev, 0);
- } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
- ret = rdev->desc->fixed_uV;
- } else if (ri->min_uv && ri->min_uv == ri->max_uv) {
- ret = ri->min_uv;
- } else if (rdev->supply) {
- ret = regulator_get_voltage(rdev->supply);
- } else {
- return -EINVAL;
- }
-
- return ret;
+ return regulator_get_voltage_rdev(regulator->rdev);
}
EXPORT_SYMBOL_GPL(regulator_get_voltage);
-static void regulator_print_one(struct regulator_internal *ri)
+static int regulator_name_indent(unsigned flags)
{
+ return 30 + (flags & REGULATOR_PRINT_DEVS ? 50 : 0);
+}
+
+static void regulator_print_one(struct regulator_dev *rdev, int level, unsigned flags)
+{
+ const char *name = rdev->name;
struct regulator *r;
+ char buf[256];
+
+ if (!rdev)
+ return;
- printf("%-20s %6d %10d %10d\n", ri->name, ri->enable_count, ri->min_uv, ri->max_uv);
+ if (flags & REGULATOR_PRINT_DEVS) {
+ snprintf(buf, sizeof(buf), "%s %s", dev_name(rdev->dev), rdev->name);
+ name = buf;
+ }
- if (!list_empty(&ri->consumer_list)) {
- printf(" consumers:\n");
+ printf("%*s%-*s %6d %10d %10d\n", level * 3, "",
+ regulator_name_indent(flags) - level * 3,
+ name, rdev->enable_count, rdev->min_uv, rdev->max_uv);
- list_for_each_entry(r, &ri->consumer_list, list)
- printf(" %s\n", r->dev ? dev_name(r->dev) : "none");
+ list_for_each_entry(r, &rdev->consumer_list, list) {
+ if (r->rdev_consumer)
+ regulator_print_one(r->rdev_consumer, level + 1, flags);
+ else
+ printf("%*s%s\n", (level + 1) * 3, "", r->dev ? dev_name(r->dev) : "none");
}
}
/*
* regulators_print - print informations about all regulators
*/
-void regulators_print(void)
+void regulators_print(unsigned flags)
{
- struct regulator_internal *ri;
+ struct regulator_dev *rdev;
- printf("%-20s %6s %10s %10s\n", "name", "enable", "min_uv", "max_uv");
- list_for_each_entry(ri, &regulator_list, list)
- regulator_print_one(ri);
+ printf("%-*s %6s %10s %10s\n", regulator_name_indent(flags),
+ "name", "enable", "min_uv", "max_uv");
+ list_for_each_entry(rdev, &regulator_list, list) {
+ if (!rdev->supply)
+ regulator_print_one(rdev, 0, flags);
+ }
}
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index bdb01c0a95..0edb5ceb10 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -9,12 +9,10 @@
#include <init.h>
#include <regulator.h>
#include <of.h>
-#include <of_gpio.h>
-#include <gpio.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
struct regulator_fixed {
- int gpio;
+ struct gpio_desc *gpio;
struct regulator_dev rdev;
struct regulator_desc rdesc;
};
@@ -23,20 +21,14 @@ static int regulator_fixed_enable(struct regulator_dev *rdev)
{
struct regulator_fixed *fix = container_of(rdev, struct regulator_fixed, rdev);
- if (!gpio_is_valid(fix->gpio))
- return 0;
-
- return gpio_direction_active(fix->gpio, true);
+ return gpiod_direction_output(fix->gpio, true);
}
static int regulator_fixed_disable(struct regulator_dev *rdev)
{
struct regulator_fixed *fix = container_of(rdev, struct regulator_fixed, rdev);
- if (!gpio_is_valid(fix->gpio))
- return 0;
-
- return gpio_direction_active(fix->gpio, false);
+ return gpiod_direction_output(fix->gpio, false);
}
const static struct regulator_ops fixed_ops = {
@@ -44,25 +36,22 @@ const static struct regulator_ops fixed_ops = {
.disable = regulator_fixed_disable,
};
-static int regulator_fixed_probe(struct device_d *dev)
+static int regulator_fixed_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct regulator_fixed *fix;
u32 delay;
int ret;
- if (!dev->device_node)
+ if (!dev->of_node)
return -EINVAL;
fix = xzalloc(sizeof(*fix));
- fix->gpio = -EINVAL;
-
- if (of_get_property(np, "gpio", NULL)) {
- fix->gpio = gpiod_get(dev, NULL, GPIOD_ASIS);
- if (fix->gpio < 0) {
- ret = fix->gpio;
- goto err;
- }
+
+ fix->gpio = gpiod_get_optional(dev, NULL, GPIOD_ASIS);
+ if (IS_ERR(fix->gpio)) {
+ ret = PTR_ERR(fix->gpio);
+ goto err;
}
fix->rdesc.ops = &fixed_ops;
@@ -90,8 +79,9 @@ static struct of_device_id regulator_fixed_of_ids[] = {
{ .compatible = "regulator-fixed", },
{ }
};
+MODULE_DEVICE_TABLE(of, regulator_fixed_of_ids);
-static struct driver_d regulator_fixed_driver = {
+static struct driver regulator_fixed_driver = {
.name = "regulator-fixed",
.probe = regulator_fixed_probe,
.of_compatible = DRV_OF_COMPAT(regulator_fixed_of_ids),
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index fb689a6bfc..80102e2c10 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <regulator.h>
/**
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index c536a82c43..10f75a4f1c 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -33,7 +33,7 @@ struct devm_of_regulator_matches {
*
* Returns the number of matches found or a negative error code on failure.
*/
-int of_regulator_match(struct device_d *dev, struct device_node *node,
+int of_regulator_match(struct device *dev, struct device_node *node,
struct of_regulator_match *matches,
unsigned int num_matches)
{
diff --git a/drivers/regulator/pfuze.c b/drivers/regulator/pfuze.c
index 5ef5dacc6f..3e8890b10c 100644
--- a/drivers/regulator/pfuze.c
+++ b/drivers/regulator/pfuze.c
@@ -10,13 +10,12 @@
#include <errno.h>
#include <malloc.h>
#include <of.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <mfd/pfuze.h>
#include <i2c/i2c.h>
#include <poweroff.h>
-#include <mach/imx6.h>
#define DRIVERNAME "pfuze"
@@ -41,7 +40,7 @@
#define PFUZE100_VGENxSTBY BIT(5)
struct pfuze {
- struct device_d *dev;
+ struct device *dev;
struct regmap *map;
struct i2c_client *client;
int revision;
@@ -144,7 +143,7 @@ static const struct regmap_config pfuze_regmap_i2c_config = {
.max_register = 127,
};
-static int __init pfuze_probe(struct device_d *dev)
+static int __init pfuze_probe(struct device *dev)
{
int ret;
@@ -165,7 +164,7 @@ static int __init pfuze_probe(struct device_d *dev)
if (pfuze_init_callback)
pfuze_init_callback(pfuze_dev->map);
- if (of_property_read_bool(dev->device_node,
+ if (of_property_read_bool(dev->of_node,
"fsl,pmic-stby-poweroff"))
return poweroff_handler_register_fn(pfuze_power_off_prepare);
@@ -187,8 +186,9 @@ static __maybe_unused struct of_device_id pfuze_dt_ids[] = {
{ .compatible = "fsl,pfuze3001" },
{ }
};
+MODULE_DEVICE_TABLE(of, pfuze_dt_ids);
-static struct driver_d pfuze_i2c_driver = {
+static struct driver pfuze_i2c_driver = {
.name = "pfuze-i2c",
.probe = pfuze_probe,
.id_table = pfuze_ids,
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index 39eadbd3eb..adb0262314 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -14,7 +14,7 @@
#include <init.h>
#include <i2c/i2c.h>
#include <of_device.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/regulator/of_regulator.h>
#include <regulator.h>
#include <linux/mfd/rk808.h>
@@ -526,19 +526,6 @@ static struct rk_regulator_cfg rk809_reg[] = {
.enable_mask = ENABLE_MASK(RK817_ID_DCDC4),
.enable_val = ENABLE_MASK(RK817_ID_DCDC4),
.disable_val = DISABLE_VAL(RK817_ID_DCDC4),
- }}, {{
- /* .name = "DCDC_REG5", */
- .supply_name = "vcc9",
- .ops = &rk809_buck5_ops_range,
- .n_voltages = RK809_BUCK5_SEL_CNT,
- .linear_ranges = rk809_buck5_voltage_ranges,
- .n_linear_ranges = ARRAY_SIZE(rk809_buck5_voltage_ranges),
- .vsel_reg = RK809_BUCK5_CONFIG(0),
- .vsel_mask = RK809_BUCK5_VSEL_MASK,
- .enable_reg = RK817_POWER_EN_REG(3),
- .enable_mask = ENABLE_MASK(1),
- .enable_val = ENABLE_MASK(1),
- .disable_val = DISABLE_VAL(1),
}},
RK817_DESC(/* "LDO_REG1", */ "vcc5", 600, 3400, 25,
RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK,
@@ -576,6 +563,20 @@ static struct rk_regulator_cfg rk809_reg[] = {
RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK,
RK817_POWER_EN_REG(3), ENABLE_MASK(0),
DISABLE_VAL(0), 400),
+ {{
+ /* .name = "DCDC_REG5", */
+ .supply_name = "vcc9",
+ .ops = &rk809_buck5_ops_range,
+ .n_voltages = RK809_BUCK5_SEL_CNT,
+ .linear_ranges = rk809_buck5_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk809_buck5_voltage_ranges),
+ .vsel_reg = RK809_BUCK5_CONFIG(0),
+ .vsel_mask = RK809_BUCK5_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(3),
+ .enable_mask = ENABLE_MASK(1),
+ .enable_val = ENABLE_MASK(1),
+ .disable_val = DISABLE_VAL(1),
+ }},
RK817_DESC_SWITCH(/* "SWITCH_REG1", */ "vcc9",
RK817_POWER_EN_REG(3), ENABLE_MASK(2), DISABLE_VAL(2)),
RK817_DESC_SWITCH(/* "SWITCH_REG2", */ "vcc8",
@@ -769,7 +770,7 @@ static int rk808_regulator_register(struct rk808 *rk808, int id,
struct of_regulator_match *match,
struct rk_regulator_cfg *cfg)
{
- struct device_d *dev = &rk808->i2c->dev;
+ struct device *dev = &rk808->i2c->dev;
int ret;
if (!match->of_node) {
@@ -782,10 +783,9 @@ static int rk808_regulator_register(struct rk808 *rk808, int id,
cfg->rdev.regmap = rk808->regmap;
ret = of_regulator_register(&cfg->rdev, match->of_node);
- if (ret) {
- dev_err(dev, "failed to register %s regulator\n", match->name);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register %s regulator\n",
+ match->name);
dev_dbg(dev, "registered %s\n", match->name);
@@ -884,11 +884,11 @@ static struct of_regulator_match rk818_reg_matches[] = {
};
static_assert(ARRAY_SIZE(rk818_reg_matches) == RK818_NUM_REGULATORS);
-static int rk808_regulator_dt_parse(struct device_d *dev,
+static int rk808_regulator_dt_parse(struct device *dev,
struct of_regulator_match *matches,
int nregulators)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
np = of_get_child_by_name(np, "regulators");
if (!np)
@@ -897,7 +897,7 @@ static int rk808_regulator_dt_parse(struct device_d *dev,
return of_regulator_match(dev, np, matches, nregulators);
}
-static int rk808_regulator_probe(struct device_d *dev)
+static int rk808_regulator_probe(struct device *dev)
{
struct rk808 *rk808 = dev->parent->priv;
struct rk_regulator_cfg *regulators;
@@ -950,7 +950,7 @@ static int rk808_regulator_probe(struct device_d *dev)
return 0;
}
-static struct driver_d rk808_regulator_driver = {
+static struct driver rk808_regulator_driver = {
.name = "rk808-regulator",
.probe = rk808_regulator_probe,
};
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
index 3e6fd3ec60..6f22fa6420 100644
--- a/drivers/regulator/scmi-regulator.c
+++ b/drivers/regulator/scmi-regulator.c
@@ -44,7 +44,7 @@ struct scmi_regulator {
struct scmi_device *sdev;
struct scmi_protocol_handle *ph;
struct regulator_dev rdev;
- struct device_node *device_node;
+ struct device_node *of_node;
struct scmi_reg_desc sdesc;
};
@@ -213,7 +213,7 @@ scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg,
static int scmi_regulator_common_init(struct scmi_regulator *sreg)
{
int ret;
- struct device_d *dev = &sreg->sdev->dev;
+ struct device *dev = &sreg->sdev->dev;
const struct scmi_voltage_info *vinfo;
struct scmi_reg_desc *sdesc = &sreg->sdesc;
@@ -231,8 +231,8 @@ static int scmi_regulator_common_init(struct scmi_regulator *sreg)
* be non-negative from here on.
*/
if (vinfo->negative_volts_allowed) {
- dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
- sreg->device_node->full_name);
+ dev_warn(dev, "Negative voltages NOT supported...skip %pOF\n",
+ sreg->of_node);
return -EOPNOTSUPP;
}
@@ -264,8 +264,8 @@ static int process_scmi_regulator_of_node(struct scmi_device *sdev,
if (rinfo->sregv[dom]) {
dev_err(&sdev->dev,
- "SCMI Voltage Domain %d already in use. Skipping: %s\n",
- dom, np->full_name);
+ "SCMI Voltage Domain %d already in use. Skipping: %pOF\n",
+ dom, np);
return -EINVAL;
}
@@ -278,11 +278,11 @@ static int process_scmi_regulator_of_node(struct scmi_device *sdev,
rinfo->sregv[dom]->ph = ph;
/* get hold of good nodes */
- rinfo->sregv[dom]->device_node = np;
+ rinfo->sregv[dom]->of_node = np;
dev_dbg(&sdev->dev,
- "Found SCMI Regulator entry -- OF node [%d] -> %s\n",
- dom, np->full_name);
+ "Found SCMI Regulator entry -- OF node [%d] -> %pOF\n",
+ dom, np);
return 0;
}
@@ -299,7 +299,7 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
if (!handle)
return -ENODEV;
- voltage_ops = handle->protocol_get(sdev, SCMI_PROTOCOL_VOLTAGE, &ph);
+ voltage_ops = handle->dev_protocol_get(sdev, SCMI_PROTOCOL_VOLTAGE, &ph);
if (IS_ERR(voltage_ops))
return PTR_ERR(voltage_ops);
@@ -335,7 +335,7 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
* plausible SCMI Voltage Domain number, all belonging to this SCMI
* platform instance node (handle->dev->of_node).
*/
- np = of_find_node_by_name(handle->dev->device_node, "regulators");
+ np = of_find_node_by_name(handle->dev->of_node, "regulators");
for_each_child_of_node(np, child) {
ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);
/* abort on any mem issue */
@@ -362,13 +362,16 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
if (ret)
continue;
- ret = of_regulator_register(&sreg->rdev, np);
+ sreg->rdev.desc = &sdesc->desc;
+ sreg->rdev.dev = &sdev->dev;
+
+ ret = of_regulator_register(&sreg->rdev, sreg->of_node);
if (ret)
continue;
- dev_info(&sdev->dev,
- "Regulator %s registered for domain [%d]\n",
- sreg->sdesc.name, sreg->id);
+ dev_dbg(&sdev->dev,
+ "Regulator %s registered for domain [%d]\n",
+ sreg->sdesc.name, sreg->id);
}
return 0;
diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c
index 8cb0b0c12e..ca03529b7f 100644
--- a/drivers/regulator/stm32-pwr.c
+++ b/drivers/regulator/stm32-pwr.c
@@ -141,7 +141,7 @@ static const struct stm32_pwr_desc stm32_pwr_desc[] = {
PWR_REG(PWR_USB33, "usb33", 3300000, USB_3_3_EN, "vdd_3v3_usbfs"),
};
-static int stm32_pwr_regulator_probe(struct device_d *dev)
+static int stm32_pwr_regulator_probe(struct device *dev)
{
struct stm32_pwr_reg *priv;
struct device_node *child;
@@ -152,7 +152,7 @@ static int stm32_pwr_regulator_probe(struct device_d *dev)
if (IS_ERR(iores))
return PTR_ERR(iores);
- for_each_child_of_node(dev->device_node, child) {
+ for_each_child_of_node(dev->of_node, child) {
const struct stm32_pwr_desc *desc = NULL;
for (i = 0; i < STM32PWR_REG_NUM_REGS; i++) {
@@ -206,8 +206,9 @@ static const struct of_device_id stm32_pwr_of_match[] = {
{ .compatible = "st,stm32mp1,pwr-reg", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_pwr_of_match);
-static struct driver_d stm32_pwr_driver = {
+static struct driver stm32_pwr_driver = {
.probe = stm32_pwr_regulator_probe,
.name = "stm32-pwr-regulator",
.of_compatible = stm32_pwr_of_match,
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
index 77287c2b0e..18cf53e735 100644
--- a/drivers/regulator/stm32-vrefbuf.c
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -27,7 +27,7 @@
struct stm32_vrefbuf {
void __iomem *base;
struct clk *clk;
- struct device_d *dev;
+ struct device *dev;
struct regulator_dev rdev;
};
@@ -140,7 +140,7 @@ static const struct stm32_vrefbuf_desc stm32_vrefbuf_regu = {
.supply_name = "vdda",
};
-static int stm32_vrefbuf_probe(struct device_d *dev)
+static int stm32_vrefbuf_probe(struct device *dev)
{
struct stm32_vrefbuf *priv;
struct regulator_dev *rdev;
@@ -173,7 +173,7 @@ static int stm32_vrefbuf_probe(struct device_d *dev)
rdev->dev = dev;
rdev->desc = &stm32_vrefbuf_regu.desc;
- ret = of_regulator_register(rdev, dev->device_node);
+ ret = of_regulator_register(rdev, dev->of_node);
if (ret) {
ret = PTR_ERR(rdev);
dev_err(dev, "register failed with error %d\n", ret);
@@ -192,7 +192,7 @@ err_clk_dis:
return ret;
}
-static void stm32_vrefbuf_remove(struct device_d *dev)
+static void stm32_vrefbuf_remove(struct device *dev)
{
struct stm32_vrefbuf *priv = dev->priv;
@@ -203,8 +203,9 @@ static const struct of_device_id __maybe_unused stm32_vrefbuf_of_match[] = {
{ .compatible = "st,stm32-vrefbuf", },
{},
};
+MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match);
-static struct driver_d stm32_vrefbuf_driver = {
+static struct driver stm32_vrefbuf_driver = {
.probe = stm32_vrefbuf_probe,
.name = "stm32-vrefbuf",
.remove = stm32_vrefbuf_remove,
diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c
index 63ed4468ed..3ed351b580 100644
--- a/drivers/regulator/stpmic1_regulator.c
+++ b/drivers/regulator/stpmic1_regulator.c
@@ -5,7 +5,7 @@
#include <common.h>
#include <init.h>
#include <of_device.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/regulator/of_regulator.h>
#include <regulator.h>
#include <linux/mfd/stpmic1.h>
@@ -126,6 +126,7 @@ static const struct regulator_ops stpmic1_ldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
@@ -135,6 +136,7 @@ static const struct regulator_ops stpmic1_ldo3_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
@@ -150,6 +152,7 @@ static const struct regulator_ops stpmic1_buck_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
@@ -182,6 +185,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = LDO_ENABLE_MASK, \
.enable_val = 1, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_LDO3(ids, base) { \
@@ -195,6 +199,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = LDO_ENABLE_MASK, \
.enable_val = 1, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_LDO4(ids, base) { \
@@ -205,6 +210,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = LDO_ENABLE_MASK, \
.enable_val = 1, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_BUCK(ids, base) { \
@@ -218,6 +224,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = BUCK_ENABLE_MASK, \
.enable_val = 1, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_VREF_DDR(ids, base) { \
@@ -228,6 +235,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = BUCK_ENABLE_MASK, \
.enable_val = 1, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_BOOST(ids, base) { \
@@ -238,6 +246,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = BOOST_ENABLED, \
.enable_val = BOOST_ENABLED, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_VBUS_OTG(ids, base) { \
@@ -248,6 +257,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = USBSW_OTG_SWITCH_ENABLED, \
.enable_val = USBSW_OTG_SWITCH_ENABLED, \
.disable_val = 0, \
+ .supply_name = #base, \
}
#define REG_SW_OUT(ids, base) { \
@@ -258,6 +268,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = {
.enable_mask = SWIN_SWOUT_ENABLED, \
.enable_val = SWIN_SWOUT_ENABLED, \
.disable_val = 0, \
+ .supply_name = #base, \
}
static struct stpmic1_regulator_cfg stpmic1_regulator_cfgs[] = {
@@ -376,7 +387,7 @@ static struct of_regulator_match stpmic1_matches[] = {
MATCH(pwr_sw2, SW_OUT),
};
-static int stpmic1_regulator_register(struct device_d *dev, int id,
+static int stpmic1_regulator_register(struct device *dev, int id,
struct of_regulator_match *match,
struct stpmic1_regulator_cfg *cfg)
{
@@ -404,11 +415,11 @@ static int stpmic1_regulator_register(struct device_d *dev, int id,
return 0;
}
-static int stpmic1_regulator_probe(struct device_d *dev)
+static int stpmic1_regulator_probe(struct device *dev)
{
int i, ret;
- ret = of_regulator_match(dev, dev->device_node, stpmic1_matches,
+ ret = of_regulator_match(dev, dev->of_node, stpmic1_matches,
ARRAY_SIZE(stpmic1_matches));
if (ret < 0) {
dev_err(dev, "Error in PMIC regulator device tree node");
@@ -431,8 +442,9 @@ static __maybe_unused const struct of_device_id stpmic1_regulator_of_match[] = {
{ .compatible = "st,stpmic1-regulators" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stpmic1_regulator_of_match);
-static struct driver_d stpmic1_regulator_driver = {
+static struct driver stpmic1_regulator_driver = {
.name = "stpmic1-regulator",
.probe = stpmic1_regulator_probe,
.of_compatible = DRV_OF_COMPAT(stpmic1_regulator_of_match),
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index a193fadb69..94babd28ff 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -12,11 +12,11 @@ config REMOTEPROC
if REMOTEPROC
config IMX_REMOTEPROC
- tristate "IMX6/7 remoteproc support"
+ tristate "IMX6/7/8 remoteproc support"
depends on ARCH_IMX
select MFD_SYSCON
help
- Say y here to support iMX's remote processors (Cortex M4
+ Say y here to support iMX's remote processors (e.g. Cortex M4
on iMX7D) via the remote processor framework.
It's safe to say N here.
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index ff74adb112..6ae8ef3966 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -17,7 +17,7 @@
#include <memory.h>
#include <of_address.h>
#include <of_device.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#define IMX7D_SRC_SCR 0x0C
#define IMX7D_ENABLE_M4 BIT(3)
@@ -79,7 +79,7 @@ struct imx_rproc_dcfg {
};
struct imx_rproc {
- struct device_d *dev;
+ struct device *dev;
struct regmap *regmap;
struct regmap *gpr;
struct rproc *rproc;
@@ -384,10 +384,10 @@ static const struct rproc_ops imx_rproc_ops = {
};
static int imx_rproc_addr_init(struct imx_rproc *priv,
- struct device_d *dev)
+ struct device *dev)
{
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int a, b = 0, err, nph;
/* remap required addresses */
@@ -422,7 +422,7 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
/* remap optional addresses */
for (a = 0; a < nph; a++) {
struct device_node *node;
- struct resource res, *res_cpu;
+ struct resource res;
node = of_parse_phandle(np, "memory-region", a);
err = of_address_to_resource(node, 0, &res);
@@ -434,13 +434,12 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
if (b >= IMX7D_RPROC_MEM_MAX)
break;
- res_cpu = request_sdram_region(dev_name(dev), res.start,
- resource_size(&res));
- if (!res_cpu) {
- dev_err(dev, "remap optional addresses failed\n");
- return -ENOMEM;
- }
- priv->mem[b].cpu_addr = (void *)res_cpu->start;
+ /*
+ * reserved memory region are automatically requested and
+ * mapped uncached
+ */
+
+ priv->mem[b].cpu_addr = phys_to_virt(res.start);
priv->mem[b].sys_addr = res.start;
priv->mem[b].size = resource_size(&res);
b++;
@@ -449,9 +448,9 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
return 0;
}
-static int imx_rproc_probe(struct device_d *dev)
+static int imx_rproc_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct imx_rproc *priv;
struct rproc *rproc;
const struct imx_rproc_dcfg *dcfg;
@@ -533,8 +532,9 @@ static const struct of_device_id imx_rproc_of_match[] = {
{ .compatible = "fsl,imx8mq-cm4", .data = &imx_rproc_cfg_imx8mq },
{},
};
+MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
-static struct driver_d imx_rproc_driver = {
+static struct driver imx_rproc_driver = {
.name = "imx-rproc",
.probe = imx_rproc_probe,
.of_compatible = DRV_OF_COMPAT(imx_rproc_of_match),
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index a81787bc6c..7590c1f930 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -35,7 +35,7 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
static int rproc_start(struct rproc *rproc, const struct firmware *fw)
{
- struct device_d *dev = &rproc->dev;
+ struct device *dev = &rproc->dev;
int ret;
/* load the ELF segments to memory */
@@ -85,7 +85,7 @@ static int rproc_firmware_finish(struct firmware_handler *fh)
{
struct rproc *rproc = container_of(fh, struct rproc, fh);
struct firmware fw;
- struct device_d *dev;
+ struct device *dev;
int ret;
if (!rproc) {
@@ -122,13 +122,13 @@ static int rproc_register_dev(struct rproc *rproc, const char *alias)
int rproc_add(struct rproc *rproc)
{
- struct device_d *dev = &rproc->dev;
+ struct device *dev = &rproc->dev;
struct firmware_handler *fh;
const char *alias = NULL;
int ret;
- if (dev->device_node)
- alias = of_alias_get(dev->device_node);
+ if (dev->of_node)
+ alias = of_alias_get(dev->of_node);
ret = rproc_register_dev(rproc, alias);
if (ret)
@@ -150,7 +150,7 @@ int rproc_add(struct rproc *rproc)
return ret;
}
-struct rproc *rproc_alloc(struct device_d *dev, const char *name,
+struct rproc *rproc_alloc(struct device *dev, const char *name,
const struct rproc_ops *ops, int len)
{
struct rproc *rproc;
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index 45d52db5c1..f3bf93df2c 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -23,7 +23,7 @@
int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
{
- struct device_d *dev = &rproc->dev;
+ struct device *dev = &rproc->dev;
struct elf32_hdr *ehdr;
struct elf32_phdr *phdr;
int i, ret = 0;
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index d29b59bcdc..4dd1634a47 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -10,10 +10,10 @@
#include <driver.h>
#include <init.h>
#include <io.h>
-#include <mach/smc.h>
+#include <mach/stm32mp/smc.h>
#include <mfd/syscon.h>
#include <of_address.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/remoteproc.h>
#include <linux/reset.h>
@@ -30,17 +30,18 @@ struct stm32_syscon {
struct stm32_rproc {
struct reset_control *rst;
+ struct reset_control *hold_boot_rst;
struct stm32_syscon hold_boot;
- bool secured_soc;
+ bool hold_boot_smc;
};
static void *stm32_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
{
__be32 in_addr = cpu_to_be32(da);
- struct device_d *dev = &rproc->dev;
+ struct device *dev = &rproc->dev;
u64 paddr;
- paddr = of_translate_dma_address(dev->parent->device_node, &in_addr);
+ paddr = of_translate_dma_address(dev->parent->of_node, &in_addr);
if (paddr == OF_BAD_ADDR)
return NULL;
@@ -54,13 +55,28 @@ static int stm32_rproc_set_hold_boot(struct rproc *rproc, bool hold)
struct arm_smccc_res smc_res;
int val, err;
+ /*
+ * Three ways to manage the hold boot
+ * - using SCMI: the hold boot is managed as a reset,
+ * - using Linux(no SCMI): the hold boot is managed as a syscon register
+ * - using SMC call (deprecated): use SMC reset interface
+ */
+
val = hold ? HOLD_BOOT : RELEASE_BOOT;
- if (IS_ENABLED(CONFIG_ARM_SMCC) && ddata->secured_soc) {
+ if (ddata->hold_boot_rst) {
+ /* Use the SCMI reset controller */
+ if (!hold)
+ err = reset_control_deassert(ddata->hold_boot_rst);
+ else
+ err = reset_control_assert(ddata->hold_boot_rst);
+ } else if (IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) && ddata->hold_boot_smc) {
+ /* Use the SMC call */
arm_smccc_smc(STM32_SMC_RCC, STM32_SMC_REG_WRITE,
hold_boot->reg, val, 0, 0, 0, 0, &smc_res);
err = smc_res.a0;
} else {
+ /* Use syscon */
err = regmap_update_bits(hold_boot->map, hold_boot->reg,
hold_boot->mask, val);
}
@@ -128,9 +144,9 @@ out:
return err;
}
-static int stm32_rproc_parse_dt(struct device_d *dev, struct stm32_rproc *ddata)
+static int stm32_rproc_parse_dt(struct device *dev, struct stm32_rproc *ddata)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct stm32_syscon tz;
unsigned int tzen;
int err;
@@ -142,34 +158,48 @@ static int stm32_rproc_parse_dt(struct device_d *dev, struct stm32_rproc *ddata)
}
/*
- * if platform is secured the hold boot bit must be written by
- * smc call and read normally.
- * if not secure the hold boot bit could be read/write normally
+ * Three ways to manage the hold boot
+ * - using SCMI: the hold boot is managed as a reset
+ * The DT "reset-mames" property should be defined with 2 items:
+ * reset-names = "mcu_rst", "hold_boot";
+ * - using SMC call (deprecated): use SMC reset interface
+ * The DT "reset-mames" property is optional, "st,syscfg-tz" is required
+ * - default(no SCMI, no SMC): the hold boot is managed as a syscon register
+ * The DT "reset-mames" property is optional, "st,syscfg-holdboot" is required
*/
- err = stm32_rproc_get_syscon(np, "st,syscfg-tz", &tz);
- if (err) {
- dev_err(dev, "failed to get tz syscfg\n");
- return err;
- }
- err = regmap_read(tz.map, tz.reg, &tzen);
- if (err) {
- dev_err(dev, "failed to read tzen\n");
- return err;
+ ddata->hold_boot_rst = reset_control_get_optional(dev, "hold_boot");
+ if (IS_ERR(ddata->hold_boot_rst))
+ return dev_err_probe(dev, PTR_ERR(ddata->hold_boot_rst),
+ "failed to get hold_boot reset\n");
+
+ if (!ddata->hold_boot_rst && IS_ENABLED(CONFIG_HAVE_ARM_SMCCC)) {
+ /* Manage the MCU_BOOT using SMC call */
+ err = stm32_rproc_get_syscon(np, "st,syscfg-tz", &tz);
+ if (!err) {
+ err = regmap_read(tz.map, tz.reg, &tzen);
+ if (err) {
+ dev_err(dev, "failed to read tzen\n");
+ return err;
+ }
+ ddata->hold_boot_smc = tzen & tz.mask;
+ }
}
- ddata->secured_soc = tzen & tz.mask;
- err = stm32_rproc_get_syscon(np, "st,syscfg-holdboot",
- &ddata->hold_boot);
- if (err) {
- dev_err(dev, "failed to get hold boot\n");
- return err;
+ if (!ddata->hold_boot_rst && !ddata->hold_boot_smc) {
+ /* Default: hold boot manage it through the syscon controller */
+ err = stm32_rproc_get_syscon(np, "st,syscfg-holdboot",
+ &ddata->hold_boot);
+ if (err) {
+ dev_err(dev, "failed to get hold boot\n");
+ return err;
+ }
}
return 0;
}
-static int stm32_rproc_probe(struct device_d *dev)
+static int stm32_rproc_probe(struct device *dev)
{
struct rproc *rproc;
int ret;
@@ -190,8 +220,9 @@ static const struct of_device_id stm32_rproc_of_match[] = {
{ .compatible = "st,stm32mp1-m4" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_rproc_of_match);
-static struct driver_d stm32_rproc_driver = {
+static struct driver stm32_rproc_driver = {
.name = "stm32-rproc",
.probe = stm32_rproc_probe,
.of_compatible = DRV_OF_COMPAT(stm32_rproc_of_match),
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index b252f94d6e..94bfad2067 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -29,8 +29,21 @@ struct reset_control {
int gpio;
int gpio_active_high;
- struct device_d *dev;
+ struct device *dev;
unsigned int id;
+ bool array;
+};
+
+/**
+ * struct reset_control_array - an array of reset controls
+ * @base: reset control for compatibility with reset control API functions
+ * @num_rstcs: number of reset controls
+ * @rstc: array of reset controls
+ */
+struct reset_control_array {
+ struct reset_control base;
+ unsigned int num_rstcs;
+ struct reset_control *rstc[];
};
/**
@@ -102,6 +115,65 @@ int reset_control_status(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_status);
+static inline struct reset_control_array *
+rstc_to_array(struct reset_control *rstc) {
+ return container_of(rstc, struct reset_control_array, base);
+}
+
+static int reset_control_array_reset(struct reset_control_array *resets)
+{
+ int ret, i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ ret = reset_control_reset(resets->rstc[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int reset_control_array_assert(struct reset_control_array *resets)
+{
+ int ret, i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ ret = reset_control_assert(resets->rstc[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ reset_control_deassert(resets->rstc[i]);
+ return ret;
+}
+
+static int reset_control_array_deassert(struct reset_control_array *resets)
+{
+ int ret, i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ ret = reset_control_deassert(resets->rstc[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ reset_control_assert(resets->rstc[i]);
+ return ret;
+}
+
+static inline bool reset_control_is_array(struct reset_control *rstc)
+{
+ return rstc->array;
+}
+
/**
* reset_control_reset - reset the controlled device
* @rstc: reset controller
@@ -111,6 +183,9 @@ int reset_control_reset(struct reset_control *rstc)
if (!rstc)
return 0;
+ if (reset_control_is_array(rstc))
+ return reset_control_array_reset(rstc_to_array(rstc));
+
if (rstc->rcdev->ops->reset)
return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
@@ -127,6 +202,9 @@ int reset_control_assert(struct reset_control *rstc)
if (!rstc)
return 0;
+ if (reset_control_is_array(rstc))
+ return reset_control_array_assert(rstc_to_array(rstc));
+
if (rstc->gpio >= 0)
return gpio_direction_output(rstc->gpio, rstc->gpio_active_high);
@@ -146,6 +224,9 @@ int reset_control_deassert(struct reset_control *rstc)
if (!rstc)
return 0;
+ if (reset_control_is_array(rstc))
+ return reset_control_array_deassert(rstc_to_array(rstc));
+
if (rstc->gpio >= 0)
return gpio_direction_output(rstc->gpio, !rstc->gpio_active_high);
@@ -162,9 +243,10 @@ EXPORT_SYMBOL_GPL(reset_control_deassert);
*
* Returns number of resets, 0 if none specified
*/
-int reset_control_get_count(struct device_d *dev)
+int reset_control_get_count(struct device *dev)
{
- return of_count_phandle_with_args(dev->device_node, "resets", "#reset-cells");
+ return of_count_phandle_with_args(dev->of_node, "resets",
+ "#reset-cells");
}
/**
@@ -191,9 +273,8 @@ static struct reset_control *of_reset_control_get_by_index(struct device_node *n
if (ret)
return ERR_PTR(ret);
- ret = of_device_ensure_probed(args.np);
- if (ret)
- return ERR_PTR(ret);
+ /* Ignore error, as CLK_OF_DECLARE resets have no proper driver. */
+ of_device_ensure_probed(args.np);
rcdev = NULL;
list_for_each_entry(r, &reset_controller_list, list) {
@@ -246,7 +327,7 @@ struct reset_control *of_reset_control_get(struct device_node *node,
}
static struct reset_control *
-gpio_reset_control_get(struct device_d *dev, const char *id)
+gpio_reset_control_get(struct device *dev, const char *id)
{
struct reset_control *rc;
int gpio;
@@ -255,10 +336,10 @@ gpio_reset_control_get(struct device_d *dev, const char *id)
if (id)
return ERR_PTR(-EINVAL);
- if (!of_get_property(dev->device_node, "reset-gpios", NULL))
+ if (!of_get_property(dev->of_node, "reset-gpios", NULL))
return NULL;
- gpio = of_get_named_gpio_flags(dev->device_node, "reset-gpios", 0, &flags);
+ gpio = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0, &flags);
if (gpio < 0)
return ERR_PTR(gpio);
@@ -278,14 +359,14 @@ gpio_reset_control_get(struct device_d *dev, const char *id)
*
* Use of id names is optional.
*/
-struct reset_control *reset_control_get(struct device_d *dev, const char *id)
+struct reset_control *reset_control_get(struct device *dev, const char *id)
{
struct reset_control *rstc;
if (!dev)
return ERR_PTR(-EINVAL);
- rstc = of_reset_control_get(dev->device_node, id);
+ rstc = of_reset_control_get(dev->of_node, id);
if (IS_ERR(rstc))
return ERR_CAST(rstc);
@@ -331,7 +412,7 @@ EXPORT_SYMBOL_GPL(reset_control_put);
* This is useful for the common case of devices with single, dedicated reset
* lines.
*/
-int device_reset(struct device_d *dev)
+int device_reset(struct device *dev)
{
struct reset_control *rstc;
int ret;
@@ -352,7 +433,53 @@ int device_reset(struct device_d *dev)
}
EXPORT_SYMBOL_GPL(device_reset);
-int device_reset_all(struct device_d *dev)
+/*
+ * APIs to manage an array of reset controls.
+ */
+
+/**
+ * reset_control_array_get - Get a list of reset controls
+ *
+ * @dev: device that requests the reset controls array
+ *
+ * Returns pointer to allocated reset_control on success or error on failure
+ */
+struct reset_control *reset_control_array_get(struct device *dev)
+{
+ struct reset_control_array *resets;
+ struct reset_control *rstc;
+ struct device_node *np = dev->of_node;
+ int num, i;
+
+ num = reset_control_get_count(dev);
+ if (num < 0)
+ return ERR_PTR(num);
+
+ resets = kzalloc(struct_size(resets, rstc, num), GFP_KERNEL);
+ if (!resets)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < num; i++) {
+ rstc = of_reset_control_get_by_index(np, i);
+ if (IS_ERR(rstc))
+ goto err_rst;
+ resets->rstc[i] = rstc;
+ }
+ resets->num_rstcs = num;
+ resets->base.array = true;
+
+ return &resets->base;
+
+err_rst:
+ while (--i >= 0)
+ reset_control_put(resets->rstc[i]);
+
+ kfree(resets);
+
+ return rstc;
+}
+
+int device_reset_all(struct device *dev)
{
struct reset_control *rstc;
int ret, i;
@@ -360,7 +487,7 @@ int device_reset_all(struct device_d *dev)
for (i = 0; i < reset_control_get_count(dev); i++) {
int ret;
- rstc = of_reset_control_get_by_index(dev->device_node, i);
+ rstc = of_reset_control_get_by_index(dev->of_node, i);
if (IS_ERR(rstc))
return PTR_ERR(rstc);
@@ -385,7 +512,7 @@ int device_reset_all(struct device_d *dev)
}
EXPORT_SYMBOL_GPL(device_reset_all);
-int device_reset_us(struct device_d *dev, int us)
+int device_reset_us(struct device *dev, int us)
{
struct reset_control *rstc;
int ret;
diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c
index 1591ace2cd..c6c38f48a8 100644
--- a/drivers/reset/reset-imx7.c
+++ b/drivers/reset/reset-imx7.c
@@ -14,7 +14,7 @@
#include <linux/err.h>
#include <linux/reset-controller.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <of_device.h>
struct imx7_src_signal {
@@ -253,14 +253,14 @@ static const struct imx7_src_variant variant_imx8mq = {
},
};
-static int imx7_reset_probe(struct device_d *dev)
+static int imx7_reset_probe(struct device *dev)
{
struct imx7_src *imx7src;
const struct imx7_src_variant *variant = of_device_get_match_data(dev);
imx7src = xzalloc(sizeof(*imx7src));
imx7src->signals = variant->signals;
- imx7src->regmap = syscon_node_to_regmap(dev->device_node);
+ imx7src->regmap = syscon_node_to_regmap(dev->of_node);
if (IS_ERR(imx7src->regmap)) {
dev_err(dev, "Unable to get imx7-src regmap");
return PTR_ERR(imx7src->regmap);
@@ -268,7 +268,7 @@ static int imx7_reset_probe(struct device_d *dev)
imx7src->rcdev.nr_resets = variant->signals_num;
imx7src->rcdev.ops = &variant->ops;
- imx7src->rcdev.of_node = dev->device_node;
+ imx7src->rcdev.of_node = dev->of_node;
return reset_controller_register(&imx7src->rcdev);
}
@@ -278,8 +278,9 @@ static const struct of_device_id imx7_reset_dt_ids[] = {
{ .compatible = "fsl,imx8mq-src", .data = &variant_imx8mq },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, imx7_reset_dt_ids);
-static struct driver_d imx7_reset_driver = {
+static struct driver imx7_reset_driver = {
.name = "imx7d-src",
.probe = imx7_reset_probe,
.of_compatible = DRV_OF_COMPAT(imx7_reset_dt_ids),
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
index c33bbc5c8a..d8c4734f1b 100644
--- a/drivers/reset/reset-scmi.c
+++ b/drivers/reset/reset-scmi.c
@@ -89,15 +89,15 @@ static const struct reset_control_ops scmi_reset_ops = {
static int scmi_reset_probe(struct scmi_device *sdev)
{
struct scmi_reset_data *data;
- struct device_d *dev = &sdev->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = &sdev->dev;
+ struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
struct scmi_protocol_handle *ph;
if (!handle)
return -ENODEV;
- reset_ops = handle->protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
+ reset_ops = handle->dev_protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
if (IS_ERR(reset_ops))
return PTR_ERR(reset_ops);
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
index 9db00f64f4..20f3df18f4 100644
--- a/drivers/reset/reset-simple.c
+++ b/drivers/reset/reset-simple.c
@@ -146,8 +146,9 @@ static const struct of_device_id reset_simple_dt_ids[] = {
.data = &reset_simple_active_low },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, reset_simple_dt_ids);
-static int reset_simple_probe(struct device_d *dev)
+static int reset_simple_probe(struct device *dev)
{
const struct reset_simple_devdata *devdata;
struct reset_simple_data *data;
@@ -167,7 +168,7 @@ static int reset_simple_probe(struct device_d *dev)
data->membase = IOMEM(res->start);
data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
data->rcdev.ops = &reset_simple_ops;
- data->rcdev.of_node = dev->device_node;
+ data->rcdev.of_node = dev->of_node;
if (devdata) {
reg_offset = devdata->reg_offset;
@@ -182,7 +183,7 @@ static int reset_simple_probe(struct device_d *dev)
return reset_controller_register(&data->rcdev);
}
-static struct driver_d reset_simple_driver = {
+static struct driver reset_simple_driver = {
.probe = reset_simple_probe,
.name = "simple-reset",
.of_compatible = reset_simple_dt_ids,
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
index 146cc92eb3..d214ce503e 100644
--- a/drivers/reset/reset-socfpga.c
+++ b/drivers/reset/reset-socfpga.c
@@ -74,11 +74,11 @@ static const struct reset_control_ops socfpga_reset_ops = {
.deassert = socfpga_reset_deassert,
};
-static int socfpga_reset_probe(struct device_d *dev)
+static int socfpga_reset_probe(struct device *dev)
{
struct socfpga_reset_data *data;
struct resource *res;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
u32 modrst_offset;
data = xzalloc(sizeof(*data));
@@ -108,8 +108,9 @@ static const struct of_device_id socfpga_reset_dt_ids[] = {
{ .compatible = "altr,rst-mgr", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, socfpga_reset_dt_ids);
-static struct driver_d socfpga_reset_driver = {
+static struct driver socfpga_reset_driver = {
.name = "socfpga_reset",
.probe = socfpga_reset_probe,
.of_compatible = DRV_OF_COMPAT(socfpga_reset_dt_ids),
diff --git a/drivers/reset/reset-starfive-vic.c b/drivers/reset/reset-starfive-vic.c
index 960e82e824..3e9e367215 100644
--- a/drivers/reset/reset-starfive-vic.c
+++ b/drivers/reset/reset-starfive-vic.c
@@ -196,7 +196,7 @@ static const struct reset_control_ops starfive_rstgen_ops = {
.reset = starfive_reset,
};
-static int starfive_rstgen_probe(struct device_d *dev)
+static int starfive_rstgen_probe(struct device *dev)
{
struct starfive_rstgen *priv;
struct resource *iores;
@@ -216,7 +216,7 @@ static int starfive_rstgen_probe(struct device_d *dev)
priv->base = IOMEM(iores->start);
priv->rcdev.nr_resets = RSTN_END;
priv->rcdev.ops = &starfive_rstgen_ops;
- priv->rcdev.of_node = dev->device_node;
+ priv->rcdev.of_node = dev->of_node;
return reset_controller_register(&priv->rcdev);
}
@@ -225,8 +225,9 @@ static const struct of_device_id starfive_rstgen_reset_dt_ids[] = {
{ .compatible = "starfive,jh7100-rstgen", .data = jh7110_rstgen_sync_resets },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, starfive_rstgen_reset_dt_ids);
-static struct driver_d starfive_rstgen_reset_driver = {
+static struct driver starfive_rstgen_reset_driver = {
.name = "starfive_rstgen",
.probe = starfive_rstgen_probe,
.of_compatible = starfive_rstgen_reset_dt_ids,
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index db87aee1f3..98e58da89b 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -40,6 +40,7 @@ config RTC_DRV_ABRACON
config RTC_DRV_PCF85363
tristate "NXP PCF85363"
depends on I2C
+ select REGMAP_I2C
help
If you say yes here you get support for the PCF85363 RTC chip.
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 12751c8a3c..3edb294ed7 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -51,7 +51,7 @@ EXPORT_SYMBOL(rtc_set_time);
int rtc_register(struct rtc_device *rtcdev)
{
- struct device_d *dev = &rtcdev->class_dev;
+ struct device *dev = &rtcdev->class_dev;
if (!rtcdev->ops)
return -EINVAL;
diff --git a/drivers/rtc/rtc-abracon.c b/drivers/rtc/rtc-abracon.c
index 585b95c4a7..d43b1b4021 100644
--- a/drivers/rtc/rtc-abracon.c
+++ b/drivers/rtc/rtc-abracon.c
@@ -80,7 +80,7 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
.set_time = abracon_set_time,
};
-static int abracon_probe(struct device_d *dev)
+static int abracon_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct abracon *abracon;
@@ -100,7 +100,7 @@ static struct platform_device_id abracon_id[] = {
{ }
};
-static struct driver_d abracon_driver = {
+static struct driver abracon_driver = {
.name = "rtc-abracon",
.probe = abracon_probe,
.id_table = abracon_id,
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 314d794f11..e1a8e214d2 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -193,7 +193,7 @@ static inline struct ds1307 *to_ds1307_priv(struct rtc_device *rtcdev)
static int ds1307_get_time(struct rtc_device *rtcdev, struct rtc_time *t)
{
- struct device_d *dev = rtcdev->dev;
+ struct device *dev = rtcdev->dev;
struct ds1307 *ds1307 = to_ds1307_priv(rtcdev);
int tmp;
@@ -231,7 +231,7 @@ static int ds1307_get_time(struct rtc_device *rtcdev, struct rtc_time *t)
static int ds1307_set_time(struct rtc_device *rtcdev, struct rtc_time *t)
{
- struct device_d *dev = rtcdev->dev;
+ struct device *dev = rtcdev->dev;
struct ds1307 *ds1307 = to_ds1307_priv(rtcdev);
int result;
int tmp;
@@ -280,7 +280,7 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
.set_time = ds1307_set_time,
};
-static int ds1307_probe(struct device_d *dev)
+static int ds1307_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds1307 *ds1307;
@@ -288,7 +288,7 @@ static int ds1307_probe(struct device_d *dev)
int tmp;
unsigned char *buf;
unsigned long driver_data;
- const struct device_node *np = dev->device_node;
+ const struct device_node *np = dev->of_node;
ds1307 = xzalloc(sizeof(struct ds1307));
@@ -481,7 +481,7 @@ exit:
return err;
}
-static struct driver_d ds1307_driver = {
+static struct driver ds1307_driver = {
.name = "rtc-ds1307",
.probe = ds1307_probe,
.id_table = ds1307_id,
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
index 297fdc17b9..e9d68c6739 100644
--- a/drivers/rtc/rtc-imxdi.c
+++ b/drivers/rtc/rtc-imxdi.c
@@ -102,7 +102,7 @@
* @dsr: copy of the DSR register
*/
struct imxdi_dev {
- struct device_d *dev;
+ struct device *dev;
struct rtc_device rtc;
void __iomem *ioaddr;
struct clk *clk;
@@ -529,20 +529,16 @@ static int nvstore_read(void *ctx, unsigned reg, void *val, size_t bytes)
return 0;
}
-static struct nvmem_bus nvstore_nvmem_bus = {
- .write = nvstore_write,
- .read = nvstore_read,
-};
-
static struct nvmem_config nvstore_nvmem_config = {
.name = "nvstore",
.stride = 4,
.word_size = 4,
.size = 4,
- .bus = &nvstore_nvmem_bus,
+ .reg_write = nvstore_write,
+ .reg_read = nvstore_read,
};
-static int __init dryice_rtc_probe(struct device_d *dev)
+static int __init dryice_rtc_probe(struct device *dev)
{
struct resource *res;
struct imxdi_dev *imxdi;
@@ -603,8 +599,9 @@ static __maybe_unused const struct of_device_id dryice_dt_ids[] = {
{ .compatible = "fsl,imx25-rtc" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dryice_dt_ids);
-static struct driver_d dryice_rtc_driver = {
+static struct driver dryice_rtc_driver = {
.name = "imx-di-rtc",
.probe = dryice_rtc_probe,
.of_compatible = DRV_OF_COMPAT(dryice_dt_ids),
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 52f52349e6..aa79cb2ac5 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -106,7 +106,7 @@ static struct rtc_class_ops jz4740_rtc_ops = {
.set_time = jz4740_rtc_set_time,
};
-static int jz4740_rtc_probe(struct device_d *dev)
+static int jz4740_rtc_probe(struct device *dev)
{
struct resource *iores;
int ret;
@@ -153,8 +153,9 @@ static __maybe_unused struct of_device_id jz4740_rtc_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, jz4740_rtc_dt_ids);
-static struct driver_d jz4740_rtc_driver = {
+static struct driver jz4740_rtc_driver = {
.name = "jz4740-rtc",
.probe = jz4740_rtc_probe,
.of_compatible = DRV_OF_COMPAT(jz4740_rtc_dt_ids),
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index b9f0fc6012..bcc251e138 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -16,7 +16,7 @@
#include <malloc.h>
#include <errno.h>
#include <i2c/i2c.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <rtc.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
@@ -55,7 +55,7 @@ static inline struct pcf85363 *to_pcf85363_priv(struct rtc_device *rtcdev)
static int pcf85363_rtc_read_time(struct rtc_device *rtcdev,
struct rtc_time *tm)
{
- struct device_d *dev = rtcdev->dev;
+ struct device *dev = rtcdev->dev;
struct pcf85363 *pcf85363 = to_pcf85363_priv(rtcdev);
unsigned char buf[DT_YEARS + 1];
int ret, len = sizeof(buf);
@@ -126,7 +126,7 @@ static const struct regmap_config pcf85363_regmap_i2c_config = {
.max_register = 0x7f,
};
-static int pcf85363_probe(struct device_d *dev)
+static int pcf85363_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct pcf85363 *pcf85363;
@@ -154,7 +154,7 @@ static struct platform_device_id dev_ids[] = {
{ /* sentinel */ }
};
-static struct driver_d pcf85363_driver = {
+static struct driver pcf85363_driver = {
.name = "pcf85363",
.probe = pcf85363_probe,
.id_table = dev_ids,
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 6fda32d44c..60b0e5f1dc 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -31,7 +31,7 @@ config DRIVER_SERIAL_AR933X
config DRIVER_SERIAL_EFI
bool "EFI serial"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
config DRIVER_SERIAL_IMX
depends on ARCH_IMX
@@ -58,7 +58,7 @@ config DRIVER_SERIAL_LINUX_CONSOLE
bool "linux console driver"
config DRIVER_SERIAL_EFI_STDIO
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
bool "EFI stdio driver"
config DRIVER_SERIAL_MPC5XXX
@@ -71,16 +71,6 @@ config DRIVER_SERIAL_CLPS711X
default y
bool "CLPS711X serial driver"
-config DRIVER_SERIAL_ALTERA
- depends on NIOS2
- default y
- bool "Altera serial driver"
-
-config DRIVER_SERIAL_ALTERA_JTAG
- depends on NIOS2
- default n
- bool "Altera JTAG serial driver"
-
config DRIVER_SERIAL_NS16550
default n
bool "NS16550 serial driver"
@@ -107,24 +97,6 @@ config DRIVER_SERIAL_PL010
help
Enable this to get support for AMBA PL010 based serial devices
-config DRIVER_SERIAL_S3C_IMPROVED
- bool
-
-config DRIVER_SERIAL_S3C
- bool "Samsung S3C serial driver"
- depends on ARCH_SAMSUNG
- select DRIVER_SERIAL_S3C_IMPROVED if (CPU_S5PC110 || CPU_S5PV210 || CPU_S3C6410)
- default y
- help
- Say Y here if you want to use the CONS on a Samsung S3C CPU
-
-config DRIVER_SERIAL_S3C_AUTOSYNC
- bool "Enable auto flow"
- depends on DRIVER_SERIAL_S3C
- help
- Say Y here if you want to use the auto flow feature of this
- UART. RTS and CTS will be handled by the hardware when enabled.
-
config DRIVER_SERIAL_PXA
bool "PXA serial driver"
depends on ARCH_PXA
@@ -150,6 +122,10 @@ config DRIVER_SERIAL_LPUART
default y
bool "LPUART serial driver"
+config DRIVER_SERIAL_LPUART32
+ depends on ARCH_IMX
+ bool "LPUART32 serial driver"
+
config VIRTIO_CONSOLE
tristate "Virtio console"
depends on VIRTIO
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 4b2bfb0537..4887e24ee1 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -12,10 +12,7 @@ obj-$(CONFIG_DRIVER_SERIAL_CLPS711X) += serial_clps711x.o
obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o
obj-$(CONFIG_DRIVER_SERIAL_NS16550_PCI) += serial_ns16550_pci.o
obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o
-obj-$(CONFIG_DRIVER_SERIAL_S3C) += serial_s3c.o
obj-$(CONFIG_DRIVER_SERIAL_STM32) += serial_stm32.o
-obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o
-obj-$(CONFIG_DRIVER_SERIAL_ALTERA_JTAG) += serial_altera_jtag.o
obj-$(CONFIG_DRIVER_SERIAL_PXA) += serial_pxa.o
obj-$(CONFIG_DRIVER_SERIAL_OMAP4_USBBOOT) += serial_omap4_usbboot.o
obj-$(CONFIG_DRIVER_SERIAL_AUART) += serial_auart.o
@@ -23,6 +20,7 @@ obj-$(CONFIG_DRIVER_SERIAL_CADENCE) += serial_cadence.o
obj-$(CONFIG_DRIVER_SERIAL_EFI_STDIO) += efi-stdio.o
obj-$(CONFIG_DRIVER_SERIAL_DIGIC) += serial_digic.o
obj-$(CONFIG_DRIVER_SERIAL_LPUART) += serial_lpuart.o
+obj-$(CONFIG_DRIVER_SERIAL_LPUART32) += serial_lpuart32.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_SERIAL_SIFIVE) += serial_sifive.o
obj-$(CONFIG_SERIAL_SBI) += serial_sbi.o
diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c
index db0ee7fe09..1e2b4e425c 100644
--- a/drivers/serial/arm_dcc.c
+++ b/drivers/serial/arm_dcc.c
@@ -108,7 +108,7 @@ static int arm_dcc_tstc(struct console_device *cdev)
static struct console_device arm_dcc_dev;
-static int arm_dcc_probe(struct device_d *dev)
+static int arm_dcc_probe(struct device *dev)
{
struct console_device *cdev;
@@ -125,13 +125,13 @@ static int arm_dcc_probe(struct device_d *dev)
return 0;
}
-static struct driver_d arm_dcc_driver = {
+static struct driver arm_dcc_driver = {
.name = "arm_dcc",
.probe = arm_dcc_probe,
};
console_platform_driver(arm_dcc_driver);
-static struct device_d arm_dcc_device = {
+static struct device arm_dcc_device = {
.id = DEVICE_ID_DYNAMIC,
.name = "arm_dcc",
};
diff --git a/drivers/serial/atmel.c b/drivers/serial/atmel.c
index 9e20754998..b957b75284 100644
--- a/drivers/serial/atmel.c
+++ b/drivers/serial/atmel.c
@@ -384,7 +384,7 @@ static int atmel_serial_set_mode(struct console_device *cdev, enum console_mode
*/
static int atmel_serial_init_port(struct console_device *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct atmel_uart_port *uart = to_atmel_uart_port(cdev);
uart->base = dev_request_mem_region_err_null(dev, 0);
@@ -412,7 +412,7 @@ static int atmel_serial_init_port(struct console_device *cdev)
return 0;
}
-static int atmel_serial_probe(struct device_d *dev)
+static int atmel_serial_probe(struct device *dev)
{
struct atmel_uart_port *uart;
struct console_device *cdev;
@@ -447,8 +447,9 @@ static const struct of_device_id __maybe_unused atmel_serial_dt_ids[] = {
{ .compatible = "atmel,at91sam9260-usart" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids);
-static struct driver_d atmel_serial_driver = {
+static struct driver atmel_serial_driver = {
.name = "atmel_usart",
.probe = atmel_serial_probe,
.of_compatible = DRV_OF_COMPAT(atmel_serial_dt_ids),
diff --git a/drivers/serial/efi-stdio.c b/drivers/serial/efi-stdio.c
index 2e2aa57227..92133f8378 100644
--- a/drivers/serial/efi-stdio.c
+++ b/drivers/serial/efi-stdio.c
@@ -20,7 +20,7 @@
struct efi_console_priv {
struct efi_simple_text_output_protocol *out;
- struct efi_simple_input_interface *in;
+ struct efi_simple_text_input_protocol *in;
struct efi_simple_text_input_ex_protocol *inex;
struct console_device cdev;
u16 efi_console_buffer[CONFIG_CBSIZE + 1];
@@ -79,17 +79,23 @@ static int xlate_keypress(struct efi_input_key *k)
return k->unicode_char & 0xff;
}
+static void efi_wait_single_event(struct efi_event *event)
+{
+ size_t index;
+
+ /* wait until key is pressed */
+ BS->wait_for_event(1, &event, &index);
+}
+
static int efi_read_key(struct efi_console_priv *priv, bool wait)
{
- unsigned long index;
efi_status_t efiret;
struct efi_key_data kd;
- /* wait until key is pressed */
- if (wait)
- BS->wait_for_event(1, priv->in->wait_for_key, &index);
-
if (priv->inex) {
+ if (wait)
+ efi_wait_single_event(priv->inex->wait_for_key_ex);
+
efiret = priv->inex->read_key_stroke_ex(priv->inex, &kd);
if (efiret == EFI_NOT_READY)
@@ -118,6 +124,9 @@ static int efi_read_key(struct efi_console_priv *priv, bool wait)
}
}
+ if (wait)
+ efi_wait_single_event(priv->in->wait_for_key);
+
efiret = priv->in->read_key_stroke(priv->in, &kd.key);
if (EFI_ERROR(efiret))
@@ -412,7 +421,7 @@ static void efi_set_mode(struct efi_console_priv *priv)
priv->mode_names, n, priv);
}
-static int efi_console_probe(struct device_d *dev)
+static int efi_console_probe(struct device *dev)
{
efi_guid_t inex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
struct efi_simple_text_input_ex_protocol *inex;
@@ -430,7 +439,7 @@ static int efi_console_probe(struct device_d *dev)
if (!priv->inputbuffer)
return -ENOMEM;
- efiret = BS->open_protocol((void *)efi_sys_table->con_in_handle,
+ efiret = BS->open_protocol(efi_sys_table->con_in_handle,
&inex_guid,
(void **)&inex,
efi_parent_image,
@@ -467,7 +476,7 @@ static int efi_console_probe(struct device_d *dev)
return 0;
}
-static struct driver_d efi_console_driver = {
+static struct driver efi_console_driver = {
.name = "efi-stdio",
.probe = efi_console_probe,
};
diff --git a/drivers/serial/linux_console.c b/drivers/serial/linux_console.c
index a8e9c52125..b8303dfd12 100644
--- a/drivers/serial/linux_console.c
+++ b/drivers/serial/linux_console.c
@@ -15,7 +15,7 @@
static void linux_console_putc(struct console_device *cdev, char c)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct linux_console_data *d = dev->platform_data;
linux_write(d->stdoutfd, &c, 1);
@@ -23,7 +23,7 @@ static void linux_console_putc(struct console_device *cdev, char c)
static int linux_console_tstc(struct console_device *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct linux_console_data *d = dev->platform_data;
return linux_tstc(d->stdinfd);
@@ -31,7 +31,7 @@ static int linux_console_tstc(struct console_device *cdev)
static int linux_console_getc(struct console_device *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct linux_console_data *d = dev->platform_data;
static char old_c;
char c;
@@ -45,7 +45,7 @@ static int linux_console_getc(struct console_device *cdev)
return c;
}
-static int linux_console_probe(struct device_d *dev)
+static int linux_console_probe(struct device *dev)
{
struct console_device *cdev;
struct linux_console_data *data = dev->platform_data;
@@ -67,7 +67,7 @@ static int linux_console_probe(struct device_d *dev)
return 0;
}
-static struct driver_d linux_console_driver = {
+static struct driver linux_console_driver = {
.name = "console",
.probe = linux_console_probe,
};
diff --git a/drivers/serial/serial_altera.c b/drivers/serial/serial_altera.c
deleted file mode 100644
index 55f4443862..0000000000
--- a/drivers/serial/serial_altera.c
+++ /dev/null
@@ -1,94 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * (C) Copyright 2011, Franck JULLIEN, <elec4fun@gmail.com>
- */
-
-#include <common.h>
-#include <driver.h>
-#include <init.h>
-#include <malloc.h>
-#include <io.h>
-#include <asm/nios2-io.h>
-
-struct altera_serial_priv {
- struct console_device cdev;
- void __iomem *regs;
-};
-
-static int altera_serial_setbaudrate(struct console_device *cdev, int baudrate)
-{
- struct altera_serial_priv *priv = container_of(cdev,
- struct altera_serial_priv, cdev);
-
- struct nios_uart *uart = priv->regs;
- uint16_t div;
-
- div = (CPU_FREQ / baudrate) - 1;
- writew(div, &uart->divisor);
-
- return 0;
-}
-
-static void altera_serial_putc(struct console_device *cdev, char c)
-{
- struct altera_serial_priv *priv = container_of(cdev,
- struct altera_serial_priv, cdev);
-
- struct nios_uart *uart = priv->regs;
-
- while ((readw(&uart->status) & NIOS_UART_TRDY) == 0);
-
- writew(c, &uart->txdata);
-}
-
-static int altera_serial_tstc(struct console_device *cdev)
-{
- struct altera_serial_priv *priv = container_of(cdev,
- struct altera_serial_priv, cdev);
-
- struct nios_uart *uart = priv->regs;
-
- return readw(&uart->status) & NIOS_UART_RRDY;
-}
-
-static int altera_serial_getc(struct console_device *cdev)
-{
- struct altera_serial_priv *priv = container_of(cdev,
- struct altera_serial_priv, cdev);
-
- struct nios_uart *uart = priv->regs;
-
- while (altera_serial_tstc(cdev) == 0);
-
- return readw(&uart->rxdata) & 0x000000FF;
-}
-
-static int altera_serial_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct console_device *cdev;
- struct altera_serial_priv *priv;
-
- priv = xzalloc(sizeof(*priv));
- cdev = &priv->cdev;
-
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->regs = IOMEM(iores->start);
- cdev->dev = dev;
- cdev->tstc = altera_serial_tstc;
- cdev->putc = altera_serial_putc;
- cdev->getc = altera_serial_getc;
- cdev->setbrg = altera_serial_setbaudrate;
-
- console_register(cdev);
-
- return 0;
-}
-
-static struct driver_d altera_serial_driver = {
- .name = "altera_serial",
- .probe = altera_serial_probe,
-};
-console_platform_driver(altera_serial_driver);
diff --git a/drivers/serial/serial_altera_jtag.c b/drivers/serial/serial_altera_jtag.c
deleted file mode 100644
index b6720545ce..0000000000
--- a/drivers/serial/serial_altera_jtag.c
+++ /dev/null
@@ -1,99 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * (C) Copyright 2004, Psyent Corporation <www.psyent.com>
- * Scott McNutt <smcnutt@psyent.com>
- *
- * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com>
- */
-
-#include <common.h>
-#include <driver.h>
-#include <init.h>
-#include <malloc.h>
-#include <io.h>
-#include <asm/nios2-io.h>
-
-struct altera_serial_jtag_priv {
- struct console_device cdev;
- void __iomem *regs;
-};
-
-
-static int altera_serial_jtag_setbaudrate(struct console_device *cdev, int baudrate)
-{
- return 0;
-}
-
-static void altera_serial_jtag_putc(struct console_device *cdev, char c)
-{
- struct altera_serial_jtag_priv *priv = container_of(cdev,
- struct altera_serial_jtag_priv, cdev);
-
- struct nios_jtag *jtag = priv->regs;
- uint32_t st;
-
- while (1) {
- st = readl(&jtag->control);
- if (NIOS_JTAG_WSPACE(st))
- break;
- }
-
- writel(c, &jtag->data);
-}
-
-static int altera_serial_jtag_tstc(struct console_device *cdev)
-{
- struct altera_serial_jtag_priv *priv = container_of(cdev,
- struct altera_serial_jtag_priv, cdev);
-
- struct nios_jtag *jtag = priv->regs;
-
- return readl(&jtag->control) & NIOS_JTAG_RRDY;
-}
-
-static int altera_serial_jtag_getc(struct console_device *cdev)
-{
- struct altera_serial_jtag_priv *priv = container_of(cdev,
- struct altera_serial_jtag_priv, cdev);
-
- struct nios_jtag *jtag = priv->regs;
- uint32_t val;
-
- while (1) {
- val = readl(&jtag->data);
- if (val & NIOS_JTAG_RVALID)
- break;
- }
-
- return val & 0xFF;
-}
-
-static int altera_serial_jtag_probe(struct device_d *dev) {
- struct resource *iores;
-
- struct console_device *cdev;
- struct altera_serial_jtag_priv *priv;
-
- priv = xzalloc(sizeof(*priv));
- cdev = &priv->cdev;
-
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->regs = IOMEM(iores->start);
- cdev->dev = dev;
- cdev->tstc = altera_serial_jtag_tstc;
- cdev->putc = altera_serial_jtag_putc;
- cdev->getc = altera_serial_jtag_getc;
- cdev->setbrg = altera_serial_jtag_setbaudrate;
-
- console_register(cdev);
-
- return 0;
-}
-
-static struct driver_d altera_serial_jtag_driver = {
- .name = "altera_serial_jtag",
- .probe = altera_serial_jtag_probe,
-};
-console_platform_driver(altera_serial_jtag_driver);
diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c
index 29e1151db3..f5595ec1ee 100644
--- a/drivers/serial/serial_ar933x.c
+++ b/drivers/serial/serial_ar933x.c
@@ -141,7 +141,7 @@ static int ar933x_serial_getc(struct console_device *cdev)
return rdata & AR933X_UART_DATA_TX_RX_MASK;
}
-static int ar933x_serial_probe(struct device_d *dev)
+static int ar933x_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -188,8 +188,9 @@ static struct of_device_id ar933x_serial_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, ar933x_serial_dt_ids);
-static struct driver_d ar933x_serial_driver = {
+static struct driver ar933x_serial_driver = {
.name = "ar933x_serial",
.probe = ar933x_serial_probe,
.of_compatible = DRV_OF_COMPAT(ar933x_serial_dt_ids),
diff --git a/drivers/serial/serial_auart.c b/drivers/serial/serial_auart.c
index d8e31fae30..217ac5c891 100644
--- a/drivers/serial/serial_auart.c
+++ b/drivers/serial/serial_auart.c
@@ -36,8 +36,6 @@
#include <linux/clk.h>
#include <linux/err.h>
-#include <mach/clock.h>
-
#define HW_UARTAPP_CTRL0 (0x00000000)
#define HW_UARTAPP_CTRL2 (0x00000020)
@@ -168,7 +166,7 @@ static void auart_serial_init_port(struct auart_priv *priv)
writel(0x0, priv->base + HW_UARTAPP_INTR);
}
-static int auart_serial_probe(struct device_d *dev)
+static int auart_serial_probe(struct device *dev)
{
struct resource *iores;
struct auart_priv *priv;
@@ -218,8 +216,9 @@ static const __maybe_unused struct of_device_id auart_serial_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, auart_serial_dt_ids);
-static struct driver_d auart_serial_driver = {
+static struct driver auart_serial_driver = {
.name = "auart_serial",
.probe = auart_serial_probe,
.of_compatible = DRV_OF_COMPAT(auart_serial_dt_ids),
diff --git a/drivers/serial/serial_cadence.c b/drivers/serial/serial_cadence.c
index 84dcd1b76b..ee162e541a 100644
--- a/drivers/serial/serial_cadence.c
+++ b/drivers/serial/serial_cadence.c
@@ -173,7 +173,7 @@ static int cadence_clocksource_clock_change(struct notifier_block *nb,
return 0;
}
-static int cadence_serial_probe(struct device_d *dev)
+static int cadence_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -240,6 +240,7 @@ static __maybe_unused struct of_device_id cadence_serial_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, cadence_serial_dt_ids);
static struct platform_device_id cadence_serial_ids[] = {
{
@@ -250,7 +251,7 @@ static struct platform_device_id cadence_serial_ids[] = {
},
};
-static struct driver_d cadence_serial_driver = {
+static struct driver cadence_serial_driver = {
.name = "cadence_serial",
.probe = cadence_serial_probe,
.of_compatible = DRV_OF_COMPAT(cadence_serial_dt_ids),
diff --git a/drivers/serial/serial_clps711x.c b/drivers/serial/serial_clps711x.c
index 81eb85429e..2a284909bf 100644
--- a/drivers/serial/serial_clps711x.c
+++ b/drivers/serial/serial_clps711x.c
@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <mfd/syscon.h>
+#include <linux/regmap.h>
#define UARTDR 0x00
# define UARTDR_FRMERR (1 << 8)
@@ -127,7 +128,7 @@ static void clps711x_flush(struct console_device *cdev)
} while (tmp & SYSFLG_UBUSY);
}
-static int clps711x_probe(struct device_d *dev)
+static int clps711x_probe(struct device *dev)
{
struct device_node *syscon;
struct clps711x_uart *s;
@@ -148,7 +149,7 @@ static int clps711x_probe(struct device_d *dev)
goto out_err;
}
- syscon = of_parse_phandle(dev->device_node, "syscon", 0);
+ syscon = of_parse_phandle(dev->of_node, "syscon", 0);
s->regmap = syscon_node_to_regmap(syscon);
if (IS_ERR(s->regmap)) {
err = PTR_ERR(s->regmap);
@@ -164,7 +165,7 @@ static int clps711x_probe(struct device_d *dev)
s->cdev.setbrg = clps711x_setbaudrate;
s->cdev.linux_console_name = "ttyCL";
- devname = of_alias_get(dev->device_node);
+ devname = of_alias_get(dev->of_node);
if (devname) {
s->cdev.devname = xstrdup(devname);
s->cdev.devid = DEVICE_ID_SINGLE;
@@ -185,8 +186,9 @@ static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {
{ .compatible = "cirrus,ep7209-uart", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids);
-static struct driver_d clps711x_driver = {
+static struct driver clps711x_driver = {
.name = "clps711x-uart",
.probe = clps711x_probe,
.of_compatible = DRV_OF_COMPAT(clps711x_uart_dt_ids),
diff --git a/drivers/serial/serial_digic.c b/drivers/serial/serial_digic.c
index 74c9c9c7e2..48e9cd3248 100644
--- a/drivers/serial/serial_digic.c
+++ b/drivers/serial/serial_digic.c
@@ -10,7 +10,7 @@
#include <malloc.h>
#include <io.h>
-#include <mach/uart.h>
+#include <mach/digic/uart.h>
/*
* This driver is based on the "Serial terminal" docs here:
@@ -89,7 +89,7 @@ static int digic_serial_tstc(struct console_device *cdev)
*/
}
-static int digic_serial_probe(struct device_d *dev)
+static int digic_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -117,8 +117,9 @@ static __maybe_unused struct of_device_id digic_serial_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, digic_serial_dt_ids);
-static struct driver_d digic_serial_driver = {
+static struct driver digic_serial_driver = {
.name = "digic-uart",
.probe = digic_serial_probe,
.of_compatible = DRV_OF_COMPAT(digic_serial_dt_ids),
diff --git a/drivers/serial/serial_imx.c b/drivers/serial/serial_imx.c
index cf9c3f02a7..0f91028605 100644
--- a/drivers/serial/serial_imx.c
+++ b/drivers/serial/serial_imx.c
@@ -197,7 +197,7 @@ static int imx_clocksource_clock_change(struct notifier_block *nb,
return 0;
}
-static int imx_serial_probe(struct device_d *dev)
+static int imx_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -236,19 +236,19 @@ static int imx_serial_probe(struct device_d *dev)
cdev->linux_console_name = "ttymxc";
cdev->linux_earlycon_name = "ec_imx6q";
cdev->phys_base = priv->regs;
- if (dev->device_node) {
- devname = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ devname = of_alias_get(dev->of_node);
if (devname) {
cdev->devname = xstrdup(devname);
cdev->devid = DEVICE_ID_SINGLE;
}
}
- if (of_property_read_bool(dev->device_node, "fsl,dte-mode"))
+ if (of_property_read_bool(dev->of_node, "fsl,dte-mode"))
priv->dte_mode = 1;
- if (of_property_read_bool(dev->device_node, "linux,rs485-enabled-at-boot-time") &&
- !of_property_read_bool(dev->device_node, "rs485-rts-active-low"))
+ if (of_property_read_bool(dev->of_node, "linux,rs485-enabled-at-boot-time") &&
+ !of_property_read_bool(dev->of_node, "rs485-rts-active-low"))
priv->rs485_mode = 1;
imx_serial_init_port(cdev);
@@ -299,6 +299,7 @@ static __maybe_unused struct of_device_id imx_serial_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_serial_dt_ids);
static struct platform_device_id imx_serial_ids[] = {
{
@@ -312,7 +313,7 @@ static struct platform_device_id imx_serial_ids[] = {
},
};
-static struct driver_d imx_serial_driver = {
+static struct driver imx_serial_driver = {
.name = "imx_serial",
.probe = imx_serial_probe,
.of_compatible = DRV_OF_COMPAT(imx_serial_dt_ids),
diff --git a/drivers/serial/serial_litex.c b/drivers/serial/serial_litex.c
index 554fee71a3..04da556f6a 100644
--- a/drivers/serial/serial_litex.c
+++ b/drivers/serial/serial_litex.c
@@ -58,7 +58,7 @@ static int litex_serial_tstc(struct console_device *cdev)
return !litex_serial_readb(cdev, UART_RXEMPTY);
}
-static int litex_serial_probe(struct device_d *dev)
+static int litex_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -90,8 +90,9 @@ static __maybe_unused struct of_device_id litex_serial_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, litex_serial_dt_ids);
-static struct driver_d litex_serial_driver = {
+static struct driver litex_serial_driver = {
.name = "litex-uart",
.probe = litex_serial_probe,
.of_compatible = DRV_OF_COMPAT(litex_serial_dt_ids),
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 720018c9ac..828a0dd0bb 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -113,7 +113,7 @@ static int lpuart_clocksource_clock_change(struct notifier_block *nb,
return lpuart_serial_setbaudrate(&lpuart->cdev, lpuart->baudrate);
}
-static int lpuart_serial_probe(struct device_d *dev)
+static int lpuart_serial_probe(struct device *dev)
{
int ret;
struct console_device *cdev;
@@ -151,8 +151,8 @@ static int lpuart_serial_probe(struct device_d *dev)
cdev->flush = lpuart_serial_flush;
cdev->setbrg = lpuart_serial_setbaudrate;
- if (dev->device_node) {
- devname = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ devname = of_alias_get(dev->of_node);
if (devname) {
cdev->devname = xstrdup(devname);
cdev->devid = DEVICE_ID_SINGLE;
@@ -186,8 +186,9 @@ static struct of_device_id lpuart_serial_dt_ids[] = {
{ .compatible = "fsl,vf610-lpuart" },
{}
};
+MODULE_DEVICE_TABLE(of, lpuart_serial_dt_ids);
-static struct driver_d lpuart_serial_driver = {
+static struct driver lpuart_serial_driver = {
.name = "lpuart-serial",
.probe = lpuart_serial_probe,
.of_compatible = DRV_OF_COMPAT(lpuart_serial_dt_ids),
diff --git a/drivers/serial/serial_lpuart32.c b/drivers/serial/serial_lpuart32.c
new file mode 100644
index 0000000000..09d4b620be
--- /dev/null
+++ b/drivers/serial/serial_lpuart32.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Pengutronix
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <malloc.h>
+#include <notifier.h>
+#include <io.h>
+#include <of.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <serial/lpuart32.h>
+
+struct lpuart32_devtype_data {
+ unsigned int reg_offs;
+};
+
+struct lpuart32 {
+ struct console_device cdev;
+ int baudrate;
+ int dte_mode;
+ struct notifier_block notify;
+ struct resource *io;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static struct lpuart32 *cdev_to_lpuart32(struct console_device *cdev)
+{
+ return container_of(cdev, struct lpuart32, cdev);
+}
+
+static void lpuart32_enable(struct lpuart32 *lpuart32)
+{
+ writel(LPUART32_UARTCTRL_TE | LPUART32_UARTCTRL_RE,
+ lpuart32->base + LPUART32_UARTCTRL);
+}
+
+static void lpuart32_disable(struct lpuart32 *lpuart32)
+{
+ writel(0, lpuart32->base + LPUART32_UARTCTRL);
+}
+
+/* Test whether a character is in the RX buffer */
+static int lpuart32_serial_tstc(struct console_device *cdev)
+{
+ struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+ if (readl(lpuart32->base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_OR)
+ writel(LPUART32_UARTSTAT_OR, lpuart32->base + LPUART32_UARTSTAT);
+
+ return readl(lpuart32->base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_RDRF;
+}
+
+static int lpuart32_serial_setbaudrate(struct console_device *cdev,
+ int baudrate)
+{
+ struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+ lpuart32_disable(lpuart32);
+
+ /*
+ * We treat baudrate of 0 as a request to disable UART
+ */
+ if (baudrate) {
+ lpuart32_setbrg(lpuart32->base, clk_get_rate(lpuart32->clk),
+ baudrate);
+ lpuart32_enable(lpuart32);
+ }
+
+ lpuart32->baudrate = baudrate;
+
+ return 0;
+}
+
+static int lpuart32_serial_getc(struct console_device *cdev)
+{
+ struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+ while (!lpuart32_serial_tstc(cdev));
+
+ return readl(lpuart32->base + LPUART32_UARTDATA) & 0xff;
+}
+
+static void lpuart32_serial_putc(struct console_device *cdev, char c)
+{
+ struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev);
+
+ lpuart32_putc(lpuart32->base, c);
+}
+
+static void lpuart32_serial_flush(struct console_device *cdev)
+{
+}
+
+static int lpuart32_serial_probe(struct device *dev)
+{
+ int ret;
+ struct console_device *cdev;
+ struct lpuart32 *lpuart32;
+ const char *devname;
+ struct lpuart32_devtype_data *devtype;
+
+ ret = dev_get_drvdata(dev, (const void **)&devtype);
+ if (ret)
+ return ret;
+
+ lpuart32 = xzalloc(sizeof(*lpuart32));
+ cdev = &lpuart32->cdev;
+ dev->priv = lpuart32;
+
+ lpuart32->io = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(lpuart32->io)) {
+ ret = PTR_ERR(lpuart32->io);
+ goto err_free;
+ }
+ lpuart32->base = IOMEM(lpuart32->io->start) + devtype->reg_offs;
+
+ lpuart32->clk = clk_get(dev, NULL);
+ if (IS_ERR(lpuart32->clk)) {
+ ret = PTR_ERR(lpuart32->clk);
+ dev_err(dev, "Failed to get UART clock %d\n", ret);
+ goto io_release;
+ }
+
+ ret = clk_enable(lpuart32->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable UART clock %d\n", ret);
+ goto io_release;
+ }
+
+ cdev->dev = dev;
+ cdev->tstc = lpuart32_serial_tstc;
+ cdev->putc = lpuart32_serial_putc;
+ cdev->getc = lpuart32_serial_getc;
+ cdev->flush = lpuart32_serial_flush;
+ cdev->setbrg = lpuart32_serial_setbaudrate;
+
+ if (dev->of_node) {
+ devname = of_alias_get(dev->of_node);
+ if (devname) {
+ cdev->devname = xstrdup(devname);
+ cdev->devid = DEVICE_ID_SINGLE;
+ }
+ }
+
+ cdev->linux_console_name = "ttyLP";
+ cdev->linux_earlycon_name = "lpuart";
+ cdev->phys_base = lpuart32->base;
+
+ lpuart32_setup(lpuart32->base, clk_get_rate(lpuart32->clk));
+
+ ret = console_register(cdev);
+ if (!ret)
+ return 0;
+
+ clk_put(lpuart32->clk);
+io_release:
+ release_region(lpuart32->io);
+err_free:
+ free(lpuart32);
+
+ return ret;
+}
+
+static struct lpuart32_devtype_data imx7ulp_data = {
+ .reg_offs = 0x10,
+};
+
+static struct of_device_id lpuart32_serial_dt_ids[] = {
+ {
+ .compatible = "fsl,imx7ulp-lpuart",
+ .data = &imx7ulp_data,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, lpuart32_serial_dt_ids);
+
+static struct driver lpuart32_serial_driver = {
+ .name = "lpuart32-serial",
+ .probe = lpuart32_serial_probe,
+ .of_compatible = DRV_OF_COMPAT(lpuart32_serial_dt_ids),
+};
+console_platform_driver(lpuart32_serial_driver);
diff --git a/drivers/serial/serial_mpc5xxx.c b/drivers/serial/serial_mpc5xxx.c
index 38248dfd82..4408de9e91 100644
--- a/drivers/serial/serial_mpc5xxx.c
+++ b/drivers/serial/serial_mpc5xxx.c
@@ -46,7 +46,7 @@ static int __mpc5xxx_serial_setbaudrate(struct mpc5xxx_psc *psc, int baudrate)
static int mpc5xxx_serial_setbaudrate(struct console_device *cdev, int baudrate)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct mpc5xxx_psc *psc = dev->priv;
__mpc5xxx_serial_setbaudrate(psc, baudrate);
@@ -90,7 +90,7 @@ static int __mpc5xxx_serial_init(struct mpc5xxx_psc *psc)
static int mpc5xxx_serial_init(struct console_device *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct mpc5xxx_psc *psc = dev->priv;
__mpc5xxx_serial_init(psc);
@@ -100,7 +100,7 @@ static int mpc5xxx_serial_init(struct console_device *cdev)
static void mpc5xxx_serial_putc (struct console_device *cdev, const char c)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct mpc5xxx_psc *psc = dev->priv;
/* Wait for last character to go. */
@@ -112,7 +112,7 @@ static void mpc5xxx_serial_putc (struct console_device *cdev, const char c)
static int mpc5xxx_serial_getc (struct console_device *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct mpc5xxx_psc *psc = dev->priv;
/* Wait for a character to arrive. */
@@ -124,13 +124,13 @@ static int mpc5xxx_serial_getc (struct console_device *cdev)
static int mpc5xxx_serial_tstc (struct console_device *cdev)
{
- struct device_d *dev = cdev->dev;
+ struct device *dev = cdev->dev;
struct mpc5xxx_psc *psc = dev->priv;
return (psc->psc_status & PSC_SR_RXRDY);
}
-static int mpc5xxx_serial_probe(struct device_d *dev)
+static int mpc5xxx_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -153,7 +153,7 @@ static int mpc5xxx_serial_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mpc5xxx_serial_driver = {
+static struct driver mpc5xxx_serial_driver = {
.name = "mpc5xxx_serial",
.probe = mpc5xxx_serial_probe,
};
diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c
index e5cbc3624e..1b1692658f 100644
--- a/drivers/serial/serial_ns16550.c
+++ b/drivers/serial/serial_ns16550.c
@@ -40,6 +40,10 @@ struct ns16550_priv {
void (*write_reg)(struct ns16550_priv *, uint8_t val, unsigned offset);
uint8_t (*read_reg)(struct ns16550_priv *, unsigned offset);
const char *access_type;
+
+ bool rs485_mode;
+ bool rs485_rts_active_low;
+ bool rs485_rx_during_tx;
};
struct ns16550_drvdata {
@@ -265,9 +269,37 @@ static void rpi_init_port(struct console_device *cdev)
*/
static void ns16550_putc(struct console_device *cdev, char c)
{
- /* Loop Doing Nothing */
- while ((ns16550_read(cdev, lsr) & LSR_THRE) == 0) ;
+ struct ns16550_priv *priv = to_ns16550_priv(cdev);
+
+ /* wait until FIFO can accept at least one byte */
+ while ((ns16550_read(cdev, lsr) & (LSR_THRE)) != (LSR_THRE))
+ ;
+
+ if (priv->rs485_mode) {
+ if (priv->rs485_rts_active_low)
+ ns16550_write(cdev, MCR_RTS, mcr);
+ else
+ ns16550_write(cdev, 0, mcr);
+
+ if (!priv->rs485_rx_during_tx)
+ ns16550_write(cdev, CNTL_TXEN, cntl);
+ }
+
ns16550_write(cdev, c, thr);
+
+ if (priv->rs485_mode) {
+ /* wait until FIFO is cleared*/
+ while ((ns16550_read(cdev, lsr) & (LSR_EMPTY)) != (LSR_EMPTY))
+ ;
+
+ if (priv->rs485_rts_active_low)
+ ns16550_write(cdev, 0, mcr);
+ else
+ ns16550_write(cdev, MCR_RTS, mcr);
+
+ if (!priv->rs485_rx_during_tx)
+ ns16550_write(cdev, CNTL_TXEN | CNTL_RXEN, cntl);
+ }
}
/**
@@ -307,7 +339,7 @@ static void ns16550_flush(struct console_device *cdev)
while ((ns16550_read(cdev, lsr) & LSR_TEMT) == 0) ;
}
-static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv)
+static void ns16550_probe_dt(struct device *dev, struct ns16550_priv *priv)
{
struct device_node *np = dev_of_node(dev);
u32 offset;
@@ -321,6 +353,12 @@ static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv)
priv->mmiobase += offset;
of_property_read_u32(np, "reg-shift", &priv->plat.shift);
of_property_read_u32(np, "reg-io-width", &width);
+ priv->rs485_rts_active_low =
+ of_property_read_bool(np, "rs485-rts-active-low");
+ priv->rs485_mode =
+ of_property_read_bool(np, "linux,rs485-enabled-at-boot-time");
+ priv->rs485_rx_during_tx =
+ of_property_read_bool(np, "rs485-rx-during-tx");
switch (width) {
case 1:
@@ -385,23 +423,51 @@ static __maybe_unused struct ns16550_drvdata rpi_drvdata = {
.linux_earlycon_name = "bcm2835aux",
};
-static int ns16550_init_iomem(struct device_d *dev, struct ns16550_priv *priv)
+/**
+ * @return the requested resource to be properly released in case probe fail
+ */
+static struct resource *ns16550_init_iores(struct device *dev, struct ns16550_priv *priv)
{
- struct resource *iores;
struct resource *res;
- int width;
+ struct resource *iores;
+ unsigned long flags;
res = dev_get_resource(dev, IORESOURCE_MEM, 0);
if (IS_ERR(res))
- return PTR_ERR(res);
+ res = dev_get_resource(dev, IORESOURCE_IO, 0);
+ if (IS_ERR(res))
+ return res;
- iores = dev_request_mem_resource(dev, 0);
+ flags = res->flags & (IORESOURCE_MEM_TYPE_MASK | IORESOURCE_IO);
+
+ if (flags & IORESOURCE_IO)
+ iores = request_ioport_region(dev_name(dev), res->start, res->end);
+ else
+ iores = request_iomem_region(dev_name(dev), res->start, res->end);
if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->mmiobase = IOMEM(iores->start);
+ return iores;
- width = res->flags & IORESOURCE_MEM_TYPE_MASK;
- switch (width) {
+ if (flags & IORESOURCE_IO)
+ priv->iobase = iores->start;
+ else
+ priv->mmiobase = IOMEM(iores->start);
+
+ switch (flags) {
+ case IORESOURCE_IO | IORESOURCE_MEM_8BIT:
+ priv->read_reg = ns16550_read_reg_ioport_8;
+ priv->write_reg = ns16550_write_reg_ioport_8;
+ priv->access_type = "io";
+ break;
+ case IORESOURCE_IO | IORESOURCE_MEM_16BIT:
+ priv->read_reg = ns16550_read_reg_ioport_16;
+ priv->write_reg = ns16550_write_reg_ioport_16;
+ priv->access_type = "io";
+ break;
+ case IORESOURCE_IO | IORESOURCE_MEM_32BIT:
+ priv->read_reg = ns16550_read_reg_ioport_32;
+ priv->write_reg = ns16550_write_reg_ioport_32;
+ priv->access_type = "io";
+ break;
case IORESOURCE_MEM_8BIT:
priv->read_reg = ns16550_read_reg_mmio_8;
priv->write_reg = ns16550_write_reg_mmio_8;
@@ -419,43 +485,7 @@ static int ns16550_init_iomem(struct device_d *dev, struct ns16550_priv *priv)
break;
}
- return 0;
-}
-
-static int ns16550_init_ioport(struct device_d *dev, struct ns16550_priv *priv)
-{
- struct resource *res;
- int width;
-
- res = dev_get_resource(dev, IORESOURCE_IO, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
-
- res = request_ioport_region(dev_name(dev), res->start, res->end);
- if (IS_ERR(res))
- return PTR_ERR(res);
-
- priv->iobase = res->start;
-
- width = res->flags & IORESOURCE_MEM_TYPE_MASK;
- switch (width) {
- case IORESOURCE_MEM_8BIT:
- priv->read_reg = ns16550_read_reg_ioport_8;
- priv->write_reg = ns16550_write_reg_ioport_8;
- break;
- case IORESOURCE_MEM_16BIT:
- priv->read_reg = ns16550_read_reg_ioport_16;
- priv->write_reg = ns16550_write_reg_ioport_16;
- break;
- case IORESOURCE_MEM_32BIT:
- priv->read_reg = ns16550_read_reg_ioport_32;
- priv->write_reg = ns16550_write_reg_ioport_32;
- break;
- }
-
- priv->access_type = "io";
-
- return 0;
+ return iores;
}
/**
@@ -467,24 +497,24 @@ static int ns16550_init_ioport(struct device_d *dev, struct ns16550_priv *priv)
* ENOMEM if calloc failed
* else return result of console_register
*/
-static int ns16550_probe(struct device_d *dev)
+static int ns16550_probe(struct device *dev)
{
struct ns16550_priv *priv;
struct console_device *cdev;
struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data;
const struct ns16550_drvdata *devtype;
+ struct resource *iores;
int ret;
devtype = device_get_match_data(dev) ?: &ns16550_drvdata;
priv = xzalloc(sizeof(*priv));
- ret = ns16550_init_iomem(dev, priv);
- if (ret)
- ret = ns16550_init_ioport(dev, priv);
-
- if (ret)
- return ret;
+ iores = ns16550_init_iores(dev, priv);
+ if (IS_ERR(iores)) {
+ ret = PTR_ERR(iores);
+ goto err;
+ }
if (plat)
priv->plat = *plat;
@@ -499,16 +529,18 @@ static int ns16550_probe(struct device_d *dev)
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
dev_err(dev, "failed to get clk (%d)\n", ret);
- goto err;
+ goto release_region;
}
- clk_enable(priv->clk);
+ ret = clk_enable(priv->clk);
+ if (ret)
+ goto clk_put;
priv->plat.clock = clk_get_rate(priv->clk);
}
if (priv->plat.clock == 0) {
dev_err(dev, "no valid clockrate\n");
ret = -EINVAL;
- goto err;
+ goto clk_disable;
}
cdev = &priv->cdev;
@@ -528,8 +560,18 @@ static int ns16550_probe(struct device_d *dev)
devtype->init_port(cdev);
- return console_register(cdev);
+ ret = console_register(cdev);
+ if (ret)
+ goto clk_disable;
+
+ return 0;
+clk_disable:
+ clk_disable(priv->clk);
+clk_put:
+ clk_put(priv->clk);
+release_region:
+ release_region(iores);
err:
free(priv);
@@ -549,6 +591,12 @@ static struct of_device_id ns16550_serial_dt_ids[] = {
}, {
.compatible = "nvidia,tegra20-uart",
},
+#if IS_ENABLED(CONFIG_ARCH_K3)
+ {
+ .compatible = "ti,am654-uart",
+ .data = &omap_clk48m_drvdata,
+ },
+#endif
#if IS_ENABLED(CONFIG_ARCH_OMAP)
{
.compatible = "ti,omap2-uart",
@@ -580,6 +628,7 @@ static struct of_device_id ns16550_serial_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, ns16550_serial_dt_ids);
static __maybe_unused struct platform_device_id ns16550_serial_ids[] = {
{
@@ -593,7 +642,7 @@ static __maybe_unused struct platform_device_id ns16550_serial_ids[] = {
/**
* @brief Driver registration structure
*/
-static struct driver_d ns16550_serial_driver = {
+static struct driver ns16550_serial_driver = {
.name = "ns16550_serial",
.probe = ns16550_probe,
.of_compatible = DRV_OF_COMPAT(ns16550_serial_dt_ids),
diff --git a/drivers/serial/serial_ns16550.h b/drivers/serial/serial_ns16550.h
index 56c639a134..2d941cb8d1 100644
--- a/drivers/serial/serial_ns16550.h
+++ b/drivers/serial/serial_ns16550.h
@@ -36,6 +36,7 @@
#define lsr 5
#define msr 6
#define scr 7
+#define cntl 8
#define thr rbr
#define iir fcr
@@ -73,6 +74,12 @@
#define LSR_TEMT 0x40 /* Xmitter empty */
#define LSR_ERR 0x80 /* Error */
+/* Transmitter FIFO completely empty */
+#define LSR_EMPTY (LSR_THRE | LSR_TEMT)
+
+#define CNTL_RXEN 0x01
+#define CNTL_TXEN 0x02
+
/* useful defaults for LCR */
#define LCR_8N1 0x03
diff --git a/drivers/serial/serial_ns16550_pci.c b/drivers/serial/serial_ns16550_pci.c
index 0d7b686fef..f82b5797ce 100644
--- a/drivers/serial/serial_ns16550_pci.c
+++ b/drivers/serial/serial_ns16550_pci.c
@@ -3656,7 +3656,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
uart.pdata->clock = board->base_baud * 16;
for (i = 0; i < nr_ports; i++) {
- struct device_d *ns16550_dev;
+ struct device *ns16550_dev;
struct resource *res;
rc = quirk->setup(priv, board, &uart, i);
diff --git a/drivers/serial/serial_omap4_usbboot.c b/drivers/serial/serial_omap4_usbboot.c
index d0d01d585f..6592be4f35 100644
--- a/drivers/serial/serial_omap4_usbboot.c
+++ b/drivers/serial/serial_omap4_usbboot.c
@@ -4,7 +4,7 @@
#include <init.h>
#include <malloc.h>
#include <errno.h>
-#include <mach/omap4_rom_usb.h>
+#include <mach/omap/omap4_rom_usb.h>
struct serial_omap4_usbboot_priv {
struct console_device cdev;
@@ -46,9 +46,15 @@ static int serial_omap4_usbboot_getc(struct console_device *cdev)
return priv->val;
}
-static int serial_omap4_usbboot_probe(struct device_d *dev)
+static int serial_omap4_usbboot_probe(struct device *dev)
{
struct serial_omap4_usbboot_priv *priv;
+ int ret;
+
+ ret = omap4_usbboot_open();
+ if (ret)
+ return ret;
+
priv = xzalloc(sizeof(*priv));
priv->cdev.dev = dev;
@@ -60,7 +66,7 @@ static int serial_omap4_usbboot_probe(struct device_d *dev)
return console_register(&priv->cdev);
}
-static struct driver_d serial_omap4_usbboot_driver = {
+static struct driver serial_omap4_usbboot_driver = {
.name = "serial_omap4_usbboot",
.probe = serial_omap4_usbboot_probe,
};
diff --git a/drivers/serial/serial_pl010.c b/drivers/serial/serial_pl010.c
index f2cf944e8f..56b9e67610 100644
--- a/drivers/serial/serial_pl010.c
+++ b/drivers/serial/serial_pl010.c
@@ -115,7 +115,7 @@ static int pl010_tstc(struct console_device *cdev)
return !(readl(&pl010->flag) & UART_PL010_FR_RXFE);
}
-static int pl010_probe(struct device_d *dev)
+static int pl010_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -138,7 +138,7 @@ static int pl010_probe(struct device_d *dev)
return 0;
}
-static struct driver_d pl010_driver = {
+static struct driver pl010_driver = {
.name = "pl010_serial",
.probe = pl010_probe,
};
diff --git a/drivers/serial/serial_pxa.c b/drivers/serial/serial_pxa.c
index c9f612dbeb..97342f923e 100644
--- a/drivers/serial/serial_pxa.c
+++ b/drivers/serial/serial_pxa.c
@@ -9,7 +9,7 @@
#include <init.h>
#include <malloc.h>
-#include <mach/clock.h>
+#include <mach/pxa/clock.h>
#include <asm/io.h>
#define RBR 0x00 /* Receive Buffer Register (read only) */
@@ -149,7 +149,7 @@ static int pxa_serial_setbaudrate(struct console_device *cdev, int baudrate)
return 0;
}
-static int pxa_serial_probe(struct device_d *dev)
+static int pxa_serial_probe(struct device *dev)
{
struct resource *iores;
struct console_device *cdev;
@@ -175,7 +175,7 @@ static int pxa_serial_probe(struct device_d *dev)
return 0;
}
-static struct driver_d pxa_serial_driver = {
+static struct driver pxa_serial_driver = {
.name = "pxa_serial",
.probe = pxa_serial_probe,
};
diff --git a/drivers/serial/serial_s3c.c b/drivers/serial/serial_s3c.c
deleted file mode 100644
index 4080079d42..0000000000
--- a/drivers/serial/serial_s3c.c
+++ /dev/null
@@ -1,198 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * (c) 2009...2011 Juergen Beisert <j.beisert@pengutronix.de>
- *
- * Based on code from:
- * (c) 2004 Sascha Hauer <sascha@saschahauer.de>
- */
-
-#include <common.h>
-#include <driver.h>
-#include <init.h>
-#include <malloc.h>
-#include <io.h>
-#include <mach/s3c-generic.h>
-#include <mach/s3c-iomap.h>
-
-/* Note: Offsets are for little endian access */
-#define ULCON 0x00 /* line control */
-#define UCON 0x04 /* UART control */
-# define UCON_SET_CLK_SRC(x) (((x) & 0x03) << 10)
-# define UCON_GET_CLK_SRC(x) (((x) >> 10) & 0x03)
-#define UFCON 0x08 /* FIFO control */
-#define UMCON 0x0c /* modem control */
-#define UTRSTAT 0x10 /* Rx/Tx status */
-#define UERSTAT 0x14 /* error status */
-#define UFSTAT 0x18 /* FIFO status */
-#define UMSTAT 0x1c /* modem status */
-#define UTXH 0x20 /* transmitt */
-#define URXH 0x24 /* receive */
-#define UBRDIV 0x28 /* baudrate generator */
-#define UBRDIVSLOT 0x2c /* baudrate slot generator */
-#define UINTM 0x38 /* interrupt mask register */
-
-struct s3c_uart {
- void __iomem *regs;
- struct console_device cdev;
-};
-
-#define to_s3c_uart(c) container_of(c, struct s3c_uart, cdev)
-
-/* each architecture has a preferred reference clock for its UARTs */
-static unsigned s3c_select_arch_input_clock(void)
-{
- /* S3C24xx: 0=2=PCLK, 1=UEXTCLK, 3=FCLK/n */
- if (IS_ENABLED(CONFIG_ARCH_S3C24xx))
- return 0; /* use the internal PCLK */
- /* S3C64xx: 0=2=PCLK, 1=UCLK0, 3=UCLK1 */
- if (IS_ENABLED(CONFIG_ARCH_S3C64xx))
- return 3; /* use the internal UCLK1 */
- /* S5PCxx: 0=PCLK, 1=SCLK_UART */
- if (IS_ENABLED(CONFIG_ARCH_S5PCxx))
- return 0; /* use the internal PCLK */
-}
-
-static unsigned s3c_get_arch_uart_input_clock(void __iomem *base)
-{
- unsigned reg = readw(base + UCON);
- return s3c_get_uart_clk(UCON_GET_CLK_SRC(reg));
-}
-
-/*
- * This table takes the fractional value of the baud divisor and gives
- * the recommended setting for the UDIVSLOT register. Refer the datasheet
- * for further details
- */
-static const uint16_t udivslot_table[] __maybe_unused = {
- 0x0000, 0x0080, 0x0808, 0x0888, 0x2222, 0x4924, 0x4A52, 0x54AA,
- 0x5555, 0xD555, 0xD5D5, 0xDDD5, 0xDDDD, 0xDFDD, 0xDFDF, 0xFFDF,
-};
-
-static int s3c_serial_setbaudrate(struct console_device *cdev, int baudrate)
-{
- struct s3c_uart *priv = to_s3c_uart(cdev);
- void __iomem *base = priv->regs;
- unsigned val;
-
- if (IS_ENABLED(CONFIG_DRIVER_SERIAL_S3C_IMPROVED)) {
- val = s3c_get_arch_uart_input_clock(base) / baudrate;
- writew(udivslot_table[val & 15], base + UBRDIVSLOT);
- }
-
- val = s3c_get_arch_uart_input_clock(base) / (16 * baudrate) - 1;
- writew(val, base + UBRDIV);
-
- return 0;
-}
-
-static int s3c_serial_init_port(struct console_device *cdev)
-{
- struct s3c_uart *priv = to_s3c_uart(cdev);
- void __iomem *base = priv->regs;
-
- /* FIFO enable, Tx/Rx FIFO clear */
- writeb(0x07, base + UFCON);
- writeb(0x00, base + UMCON);
-
- /* Normal,No parity,1 stop,8 bit */
- writeb(0x03, base + ULCON);
-
- /*
- * S3C2440 SoC:
- * - no clock divider
- * all SoCs:
- * - enable receive and transmit mode
- */
- writew(0x0005 | UCON_SET_CLK_SRC(s3c_select_arch_input_clock()),
- base + UCON);
-
- if (IS_ENABLED(CONFIG_DRIVER_SERIAL_S3C_IMPROVED))
- /* 'interrupt or polling mode' for both directions */
- writeb(0xf, base + UINTM);
-
- if (IS_ENABLED(CONFIG_DRIVER_SERIAL_S3C_AUTOSYNC))
- writeb(0x10, base + UMCON); /* enable auto flow control */
- else
- writeb(0x01, base + UMCON); /* RTS up */
-
- return 0;
-}
-
-static void s3c_serial_putc(struct console_device *cdev, char c)
-{
- struct s3c_uart *priv = to_s3c_uart(cdev);
- void __iomem *base = priv->regs;
-
- /* Wait for Tx FIFO not full */
- while (!(readb(base + UTRSTAT) & 0x2))
- ;
-
- writeb(c, base + UTXH);
-}
-
-static int s3c_serial_tstc(struct console_device *cdev)
-{
- struct s3c_uart *priv = to_s3c_uart(cdev);
- void __iomem *base = priv->regs;
-
- /* If receive fifo is empty, return false */
- if (readb(base + UTRSTAT) & 0x1)
- return 1;
-
- return 0;
-}
-
-static int s3c_serial_getc(struct console_device *cdev)
-{
- struct s3c_uart *priv = to_s3c_uart(cdev);
- void __iomem *base = priv->regs;
-
- /* wait for a character */
- while (!(readb(base + UTRSTAT) & 0x1))
- ;
-
- return readb(base + URXH);
-}
-
-static void s3c_serial_flush(struct console_device *cdev)
-{
- struct s3c_uart *priv = to_s3c_uart(cdev);
- void __iomem *base = priv->regs;
-
- while (!(readb(base + UTRSTAT) & 0x4))
- ;
-}
-
-static int s3c_serial_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct s3c_uart *priv;
- struct console_device *cdev;
-
- priv = xzalloc(sizeof(struct s3c_uart));
- cdev = &priv->cdev;
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- priv->regs = IOMEM(iores->start);
- dev->priv = priv;
- cdev->dev = dev;
- cdev->tstc = s3c_serial_tstc;
- cdev->putc = s3c_serial_putc;
- cdev->getc = s3c_serial_getc;
- cdev->flush = s3c_serial_flush;
- cdev->setbrg = s3c_serial_setbaudrate;
-
- s3c_serial_init_port(cdev);
-
- /* Enable UART */
- console_register(cdev);
-
- return 0;
-}
-
-static struct driver_d s3c_serial_driver = {
- .name = "s3c_serial",
- .probe = s3c_serial_probe,
-};
-console_platform_driver(s3c_serial_driver);
diff --git a/drivers/serial/serial_sbi.c b/drivers/serial/serial_sbi.c
index 343fcda4dc..dd3eef41d1 100644
--- a/drivers/serial/serial_sbi.c
+++ b/drivers/serial/serial_sbi.c
@@ -36,7 +36,7 @@ static int sbi_serial_tstc(struct console_device *cdev)
return priv->head != priv->tail;
}
-static int sbi_serial_probe(struct device_d *dev)
+static int sbi_serial_probe(struct device *dev)
{
struct sbi_serial_priv *priv;
@@ -51,7 +51,7 @@ static int sbi_serial_probe(struct device_d *dev)
return console_register(&priv->cdev);
}
-static struct driver_d serial_sbi_driver = {
+static struct driver serial_sbi_driver = {
.name = "riscv-serial-sbi",
.probe = sbi_serial_probe,
};
diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c
index 931269f2e3..f056233b4e 100644
--- a/drivers/serial/serial_sifive.c
+++ b/drivers/serial/serial_sifive.c
@@ -116,7 +116,7 @@ static void sifive_serial_flush(struct console_device *cdev)
;
}
-static int sifive_serial_probe(struct device_d *dev)
+static int sifive_serial_probe(struct device *dev)
{
struct sifive_serial_priv *priv;
struct resource *iores;
@@ -130,7 +130,8 @@ static int sifive_serial_probe(struct device_d *dev)
} else {
dev_dbg(dev, "failed to get clock. Fallback to device tree.\n");
- ret = of_property_read_u32(dev->device_node, "clock-frequency", &freq);
+ ret = of_property_read_u32(dev->of_node, "clock-frequency",
+ &freq);
if (ret) {
dev_warn(dev, "unknown clock frequency\n");
return ret;
@@ -162,8 +163,9 @@ static __maybe_unused struct of_device_id sifive_serial_dt_ids[] = {
{ .compatible = "sifive,uart0" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, sifive_serial_dt_ids);
-static struct driver_d serial_sifive_driver = {
+static struct driver serial_sifive_driver = {
.name = "serial_sifive",
.probe = sifive_serial_probe,
.of_compatible = sifive_serial_dt_ids,
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index f1d4c6de90..3e18a2c152 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -138,7 +138,7 @@ static void stm32_serial_init(struct console_device *cdev)
writel(cr1, base + CR1_OFFSET(stm32f4));
}
-static int stm32_serial_probe(struct device_d *dev)
+static int stm32_serial_probe(struct device *dev)
{
int ret;
struct console_device *cdev;
@@ -165,7 +165,7 @@ static int stm32_serial_probe(struct device_d *dev)
stm32->stm32f4 = info->stm32f4;
stm32->uart_enable_bit = info->uart_enable_bit;
- stm32->clk = clk_get(dev, NULL);
+ stm32->clk = clk_get_for_console(dev, NULL);
if (IS_ERR(stm32->clk)) {
ret = PTR_ERR(stm32->clk);
dev_err(dev, "Failed to get UART clock %d\n", ret);
@@ -183,11 +183,11 @@ static int stm32_serial_probe(struct device_d *dev)
cdev->putc = stm32_serial_putc;
cdev->getc = stm32_serial_getc;
cdev->flush = stm32_serial_flush;
- cdev->setbrg = stm32_serial_setbaudrate;
+ cdev->setbrg = stm32->clk ? stm32_serial_setbaudrate : NULL;
cdev->linux_console_name = "ttySTM";
- if (dev->device_node) {
- devname = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ devname = of_alias_get(dev->of_node);
if (devname) {
cdev->devname = xstrdup(devname);
cdev->devid = DEVICE_ID_SINGLE;
@@ -240,8 +240,9 @@ static struct of_device_id stm32_serial_dt_ids[] = {
}, {
}
};
+MODULE_DEVICE_TABLE(of, stm32_serial_dt_ids);
-static struct driver_d stm32_serial_driver = {
+static struct driver stm32_serial_driver = {
.name = "stm32-serial",
.probe = stm32_serial_probe,
.of_compatible = DRV_OF_COMPAT(stm32_serial_dt_ids),
diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c
index b4b2c4cc8f..af30bfa71d 100644
--- a/drivers/serial/stm-serial.c
+++ b/drivers/serial/stm-serial.c
@@ -20,7 +20,6 @@
#include <malloc.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <mach/clock.h>
#define UARTDBGDR 0x00
#define UARTDBGFR 0x18
@@ -132,7 +131,7 @@ static int stm_serial_init_port(struct stm_priv *priv)
return 0;
}
-static int stm_serial_probe(struct device_d *dev)
+static int stm_serial_probe(struct device *dev)
{
struct resource *iores;
struct stm_priv *priv;
@@ -181,8 +180,9 @@ static __maybe_unused struct of_device_id stm_serial_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, stm_serial_dt_ids);
-static struct driver_d stm_serial_driver = {
+static struct driver stm_serial_driver = {
.name = "stm_serial",
.probe = stm_serial_probe,
.of_compatible = DRV_OF_COMPAT(stm_serial_dt_ids),
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 54b69cc42e..c0fe214429 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -2,5 +2,6 @@ menu "SoC drivers"
source "drivers/soc/imx/Kconfig"
source "drivers/soc/kvx/Kconfig"
+source "drivers/soc/rockchip/Kconfig"
endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index a23e81ffb3..9bff737b78 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_ARCH_IMX) += imx/
obj-$(CONFIG_KVX) += kvx/
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_CPU_SIFIVE) += sifive/
obj-$(CONFIG_SOC_STARFIVE) += starfive/
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
index 06513c02da..333a134d3f 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -1,16 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "i.MX SoC drivers"
-config IMX_GPCV2_PM_DOMAINS
- bool "i.MX GPCv2 PM domains"
- depends on ARCH_IMX7 || ARCH_IMX8M
- select PM_GENERIC_DOMAINS
- default y if ARCH_IMX7 || ARCH_IMX8M
-
config IMX8M_FEATCTRL
- bool "i.MX8M feature controller"
+ bool "i.MX8M feature controller"
depends on ARCH_IMX8M
select FEATURE_CONTROLLER
+ select IMX_OCOTP
+ select NVMEM
default y
+ help
+ This driver disables device tree nodes that are not applicable
+ to Lite variants of i.MX8M SoCs.
endmenu
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index bd1717b038..65b2677f7a 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
obj-$(CONFIG_IMX8M_FEATCTRL) += imx8m-featctrl.o
+obj-$(CONFIG_ARCH_IMX8M) += soc-imx8m.o
diff --git a/drivers/soc/imx/imx8m-featctrl.c b/drivers/soc/imx/imx8m-featctrl.c
index 480c80e6c1..31579aff7e 100644
--- a/drivers/soc/imx/imx8m-featctrl.c
+++ b/drivers/soc/imx/imx8m-featctrl.c
@@ -28,7 +28,25 @@ static int imx8m_feat_check(struct feature_controller *feat, int idx)
return test_bit(idx, priv->features) ? FEATCTRL_OKAY : FEATCTRL_GATED;
}
-int imx8m_feat_ctrl_init(struct device_d *dev, u32 tester4,
+static inline bool is_fused(u32 val, u32 bitmask)
+{
+ return bitmask && (val & bitmask) == bitmask;
+}
+
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+
+static void check_cpus(u32 mask, u32 reg, unsigned long *features)
+{
+ switch (field_get(mask, reg)) {
+ case 0b11:
+ clear_bit(IMX8M_FEAT_CPU_DUAL, features);
+ fallthrough;
+ case 0b10:
+ clear_bit(IMX8M_FEAT_CPU_QUAD, features);
+ }
+}
+
+int imx8m_feat_ctrl_init(struct device *dev, u32 tester3, u32 tester4,
const struct imx8m_featctrl_data *data)
{
unsigned long *features;
@@ -37,25 +55,33 @@ int imx8m_feat_ctrl_init(struct device_d *dev, u32 tester4,
if (!dev || !data)
return -ENODEV;
- dev_dbg(dev, "tester4 = 0x%08x\n", tester4);
+ dev_dbg(dev, "tester3 = 0x%08x, tester4 = 0x%08x\n", tester3, tester4);
priv = xzalloc(sizeof(*priv));
features = priv->features;
bitmap_fill(features, IMX8M_FEAT_END);
- if (tester4 & data->vpu_bitmask)
+ if (is_fused(tester3, data->tester3.vpu_bitmask) ||
+ is_fused(tester4, data->tester4.vpu_bitmask))
clear_bit(IMX8M_FEAT_VPU, features);
- if (tester4 & data->gpu_bitmask)
+ if (is_fused(tester4, data->tester4.gpu_bitmask))
clear_bit(IMX8M_FEAT_GPU, features);
-
- switch (tester4 & 3) {
- case 0b11:
- clear_bit(IMX8M_FEAT_CPU_DUAL, features);
- fallthrough;
- case 0b10:
- clear_bit(IMX8M_FEAT_CPU_QUAD, features);
- }
+ if (is_fused(tester4, data->tester4.mipi_dsi_bitmask))
+ clear_bit(IMX8M_FEAT_MIPI_DSI, features);
+ if (is_fused(tester4, data->tester4.isp_bitmask))
+ clear_bit(IMX8M_FEAT_ISP, features);
+ if (is_fused(tester4, data->tester4.npu_bitmask))
+ clear_bit(IMX8M_FEAT_NPU, features);
+ if (is_fused(tester4, data->tester4.lvds_bitmask))
+ clear_bit(IMX8M_FEAT_LVDS, features);
+ if (is_fused(tester4, data->tester4.dsp_bitmask))
+ clear_bit(IMX8M_FEAT_DSP, features);
+
+ if (data->tester3.cpu_bitmask)
+ check_cpus(data->tester3.cpu_bitmask, tester3, features);
+ else if (data->tester4.cpu_bitmask)
+ check_cpus(data->tester4.cpu_bitmask, tester4, features);
priv->feat.dev = dev;
priv->feat.check = imx8m_feat_check;
diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c
new file mode 100644
index 0000000000..1b47c914de
--- /dev/null
+++ b/drivers/soc/imx/soc-imx8m.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: 2024 Marco Felsch, Pengutronix
+/*
+ * Based on Linux drivers/soc/imx/soc-imx8m.c:
+ * Copyright 2019 NXP.
+ */
+
+#include <init.h>
+#include <of.h>
+#include <of_address.h>
+#include <pm_domain.h>
+
+#include <asm/optee.h>
+#include <asm-generic/memory_layout.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+
+#include <mach/imx/generic.h>
+#include <mach/imx/imx8m-regs.h>
+#include <mach/imx/reset-reason.h>
+#include <mach/imx/revision.h>
+#include <mach/imx/scratch.h>
+#include <mach/imx/tzasc.h>
+
+#include <tee/optee.h>
+
+#define REV_B1 0x21
+
+#define IMX8MQ_SW_INFO_B1 0x40
+#define IMX8MQ_SW_MAGIC_B1 0xff0055aa
+
+#define IMX_SIP_GET_SOC_INFO 0xc2000006
+
+#define OCOTP_UID_LOW 0x410
+#define OCOTP_UID_HIGH 0x420
+
+#define IMX8MP_OCOTP_UID_OFFSET 0x10
+
+/* Same as ANADIG_DIGPROG_IMX7D */
+#define ANADIG_DIGPROG_IMX8MM 0x800
+
+struct imx8_soc_data {
+ char *name;
+ u32 (*soc_revision)(void);
+ void (*save_boot_loc)(void);
+};
+
+static u64 soc_uid;
+
+#ifdef CONFIG_HAVE_ARM_SMCCC
+static u32 imx8mq_soc_revision_from_atf(void)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
+
+ if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+ return 0;
+ else
+ return res.a0 & 0xff;
+}
+#else
+static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; };
+#endif
+
+static u32 __init imx8mq_soc_revision(void)
+{
+ struct device_node *np;
+ void __iomem *ocotp_base;
+ u32 magic;
+ u32 rev;
+ struct clk *clk;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
+ if (!np)
+ return 0;
+
+ ocotp_base = of_iomap(np, 0);
+ WARN_ON(!ocotp_base);
+ clk = of_clk_get_by_name(np, NULL);
+ if (IS_ERR(clk)) {
+ WARN_ON(IS_ERR(clk));
+ return 0;
+ }
+
+ clk_prepare_enable(clk);
+
+ /*
+ * SOC revision on older imx8mq is not available in fuses so query
+ * the value from ATF instead.
+ */
+ rev = imx8mq_soc_revision_from_atf();
+ if (!rev) {
+ magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
+ if (magic == IMX8MQ_SW_MAGIC_B1)
+ rev = REV_B1;
+ }
+
+ soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
+ soc_uid <<= 32;
+ soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
+
+ /* Keep the OCOTP clk on for the TF-A else the CPU stuck */
+ of_node_put(np);
+
+ return rev;
+}
+
+static void __init imx8mm_soc_uid(void)
+{
+ void __iomem *ocotp_base;
+ struct device_node *np;
+ struct clk *clk;
+ u32 offset = of_machine_is_compatible("fsl,imx8mp") ?
+ IMX8MP_OCOTP_UID_OFFSET : 0;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
+ if (!np)
+ return;
+
+ ocotp_base = of_iomap(np, 0);
+ WARN_ON(!ocotp_base);
+ clk = of_clk_get_by_name(np, NULL);
+ if (IS_ERR(clk)) {
+ WARN_ON(IS_ERR(clk));
+ return;
+ }
+
+ clk_prepare_enable(clk);
+
+ soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset);
+ soc_uid <<= 32;
+ soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset);
+
+ /* Keep the OCOTP clk on for the TF-A else the CPU stuck */
+ of_node_put(np);
+}
+
+static u32 __init imx8mm_soc_revision(void)
+{
+ struct device_node *np;
+ void __iomem *anatop_base;
+ u32 rev;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
+ if (!np)
+ return 0;
+
+ anatop_base = of_iomap(np, 0);
+ WARN_ON(!anatop_base);
+
+ rev = readl_relaxed(anatop_base + ANADIG_DIGPROG_IMX8MM);
+
+ of_node_put(np);
+
+ imx8mm_soc_uid();
+
+ return rev;
+}
+
+static const struct imx8_soc_data imx8mq_soc_data = {
+ .name = "i.MX8MQ",
+ .soc_revision = imx8mq_soc_revision,
+ .save_boot_loc = imx8mq_boot_save_loc,
+};
+
+static const struct imx8_soc_data imx8mm_soc_data = {
+ .name = "i.MX8MM",
+ .soc_revision = imx8mm_soc_revision,
+ .save_boot_loc = imx8mm_boot_save_loc,
+};
+
+static const struct imx8_soc_data imx8mn_soc_data = {
+ .name = "i.MX8MN",
+ .soc_revision = imx8mm_soc_revision,
+ .save_boot_loc = imx8mn_boot_save_loc,
+};
+
+static const struct imx8_soc_data imx8mp_soc_data = {
+ .name = "i.MX8MP",
+ .soc_revision = imx8mm_soc_revision,
+ .save_boot_loc = imx8mp_boot_save_loc,
+};
+
+static __maybe_unused const struct of_device_id imx8_soc_match[] = {
+ { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, },
+ { .compatible = "fsl,imx8mm", .data = &imx8mm_soc_data, },
+ { .compatible = "fsl,imx8mn", .data = &imx8mn_soc_data, },
+ { .compatible = "fsl,imx8mp", .data = &imx8mp_soc_data, },
+ { }
+};
+
+static int imx8_soc_imx8m_init(struct soc_device_attribute *soc_dev_attr)
+{
+ void __iomem *src = IOMEM(MX8M_SRC_BASE_ADDR);
+ const char *uid = soc_dev_attr->serial_number;
+ const char *cputypestr = soc_dev_attr->soc_id;
+
+ genpd_activate();
+
+ /*
+ * Reset reasons seem to be identical to that of i.MX7
+ */
+ imx_set_reset_reason(src + IMX7_SRC_SRSR, imx7_reset_reasons);
+ pr_info("%s unique ID: %s\n", cputypestr, uid);
+
+ if (IS_ENABLED(CONFIG_PBL_OPTEE) && imx8m_tzc380_is_enabled()) {
+ static struct of_optee_fixup_data optee_fixup_data = {
+ .shm_size = OPTEE_SHM_SIZE,
+ .method = "smc",
+ };
+
+ optee_set_membase(imx_scratch_get_optee_hdr());
+ of_optee_fixup(of_get_root_node(), &optee_fixup_data);
+ of_register_fixup(of_optee_fixup, &optee_fixup_data);
+ }
+
+ return 0;
+}
+
+#define imx8_revision(soc_rev) \
+ soc_rev ? \
+ xasprintf("%d.%d", (soc_rev >> 4) & 0xf, soc_rev & 0xf) : \
+ "unknown"
+
+static int __init imx8_soc_init(void)
+{
+ struct device_node *of_root = of_get_root_node();
+ struct soc_device_attribute *soc_dev_attr;
+ struct soc_device *soc_dev;
+ const struct of_device_id *id;
+ u32 soc_rev = 0;
+ const struct imx8_soc_data *data;
+ int ret;
+
+ id = of_match_node(imx8_soc_match, of_root);
+ if (!id)
+ return 0;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ soc_dev_attr->family = "Freescale i.MX";
+
+ ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
+ if (ret)
+ goto free_soc;
+
+ data = id->data;
+ if (data) {
+ soc_dev_attr->soc_id = data->name;
+ if (data->soc_revision)
+ soc_rev = data->soc_revision();
+ if (data->save_boot_loc)
+ data->save_boot_loc();
+ }
+
+ soc_dev_attr->revision = imx8_revision(soc_rev);
+ if (!soc_dev_attr->revision) {
+ ret = -ENOMEM;
+ goto free_soc;
+ }
+
+ soc_dev_attr->serial_number = xasprintf("%016llX", soc_uid);
+ if (!soc_dev_attr->serial_number) {
+ ret = -ENOMEM;
+ goto free_rev;
+ }
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ ret = PTR_ERR(soc_dev);
+ goto free_serial_number;
+ }
+
+ imx_set_silicon_revision(soc_dev_attr->soc_id, soc_rev);
+
+ return imx8_soc_imx8m_init(soc_dev_attr);
+
+free_serial_number:
+ kfree(soc_dev_attr->serial_number);
+free_rev:
+ if (strcmp(soc_dev_attr->revision, "unknown"))
+ kfree(soc_dev_attr->revision);
+free_soc:
+ kfree(soc_dev_attr);
+ return ret;
+}
+/* Aligned with imx_init() to not cause regressions */
+postcore_initcall(imx8_soc_init);
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/kvx/kvx_socinfo.c b/drivers/soc/kvx/kvx_socinfo.c
index 100faae766..87c20c327b 100644
--- a/drivers/soc/kvx/kvx_socinfo.c
+++ b/drivers/soc/kvx/kvx_socinfo.c
@@ -33,9 +33,11 @@
static char *kvx_mppa_id;
static char *kvx_arch_rev;
+static char *kvx_board_sn;
BAREBOX_MAGICVAR(kvx.arch_rev, "KVX architecture revision");
BAREBOX_MAGICVAR(kvx.mppa_id, "KVX MPPA chip id");
+BAREBOX_MAGICVAR(kvx.board_sn, "KVX board sn");
static void kvx_soc_info_read_revision(void)
{
@@ -81,7 +83,7 @@ static int base38_decode(char *s, u64 val, int nb_char)
return 0;
}
-static int kvx_read_serial(struct device_node *socinfo)
+static int kvx_read_mppa_id(struct device_node *socinfo)
{
char lot_id[LOT_ID_STR_LEN + 1] = "";
char com_ap;
@@ -102,6 +104,7 @@ static int kvx_read_serial(struct device_node *socinfo)
ews_val = (ews_val >> 32) | (ews_val << 32);
wafer_id = (ews_val >> EWS_WAFER_ID_SHIFT) & EWS_WAFER_ID_MASK;
base38_decode(lot_id, ews_val & EWS_LOT_ID_MASK, LOT_ID_STR_LEN);
+ free(cell_val64);
cell_val32 = (u32 *) nvmem_cell_get_and_read(socinfo, "ft_fuse", 4);
if (IS_ERR(cell_val32)) {
@@ -112,6 +115,7 @@ static int kvx_read_serial(struct device_node *socinfo)
ft_val = *cell_val32;
device_id = (ft_val >> FT_DEVICE_ID_SHIFT) & FT_DEVICE_ID_MASK;
base38_decode(&com_ap, (ft_val >> FT_COM_AP_SHIFT) & FT_COM_AP_MASK, 1);
+ free(cell_val32);
kvx_mppa_id = basprintf("%sA-%d%c-%03d", lot_id, wafer_id, com_ap,
device_id);
@@ -121,19 +125,49 @@ static int kvx_read_serial(struct device_node *socinfo)
return 0;
}
-static int kvx_socinfo_probe(struct device_d *dev)
+static int kvx_read_board_sn(struct device_node *socinfo)
+{
+ struct nvmem_cell *cell;
+ size_t len;
+ char *sn;
+
+ cell = of_nvmem_cell_get(socinfo, "board_sn");
+ if (IS_ERR(cell)) {
+ pr_debug("Fail to get board_sn cell\n");
+ return PTR_ERR(cell);
+ }
+
+ sn = (char *)nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+ if (IS_ERR(sn)) {
+ pr_debug("Fail to read board_sn\n");
+ return PTR_ERR(sn);
+ }
+
+ kvx_board_sn = xzalloc(len + 1);
+ memcpy(kvx_board_sn, sn, len);
+ globalvar_add_simple_string("kvx.board_sn", &kvx_board_sn);
+ free(sn);
+
+ return 0;
+}
+
+static int kvx_socinfo_probe(struct device *dev)
{
kvx_soc_info_read_revision();
- return kvx_read_serial(dev->device_node);
+ kvx_read_board_sn(dev->device_node);
+
+ return kvx_read_mppa_id(dev->device_node);
}
static const struct of_device_id kvx_socinfo_dt_ids[] = {
{ .compatible = "kalray,kvx-socinfo" },
{ }
};
+MODULE_DEVICE_TABLE(of, kvx_socinfo_dt_ids);
-static struct driver_d kvx_socinfo_driver = {
+static struct driver kvx_socinfo_driver = {
.name = "kvx-socinfo",
.probe = kvx_socinfo_probe,
.of_compatible = DRV_OF_COMPAT(kvx_socinfo_dt_ids),
diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig
new file mode 100644
index 0000000000..3484b37e22
--- /dev/null
+++ b/drivers/soc/rockchip/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+if ARCH_ROCKCHIP || COMPILE_TEST
+
+menu "Rockchip SoC drivers"
+
+config ROCKCHIP_IODOMAIN
+ tristate "Rockchip IO domain support"
+ depends on OFDEVICE
+ help
+ Say y here to enable support io domains on Rockchip SoCs. It is
+ necessary for the io domain setting of the SoC to match the
+ voltage supplied by the regulators.
+
+endmenu
+
+endif
diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile
new file mode 100644
index 0000000000..104fad968e
--- /dev/null
+++ b/drivers/soc/rockchip/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Rockchip Soc drivers
+#
+
+obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o
diff --git a/drivers/soc/rockchip/io-domain.c b/drivers/soc/rockchip/io-domain.c
new file mode 100644
index 0000000000..453d393cb0
--- /dev/null
+++ b/drivers/soc/rockchip/io-domain.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip IO Voltage Domain driver
+ *
+ * Copyright 2014 MundoReader S.L.
+ * Copyright 2014 Google, Inc.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <linux/err.h>
+#include <mfd/syscon.h>
+#include <linux/regmap.h>
+#include <regulator.h>
+
+#define MAX_SUPPLIES 16
+
+/*
+ * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under
+ * "Recommended Operating Conditions" for "Digital GPIO". When the typical
+ * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V.
+ *
+ * They are used like this:
+ * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the
+ * SoC we're at 3.3.
+ * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider
+ * that to be an error.
+ */
+#define MAX_VOLTAGE_1_8 1980000
+#define MAX_VOLTAGE_3_3 3600000
+
+#define RK3568_PMU_GRF_IO_VSEL0 (0x0140)
+#define RK3568_PMU_GRF_IO_VSEL1 (0x0144)
+#define RK3568_PMU_GRF_IO_VSEL2 (0x0148)
+
+struct rockchip_iodomain;
+
+struct rockchip_iodomain_supply {
+ struct rockchip_iodomain *iod;
+ struct regulator *reg;
+ int idx;
+};
+
+struct rockchip_iodomain_soc_data {
+ int grf_offset;
+ const char *supply_names[MAX_SUPPLIES];
+ void (*init)(struct rockchip_iodomain *iod);
+ int (*write)(struct rockchip_iodomain_supply *supply, int uV);
+};
+
+struct rockchip_iodomain {
+ struct device *dev;
+ struct regmap *grf;
+ const struct rockchip_iodomain_soc_data *soc_data;
+ struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
+ int (*write)(struct rockchip_iodomain_supply *supply, int uV);
+};
+
+static int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply,
+ int uV)
+{
+ struct rockchip_iodomain *iod = supply->iod;
+ u32 is_3v3 = uV > MAX_VOLTAGE_1_8;
+ u32 val0, val1;
+ int b;
+
+ dev_dbg(iod->dev, "set domain %d to %d uV\n", supply->idx, uV);
+
+ switch (supply->idx) {
+ case 0: /* pmuio1 */
+ break;
+ case 1: /* pmuio2 */
+ b = supply->idx;
+ val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
+ b = supply->idx + 4;
+ val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
+
+ regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);
+ regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);
+ break;
+ case 3: /* vccio2 */
+ break;
+ case 2: /* vccio1 */
+ case 4: /* vccio3 */
+ case 5: /* vccio4 */
+ case 6: /* vccio5 */
+ case 7: /* vccio6 */
+ case 8: /* vccio7 */
+ b = supply->idx - 1;
+ val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
+ val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
+
+ regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);
+ regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {
+ .grf_offset = 0x140,
+ .supply_names = {
+ "pmuio1",
+ "pmuio2",
+ "vccio1",
+ "vccio2",
+ "vccio3",
+ "vccio4",
+ "vccio5",
+ "vccio6",
+ "vccio7",
+ },
+ .write = rk3568_iodomain_write,
+};
+
+static const struct of_device_id rockchip_iodomain_match[] = {
+ { .compatible = "rockchip,rk3568-pmu-io-voltage-domain",
+ .data = &soc_data_rk3568_pmu },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_iodomain_match);
+
+static int rockchip_iodomain_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node, *parent;
+ struct rockchip_iodomain *iod;
+ int i, ret = 0;
+
+ if (!np)
+ return -ENODEV;
+
+ iod = xzalloc(sizeof(*iod));
+ iod->dev = dev;
+ iod->soc_data = device_get_match_data(dev);
+
+ if (iod->soc_data->write)
+ iod->write = iod->soc_data->write;
+
+ parent = of_get_parent(np);
+ if (parent) {
+ iod->grf = syscon_node_to_regmap(parent);
+ } else {
+ dev_dbg(dev, "falling back to old binding\n");
+ iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ }
+
+ if (IS_ERR(iod->grf)) {
+ dev_err(dev, "couldn't find grf regmap\n");
+ return PTR_ERR(iod->grf);
+ }
+
+ for (i = 0; i < MAX_SUPPLIES; i++) {
+ const char *supply_name = iod->soc_data->supply_names[i];
+ struct rockchip_iodomain_supply *supply = &iod->supplies[i];
+ struct regulator *reg;
+ int uV;
+
+ if (!supply_name)
+ continue;
+
+ reg = regulator_get_optional(dev, supply_name);
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+
+ /* If a supply wasn't specified, that's OK */
+ if (ret == -ENODEV)
+ continue;
+ dev_err_probe(dev, ret, "getting regulator\n");
+ goto out;
+ }
+
+ /* set initial correct value */
+ uV = regulator_get_voltage(reg);
+
+ /* must be a regulator we can get the voltage of */
+ if (uV < 0) {
+ dev_err(dev, "Can't determine voltage: %s\n",
+ supply_name);
+ ret = uV;
+ goto out;
+ }
+
+ if (uV > MAX_VOLTAGE_3_3) {
+ dev_crit(dev, "%d uV is too high. May damage SoC!\n",
+ uV);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* setup our supply */
+ supply->idx = i;
+ supply->iod = iod;
+ supply->reg = reg;
+
+ ret = iod->write(supply, uV);
+ if (ret) {
+ supply->reg = NULL;
+ goto out;
+ }
+ }
+
+ if (iod->soc_data->init)
+ iod->soc_data->init(iod);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static struct driver rockchip_iodomain_driver = {
+ .name = "rockchip-iodomain",
+ .probe = rockchip_iodomain_probe,
+ .of_compatible = rockchip_iodomain_match,
+};
+coredevice_platform_driver(rockchip_iodomain_driver);
diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c
index 74eab8df0a..c404143974 100644
--- a/drivers/soc/sifive/sifive_l2_cache.c
+++ b/drivers/soc/sifive/sifive_l2_cache.c
@@ -9,6 +9,7 @@
#define pr_fmt(fmt) "sifive-l2: " fmt
#include <io.h>
+#include <io-64-nonatomic-lo-hi.h>
#include <linux/printk.h>
#include <stdio.h>
#include <driver.h>
@@ -48,7 +49,7 @@
static void __iomem *l2_base = NULL;
-static void sifive_l2_config_read(struct device_d *dev)
+static void sifive_l2_config_read(struct device *dev)
{
u32 regval, val;
@@ -101,7 +102,7 @@ static void sifive_l2_enable_ways(void)
mb();
}
-static int sifive_l2_probe(struct device_d *dev)
+static int sifive_l2_probe(struct device *dev)
{
struct resource *iores;
@@ -127,8 +128,9 @@ static const struct of_device_id sifive_l2_ids[] = {
{ .compatible = "starfive,ccache0" },
{ /* end of table */ },
};
+MODULE_DEVICE_TABLE(of, sifive_l2_ids);
-static struct driver_d sifive_l2_driver = {
+static struct driver sifive_l2_driver = {
.name = "sfive-l2cache",
.probe = sifive_l2_probe,
.of_compatible = sifive_l2_ids,
diff --git a/drivers/soc/starfive/jh7100_dma.c b/drivers/soc/starfive/jh7100_dma.c
index e3cfc8cf65..5f6e78fc36 100644
--- a/drivers/soc/starfive/jh7100_dma.c
+++ b/drivers/soc/starfive/jh7100_dma.c
@@ -4,7 +4,7 @@
*/
#include <common.h>
-#include <asm/dma.h>
+#include <dma.h>
#include <soc/sifive/l2_cache.h>
#define SDRAM_CACHED_BASE 0x80000000
diff --git a/drivers/sound/gpio-beeper.c b/drivers/sound/gpio-beeper.c
index 300998d607..8fc3ce9410 100644
--- a/drivers/sound/gpio-beeper.c
+++ b/drivers/sound/gpio-beeper.c
@@ -7,10 +7,10 @@
#include <regulator.h>
#include <sound.h>
#include <of.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
struct gpio_beeper {
- int gpio;
+ struct gpio_desc *gpio;
struct sound_card card;
};
@@ -18,22 +18,20 @@ static int gpio_beeper_beep(struct sound_card *card, unsigned freq, unsigned dur
{
struct gpio_beeper *beeper = container_of(card, struct gpio_beeper, card);
- gpio_set_active(beeper->gpio, freq);
+ gpiod_set_value(beeper->gpio, freq);
return 0;
}
-static int gpio_beeper_probe(struct device_d *dev)
+static int gpio_beeper_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct gpio_beeper *beeper;
struct sound_card *card;
- int gpio;
+ struct gpio_desc *gpio;
gpio = gpiod_get(dev, NULL, GPIOD_OUT_LOW);
- if (gpio < 0) {
- dev_err(dev, "failed to request gpio: %pe\n", ERR_PTR(gpio));
- return gpio;
- }
+ if (IS_ERR(gpio))
+ return dev_errp_probe(dev, gpio, "failed to request gpio\n");
beeper = xzalloc(sizeof(*beeper));
beeper->gpio = gpio;
@@ -46,7 +44,7 @@ static int gpio_beeper_probe(struct device_d *dev)
return sound_card_register(card);
}
-static void gpio_beeper_suspend(struct device_d *dev)
+static void gpio_beeper_suspend(struct device *dev)
{
struct gpio_beeper *beeper = dev->priv;
@@ -57,8 +55,9 @@ static const struct of_device_id gpio_beeper_match[] = {
{ .compatible = "gpio-beeper", },
{ },
};
+MODULE_DEVICE_TABLE(of, gpio_beeper_match);
-static struct driver_d gpio_beeper_driver = {
+static struct driver gpio_beeper_driver = {
.name = "gpio-beeper",
.probe = gpio_beeper_probe,
.remove = gpio_beeper_suspend,
diff --git a/drivers/sound/pwm-beeper.c b/drivers/sound/pwm-beeper.c
index ef053f97cf..94b27359c1 100644
--- a/drivers/sound/pwm-beeper.c
+++ b/drivers/sound/pwm-beeper.c
@@ -31,8 +31,8 @@ static int pwm_beeper_beep(struct sound_card *card, unsigned freq, unsigned dura
pwm_get_state(beeper->pwm, &state);
- state.p_enable = true;
- state.period_ns = HZ_TO_NANOSECONDS(freq);
+ state.enabled = true;
+ state.period = HZ_TO_NANOSECONDS(freq);
pwm_set_relative_duty_cycle(&state, 50, 100);
error = pwm_apply_state(beeper->pwm, &state);
@@ -49,7 +49,7 @@ pwm_disable:
return error;
}
-static int pwm_beeper_probe(struct device_d *dev)
+static int pwm_beeper_probe(struct device *dev)
{
struct pwm_beeper *beeper;
struct sound_card *card;
@@ -60,18 +60,13 @@ static int pwm_beeper_probe(struct device_d *dev)
beeper = xzalloc(sizeof(*beeper));
dev->priv = beeper;
- beeper->pwm = of_pwm_request(dev->device_node, NULL);
- if (IS_ERR(beeper->pwm)) {
- error = PTR_ERR(beeper->pwm);
- if (error != -EPROBE_DEFER)
- dev_err(dev, "Failed to request PWM device: %d\n",
- error);
- return error;
- }
+ beeper->pwm = of_pwm_request(dev->of_node, NULL);
+ if (IS_ERR(beeper->pwm))
+ return dev_errp_probe(dev, beeper->pwm, "requesting PWM device\n");
/* Sync up PWM state and ensure it is off. */
pwm_init_state(beeper->pwm, &state);
- state.p_enable = false;
+ state.enabled = false;
error = pwm_apply_state(beeper->pwm, &state);
if (error) {
dev_err(dev, "failed to apply initial PWM state: %d\n",
@@ -80,15 +75,11 @@ static int pwm_beeper_probe(struct device_d *dev)
}
beeper->amplifier = regulator_get(dev, "amp");
- if (IS_ERR(beeper->amplifier)) {
- error = PTR_ERR(beeper->amplifier);
- if (error != -EPROBE_DEFER)
- dev_err(dev, "Failed to get 'amp' regulator: %d\n",
- error);
- return error;
- }
+ if (IS_ERR(beeper->amplifier))
+ return dev_errp_probe(dev, beeper->amplifier, "getting 'amp' regulator\n");
- error = of_property_read_u32(dev->device_node, "beeper-hz", &bell_frequency);
+ error = of_property_read_u32(dev->of_node, "beeper-hz",
+ &bell_frequency);
if (error) {
bell_frequency = 1000;
dev_dbg(dev, "failed to parse 'beeper-hz' property, using default: %uHz\n",
@@ -96,14 +87,14 @@ static int pwm_beeper_probe(struct device_d *dev)
}
card = &beeper->card;
- card->name = dev->device_node->full_name;
+ card->name = dev->of_node->full_name;
card->bell_frequency = bell_frequency;
card->beep = pwm_beeper_beep;
return sound_card_register(card);
}
-static void pwm_beeper_suspend(struct device_d *dev)
+static void pwm_beeper_suspend(struct device *dev)
{
struct pwm_beeper *beeper = dev->priv;
@@ -114,8 +105,9 @@ static const struct of_device_id pwm_beeper_match[] = {
{ .compatible = "pwm-beeper", },
{ },
};
+MODULE_DEVICE_TABLE(of, pwm_beeper_match);
-static struct driver_d pwm_beeper_driver = {
+static struct driver pwm_beeper_driver = {
.name = "pwm-beeper",
.probe = pwm_beeper_probe,
.remove = pwm_beeper_suspend,
diff --git a/drivers/sound/sdl.c b/drivers/sound/sdl.c
index 118d774295..396b6cbefb 100644
--- a/drivers/sound/sdl.c
+++ b/drivers/sound/sdl.c
@@ -40,7 +40,7 @@ static int sandbox_sound_beep(struct sound_card *card, unsigned freq, unsigned d
return ret;
}
-static int sandbox_sound_probe(struct device_d *dev)
+static int sandbox_sound_probe(struct device *dev)
{
struct sandbox_sound *priv;
struct sound_card *card;
@@ -78,8 +78,9 @@ static __maybe_unused struct of_device_id sandbox_sound_dt_ids[] = {
{ .compatible = "barebox,sandbox-sound" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, sandbox_sound_dt_ids);
-static struct driver_d sandbox_sound_drv = {
+static struct driver sandbox_sound_drv = {
.name = "sandbox-sound",
.of_compatible = sandbox_sound_dt_ids,
.probe = sandbox_sound_probe,
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8935feb97b..445c756a38 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -14,10 +14,6 @@ config SPI_MEM
This extension is meant to simplify interaction with SPI memories
by providing a high-level interface to send memory-like commands.
-config DRIVER_SPI_ALTERA
- bool "Altera SPI Master driver"
- depends on NIOS2
-
config DRIVER_SPI_ATH79
bool "Atheros AR71XX/AR724X/AR913X/AR933X SPI controller driver"
depends on MACH_MIPS_ATH79
@@ -116,7 +112,7 @@ config DRIVER_SPI_STM32
config SPI_NXP_FLEXSPI
tristate "NXP Flex SPI controller"
- depends on ARCH_IMX8M || COMPILE_TEST
+ depends on ARCH_IMX8M || ARCH_IMX93 || COMPILE_TEST
help
This enables support for the Flex SPI controller in master mode.
Up to four slave devices can be connected on two buses with two
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 3455eea869..68a8c4e675 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -8,7 +8,6 @@ obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
obj-$(CONFIG_DRIVER_SPI_LITEX_SPIFLASH) += litex_spiflash.o
obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o
obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o
-obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o
obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c
deleted file mode 100644
index 3db8af5415..0000000000
--- a/drivers/spi/altera_spi.c
+++ /dev/null
@@ -1,236 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com>
- */
-
-#include <common.h>
-#include <init.h>
-#include <driver.h>
-#include <spi/spi.h>
-#include <io.h>
-#include <asm/spi.h>
-#include <asm/nios2-io.h>
-#include <clock.h>
-
-static void altera_spi_cs_inactive(struct spi_device *spi);
-
-static int altera_spi_setup(struct spi_device *spi)
-{
- struct spi_master *master = spi->master;
- struct device_d spi_dev = spi->dev;
- struct altera_spi *altera_spi = container_of(master, struct altera_spi, master);
-
- if (spi->bits_per_word != altera_spi->databits) {
- dev_err(master->dev, " master doesn't support %d bits per word requested by %s\n",
- spi->bits_per_word, spi_dev.name);
- return -EINVAL;
- }
-
- if ((spi->mode & (SPI_CPHA | SPI_CPOL)) != altera_spi->mode) {
- dev_err(master->dev, " master doesn't support SPI_MODE%d requested by %s\n",
- spi->mode & (SPI_CPHA | SPI_CPOL), spi_dev.name);
- return -EINVAL;
- }
-
- if (spi->max_speed_hz < altera_spi->speed) {
- dev_err(master->dev, " frequency is too high for %s\n", spi_dev.name);
- return -EINVAL;
- }
-
- altera_spi_cs_inactive(spi);
-
- dev_dbg(master->dev, " mode 0x%08x, bits_per_word: %d, speed: %d\n",
- spi->mode, spi->bits_per_word, altera_spi->speed);
-
- return 0;
-}
-
-
-static unsigned int altera_spi_xchg_single(struct altera_spi *altera_spi, unsigned int data)
-{
- struct nios_spi *nios_spi = altera_spi->regs;
-
- while (!(readl(&nios_spi->status) & NIOS_SPI_TRDY));
- writel(data, &nios_spi->txdata);
-
- while (!(readl(&nios_spi->status) & NIOS_SPI_RRDY));
-
- return readl(&nios_spi->rxdata);
-}
-
-/*
- * When using SPI_CS_HIGH devices, only one device is allowed to be
- * connected to the Altera SPI master. This limitation is due to the
- * emulation of an active high CS by writing 0 to the slaveselect register
- * (this produce a '1' to all CS pins).
- */
-
-static void altera_spi_cs_active(struct spi_device *spi)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- struct nios_spi *nios_spi = altera_spi->regs;
- uint32_t tmp;
-
- if (spi->mode & SPI_CS_HIGH) {
- tmp = readw(&nios_spi->control);
- writew(tmp & ~NIOS_SPI_SSO, &nios_spi->control);
- writel(0, &nios_spi->slaveselect);
- } else {
- writel(1 << spi->chip_select, &nios_spi->slaveselect);
- tmp = readl(&nios_spi->control);
- writel(tmp | NIOS_SPI_SSO, &nios_spi->control);
- }
-}
-
-static void altera_spi_cs_inactive(struct spi_device *spi)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- struct nios_spi *nios_spi = altera_spi->regs;
- uint32_t tmp;
-
- if (spi->mode & SPI_CS_HIGH) {
- writel(1 << spi->chip_select, &nios_spi->slaveselect);
- tmp = readl(&nios_spi->control);
- writel(tmp | NIOS_SPI_SSO, &nios_spi->control);
- } else {
- tmp = readw(&nios_spi->control);
- writew(tmp & ~NIOS_SPI_SSO, &nios_spi->control);
- }
-}
-
-static unsigned altera_spi_do_xfer(struct spi_device *spi, struct spi_transfer *t)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- int word_len;
- unsigned retval = 0;
- u32 txval;
- u32 rxval;
-
- word_len = spi->bits_per_word;
-
- if (word_len <= 8) {
- const u8 *txbuf = t->tx_buf;
- u8 *rxbuf = t->rx_buf;
- int i = 0;
-
- while (i < t->len) {
- txval = txbuf ? txbuf[i] : 0;
- rxval = altera_spi_xchg_single(altera_spi, txval);
- if (rxbuf)
- rxbuf[i] = rxval;
- i++;
- retval++;
- }
- } else if (word_len <= 16) {
- const u16 *txbuf = t->tx_buf;
- u16 *rxbuf = t->rx_buf;
- int i = 0;
-
- while (i < t->len >> 1) {
- txval = txbuf ? txbuf[i] : 0;
- rxval = altera_spi_xchg_single(altera_spi, txval);
- if (rxbuf)
- rxbuf[i] = rxval;
- i++;
- retval += 2;
- }
- } else if (word_len <= 32) {
- const u32 *txbuf = t->tx_buf;
- u32 *rxbuf = t->rx_buf;
- int i = 0;
-
- while (i < t->len >> 2) {
- txval = txbuf ? txbuf[i] : 0;
- rxval = altera_spi_xchg_single(altera_spi, txval);
- if (rxbuf)
- rxbuf[i] = rxval;
- i++;
- retval += 4;
- }
- }
-
- return retval;
-}
-
-static int altera_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
-{
- struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
- struct nios_spi *nios_spi = altera_spi->regs;
- struct spi_transfer *t;
- unsigned int cs_change;
- const int nsecs = 50;
-
- altera_spi_cs_active(spi);
-
- cs_change = 0;
-
- mesg->actual_length = 0;
-
- list_for_each_entry(t, &mesg->transfers, transfer_list) {
-
- if (cs_change) {
- ndelay(nsecs);
- altera_spi_cs_inactive(spi);
- ndelay(nsecs);
- altera_spi_cs_active(spi);
- }
-
- cs_change = t->cs_change;
-
- mesg->actual_length += altera_spi_do_xfer(spi, t);
-
- if (cs_change) {
- altera_spi_cs_active(spi);
- }
- }
-
- /* Wait the end of any pending transfer */
- while ((readl(&nios_spi->status) & NIOS_SPI_TMT) == 0);
-
- if (!cs_change)
- altera_spi_cs_inactive(spi);
-
- return 0;
-}
-
-static int altera_spi_probe(struct device_d *dev)
-{
- struct resource *iores;
- struct spi_master *master;
- struct altera_spi *altera_spi;
- struct spi_altera_master *pdata = dev->platform_data;
- struct nios_spi *nios_spi;
-
- altera_spi = xzalloc(sizeof(*altera_spi));
-
- master = &altera_spi->master;
- master->dev = dev;
-
- master->setup = altera_spi_setup;
- master->transfer = altera_spi_transfer;
- master->num_chipselect = pdata->num_chipselect;
- master->bus_num = pdata->bus_num;
-
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- altera_spi->regs = IOMEM(iores->start);
-
- altera_spi->databits = pdata->databits;
- altera_spi->speed = pdata->speed;
- altera_spi->mode = pdata->spi_mode;
-
- nios_spi = altera_spi->regs;
- writel(0, &nios_spi->slaveselect);
- writel(0, &nios_spi->control);
-
- spi_register_master(master);
-
- return 0;
-}
-
-static struct driver_d altera_spi_driver = {
- .name = "altera_spi",
- .probe = altera_spi_probe,
-};
-device_platform_driver(altera_spi_driver);
diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c
index 383570b253..41a31ae922 100644
--- a/drivers/spi/ath79_spi.c
+++ b/drivers/spi/ath79_spi.c
@@ -126,7 +126,7 @@ static inline void ath79_spi_chipselect(struct ath79_spi *sp, int chipselect)
static int ath79_spi_setup(struct spi_device *spi)
{
struct spi_master *master = spi->master;
- struct device_d spi_dev = spi->dev;
+ struct device spi_dev = spi->dev;
if (spi->bits_per_word != 8) {
dev_err(master->dev, "master doesn't support %d bits per word requested by %s\n",
@@ -220,7 +220,7 @@ static void ath79_spi_disable(struct ath79_spi *sp)
ath79_spi_wr(sp, 0, AR71XX_SPI_REG_FS);
}
-static int ath79_spi_probe(struct device_d *dev)
+static int ath79_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -238,7 +238,7 @@ static int ath79_spi_probe(struct device_d *dev)
master->num_chipselect = 3;
if (IS_ENABLED(CONFIG_OFDEVICE)) {
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
u32 num_cs;
int ret;
@@ -269,7 +269,7 @@ static int ath79_spi_probe(struct device_d *dev)
return 0;
}
-static void ath79_spi_remove(struct device_d *dev)
+static void ath79_spi_remove(struct device *dev)
{
struct ath79_spi *sp = dev->priv;
@@ -284,8 +284,9 @@ static __maybe_unused struct of_device_id ath79_spi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ath79_spi_dt_ids);
-static struct driver_d ath79_spi_driver = {
+static struct driver ath79_spi_driver = {
.name = "ath79-spi",
.probe = ath79_spi_probe,
.remove = ath79_spi_remove,
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 55ebccc877..c680ee15a0 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -21,9 +21,9 @@
#include <of_gpio.h>
#include <io.h>
#include <spi/spi.h>
-#include <mach/iomux.h>
-#include <mach/board.h>
-#include <mach/cpu.h>
+#include <mach/at91/iomux.h>
+#include <mach/at91/board.h>
+#include <mach/at91/cpu.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -157,7 +157,6 @@ struct atmel_qspi {
void __iomem *mem;
struct clk *pclk;
struct clk *qspick;
- struct platform_device *pdev;
const struct atmel_qspi_caps *caps;
u32 mr;
};
@@ -414,7 +413,7 @@ static int atmel_qspi_init(struct atmel_qspi *aq)
return 0;
}
-static int atmel_qspi_probe(struct device_d *dev)
+static int atmel_qspi_probe(struct device *dev)
{
struct spi_controller *ctrl;
struct atmel_qspi *aq;
@@ -527,8 +526,9 @@ static const struct of_device_id atmel_qspi_dt_ids[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_qspi_dt_ids);
-static struct driver_d atmel_qspi_driver = {
+static struct driver atmel_qspi_driver = {
.name = "atmel_qspi",
.of_compatible = atmel_qspi_dt_ids,
.probe = atmel_qspi_probe,
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index ec90330e53..90f655dc3e 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -21,9 +21,9 @@
#include <of_gpio.h>
#include <io.h>
#include <spi/spi.h>
-#include <mach/iomux.h>
-#include <mach/board.h>
-#include <mach/cpu.h>
+#include <mach/at91/iomux.h>
+#include <mach/at91/board.h>
+#include <mach/at91/cpu.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -378,18 +378,18 @@ static void atmel_get_caps(struct atmel_spi *as)
unsigned int version;
version = atmel_get_version(as);
- dev_info(as->master.dev, "version: 0x%x\n", version);
+ dev_dbg(as->master.dev, "version: 0x%x\n", version);
as->caps.is_spi2 = version > 0x121;
}
-static int atmel_spi_probe(struct device_d *dev)
+static int atmel_spi_probe(struct device *dev)
{
struct resource *iores;
int ret = 0;
int i;
struct spi_master *master;
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
struct atmel_spi *as;
struct at91_spi_platform_data *pdata = dev->platform_data;
@@ -408,7 +408,7 @@ static int atmel_spi_probe(struct device_d *dev)
master->num_chipselect = pdata->num_chipselect;
as->cs_pins = pdata->chipselect;
} else {
- master->num_chipselect = of_gpio_named_count(node, "cs-gpios");
+ master->num_chipselect = of_gpio_count_csgpios(node);
as->cs_pins = xzalloc(sizeof(u32) * master->num_chipselect);
for (i = 0; i < master->num_chipselect; i++) {
@@ -474,8 +474,9 @@ const static __maybe_unused struct of_device_id atmel_spi_dt_ids[] = {
{ .compatible = "atmel,at91rm9200-spi" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids);
-static struct driver_d atmel_spi_driver = {
+static struct driver atmel_spi_driver = {
.name = "atmel_spi",
.probe = atmel_spi_probe,
.of_compatible = DRV_OF_COMPAT(atmel_spi_dt_ids),
diff --git a/drivers/spi/dspi_spi.c b/drivers/spi/dspi_spi.c
index fcfa2a830d..75addfd12c 100644
--- a/drivers/spi/dspi_spi.c
+++ b/drivers/spi/dspi_spi.c
@@ -21,8 +21,8 @@
#include <gpio.h>
#include <of_gpio.h>
#include <of_device.h>
-#include <mach/spi.h>
-#include <mach/generic.h>
+#include <mach/imx/spi.h>
+#include <mach/imx/generic.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <clock.h>
@@ -93,7 +93,7 @@ static struct fsl_dspi *to_dspi(struct spi_master *master)
return container_of(master, struct fsl_dspi, master);
}
-static void hz_to_spi_baud(struct device_d *dev,
+static void hz_to_spi_baud(struct device *dev,
char *pbr, char *br, int speed_hz,
unsigned long clkrate)
{
@@ -132,7 +132,7 @@ static void hz_to_spi_baud(struct device_d *dev,
}
}
-static void ns_delay_scale(struct device_d *dev,
+static void ns_delay_scale(struct device *dev,
char *psc, char *sc, int delay_ns,
unsigned long clkrate)
{
@@ -314,12 +314,12 @@ static int dspi_setup(struct spi_device *spi)
return 0;
}
-static int dspi_probe(struct device_d *dev)
+static int dspi_probe(struct device *dev)
{
struct resource *io;
struct fsl_dspi *dspi;
struct spi_master *master;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret = 0;
uint32_t bus_num = 0;
@@ -351,9 +351,9 @@ static int dspi_probe(struct device_d *dev)
else
master->bus_num = dev->id;
- of_property_read_u32(dev->device_node, "fsl,spi-cs-sck-delay",
+ of_property_read_u32(dev->of_node, "fsl,spi-cs-sck-delay",
&dspi->cs_sck_delay);
- of_property_read_u32(dev->device_node, "fsl,spi-sck-cs-delay",
+ of_property_read_u32(dev->of_node, "fsl,spi-sck-cs-delay",
&dspi->sck_cs_delay);
io = dev_request_mem_resource(dev, 0);
@@ -400,8 +400,9 @@ static const struct of_device_id dspi_dt_ids[] = {
{ .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dspi_dt_ids);
-static struct driver_d dspi_spi_driver = {
+static struct driver dspi_spi_driver = {
.name = "fsl-dspi",
.probe = dspi_probe,
.of_compatible = DRV_OF_COMPAT(dspi_dt_ids),
diff --git a/drivers/spi/gpio_spi.c b/drivers/spi/gpio_spi.c
index 1956eed9cc..e5664df3fe 100644
--- a/drivers/spi/gpio_spi.c
+++ b/drivers/spi/gpio_spi.c
@@ -126,9 +126,9 @@ static int gpio_spi_setup(struct spi_device *spi)
return 0;
}
-static int gpio_spi_of_probe(struct device_d *dev)
+static int gpio_spi_of_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct gpio_spi_pdata *pdata;
int n, sck;
@@ -136,12 +136,9 @@ static int gpio_spi_of_probe(struct device_d *dev)
return 0;
sck = of_get_named_gpio(np, "gpio-sck", 0);
- if (sck == -EPROBE_DEFER)
- return sck;
- if (!gpio_is_valid(sck)) {
- dev_err(dev, "missing mandatory SCK gpio\n");
- return sck;
- }
+ if (!gpio_is_valid(sck))
+ return dev_err_probe(dev, sck < 0 ? sck : -EINVAL,
+ "missing mandatory SCK gpio\n");
pdata = xzalloc(sizeof(*pdata));
pdata->sck = sck;
@@ -166,7 +163,7 @@ static int gpio_spi_of_probe(struct device_d *dev)
return 0;
}
-static int gpio_spi_probe(struct device_d *dev)
+static int gpio_spi_probe(struct device *dev)
{
struct gpio_spi *priv;
struct gpio_spi_pdata *pdata;
@@ -222,8 +219,9 @@ static struct of_device_id __maybe_unused gpio_spi_dt_ids[] = {
{ .compatible = "spi-gpio", },
{ }
};
+MODULE_DEVICE_TABLE(of, gpio_spi_dt_ids);
-static struct driver_d gpio_spi_driver = {
+static struct driver gpio_spi_driver = {
.name = "gpio-spi",
.probe = gpio_spi_probe,
.of_compatible = DRV_OF_COMPAT(gpio_spi_dt_ids),
diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c
index ad3e79d54c..5310a2715d 100644
--- a/drivers/spi/imx_spi.c
+++ b/drivers/spi/imx_spi.c
@@ -15,8 +15,8 @@
#include <malloc.h>
#include <gpio.h>
#include <of_gpio.h>
-#include <mach/spi.h>
-#include <mach/generic.h>
+#include <mach/imx/spi.h>
+#include <mach/imx/generic.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <clock.h>
@@ -562,13 +562,13 @@ static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_2_3 = {
static int imx_spi_dt_probe(struct imx_spi *imx)
{
- struct device_node *node = imx->master.dev->device_node;
+ struct device_node *node = imx->master.dev->of_node;
int i;
if (!node)
return -ENODEV;
- imx->master.num_chipselect = of_gpio_named_count(node, "cs-gpios");
+ imx->master.num_chipselect = of_gpio_count_csgpios(node);
imx->cs_array = xzalloc(sizeof(u32) * imx->master.num_chipselect);
for (i = 0; i < imx->master.num_chipselect; i++)
@@ -577,7 +577,7 @@ static int imx_spi_dt_probe(struct imx_spi *imx)
return 0;
}
-static int imx_spi_probe(struct device_d *dev)
+static int imx_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -603,7 +603,7 @@ static int imx_spi_probe(struct device_d *dev)
master->num_chipselect = pdata->num_chipselect;
imx->cs_array = pdata->chipselect;
} else if (IS_ENABLED(CONFIG_OFDEVICE)) {
- ret = of_alias_get_id(dev->device_node, "spi");
+ ret = of_alias_get_id(dev->of_node, "spi");
if (ret < 0)
goto err_free;
master->bus_num = ret;
@@ -662,11 +662,16 @@ static __maybe_unused struct of_device_id imx_spi_dt_ids[] = {
.compatible = "fsl,imx51-ecspi",
.data = &spi_imx_devtype_data_2_3,
},
+ {
+ .compatible = "fsl,imx6ul-ecspi",
+ .data = &spi_imx_devtype_data_2_3,
+ },
#endif
{
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_spi_dt_ids);
static struct platform_device_id imx_spi_ids[] = {
#if IS_ENABLED(CONFIG_DRIVER_SPI_IMX_0_0)
@@ -692,7 +697,7 @@ static struct platform_device_id imx_spi_ids[] = {
}
};
-static struct driver_d imx_spi_driver = {
+static struct driver imx_spi_driver = {
.name = "imx_spi",
.probe = imx_spi_probe,
.of_compatible = DRV_OF_COMPAT(imx_spi_dt_ids),
diff --git a/drivers/spi/litex_spiflash.c b/drivers/spi/litex_spiflash.c
index e1fc5a4d3f..58ce6ad5f5 100644
--- a/drivers/spi/litex_spiflash.c
+++ b/drivers/spi/litex_spiflash.c
@@ -93,7 +93,7 @@ static inline void litex_spiflash_spi_chipselect(struct litex_spiflash_spi *sc,
static int litex_spiflash_spi_setup(struct spi_device *spi)
{
struct spi_master *master = spi->master;
- struct device_d spi_dev = spi->dev;
+ struct device spi_dev = spi->dev;
if (spi->bits_per_word != 8) {
dev_err(master->dev, "master doesn't support %d bits per word requested by %s\n",
@@ -184,7 +184,7 @@ static void litex_spiflash_spi_disable(struct litex_spiflash_spi *sp)
litex_spiflash_spi_wr(sp, 0, SPIFLASH_BITBANG_EN);
}
-static int litex_spiflash_spi_probe(struct device_d *dev)
+static int litex_spiflash_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -216,7 +216,7 @@ static int litex_spiflash_spi_probe(struct device_d *dev)
return 0;
}
-static void litex_spiflash_spi_remove(struct device_d *dev)
+static void litex_spiflash_spi_remove(struct device *dev)
{
struct litex_spiflash_spi *sp = dev->priv;
@@ -231,8 +231,9 @@ static __maybe_unused struct of_device_id litex_spiflash_spi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, litex_spiflash_spi_dt_ids);
-static struct driver_d litex_spiflash_spi_driver = {
+static struct driver litex_spiflash_spi_driver = {
.name = "litex-spiflash",
.probe = litex_spiflash_spi_probe,
.remove = litex_spiflash_spi_remove,
diff --git a/drivers/spi/mvebu_spi.c b/drivers/spi/mvebu_spi.c
index 3325138604..e220d1f9ee 100644
--- a/drivers/spi/mvebu_spi.c
+++ b/drivers/spi/mvebu_spi.c
@@ -347,8 +347,9 @@ static struct of_device_id mvebu_spi_dt_ids[] = {
.data = &mvebu_spi_set_baudrate },
{ }
};
+MODULE_DEVICE_TABLE(of, mvebu_spi_dt_ids);
-static int mvebu_spi_probe(struct device_d *dev)
+static int mvebu_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -356,7 +357,7 @@ static int mvebu_spi_probe(struct device_d *dev)
const struct of_device_id *match;
int ret = 0;
- match = of_match_node(mvebu_spi_dt_ids, dev->device_node);
+ match = of_match_node(mvebu_spi_dt_ids, dev->of_node);
if (!match)
return -EINVAL;
@@ -391,7 +392,7 @@ err_free:
return ret;
}
-static struct driver_d mvebu_spi_driver = {
+static struct driver mvebu_spi_driver = {
.name = "mvebu-spi",
.probe = mvebu_spi_probe,
.of_compatible = DRV_OF_COMPAT(mvebu_spi_dt_ids),
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 071622bd54..d2ec42f064 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -18,9 +18,8 @@
#include <stmp-device.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <mach/generic.h>
-#include <mach/clock.h>
-#include <mach/ssp.h>
+#include <mach/mxs/generic.h>
+#include <mach/mxs/ssp.h>
#define MXS_SPI_MAX_TIMEOUT (10 * MSECOND)
@@ -243,7 +242,7 @@ static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
return 0;
}
-static int mxs_spi_probe(struct device_d *dev)
+static int mxs_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -275,7 +274,7 @@ static int mxs_spi_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mxs_spi_driver = {
+static struct driver mxs_spi_driver = {
.name = "mxs_spi",
.probe = mxs_spi_probe,
};
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 44c35673d8..78c3a82338 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -332,18 +332,19 @@ static int omap3_spi_setup(struct spi_device *spi)
return 0;
}
-static int omap3_spi_probe_dt(struct device_d *dev, struct omap3_spi_master *omap3_master)
+static int omap3_spi_probe_dt(struct device *dev,
+ struct omap3_spi_master *omap3_master)
{
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node)
return -ENODEV;
- if (of_property_read_bool(dev->device_node, "ti,pindir-d0-out-d1-in"))
+ if (of_property_read_bool(dev->of_node, "ti,pindir-d0-out-d1-in"))
omap3_master->swap_miso_mosi = 1;
return 0;
}
-static int omap3_spi_probe(struct device_d *dev)
+static int omap3_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -421,6 +422,7 @@ static __maybe_unused struct of_device_id omap_spi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_spi_dt_ids);
static struct platform_device_id omap_spi_ids[] = {
{
@@ -434,7 +436,7 @@ static struct platform_device_id omap_spi_ids[] = {
},
};
-static struct driver_d omap3_spi_driver = {
+static struct driver omap3_spi_driver = {
.name = "omap-spi",
.probe = omap3_spi_probe,
.of_compatible = DRV_OF_COMPAT(omap_spi_dt_ids),
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 21cf84233b..f032e2673e 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -10,7 +10,7 @@
#include <errno.h>
#include <init.h>
#include <io.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <spi/spi.h>
#include <linux/clk.h>
#include <linux/math64.h>
@@ -132,7 +132,7 @@ static const struct fsl_dspi_devtype_data ls2085a_data = {
struct fsl_dspi {
struct spi_controller ctlr;
- struct device_d *dev;
+ struct device *dev;
struct regmap *regmap;
struct regmap *regmap_pushr;
@@ -462,10 +462,10 @@ static int dspi_setup(struct spi_device *spi)
return -ENOMEM;
}
- of_property_read_u32(spi->dev.device_node, "fsl,spi-cs-sck-delay",
+ of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay",
&cs_sck_delay);
- of_property_read_u32(spi->dev.device_node, "fsl,spi-sck-cs-delay",
+ of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay",
&sck_cs_delay);
chip->void_write_data = 0;
@@ -515,6 +515,7 @@ static const struct of_device_id fsl_dspi_dt_ids[] = {
{ .compatible = "fsl,ls2085a-dspi", .data = &ls2085a_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
static const struct regmap_config dspi_regmap_config = {
.reg_bits = 32,
@@ -553,9 +554,9 @@ static void dspi_init(struct fsl_dspi *dspi)
SPI_CTARE_FMSZE(0) | SPI_CTARE_DTCP(1));
}
-static int dspi_probe(struct device_d *dev)
+static int dspi_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const struct regmap_config *regmap_config;
struct spi_master *master;
int ret, cs_num, bus_num = -1;
@@ -647,7 +648,7 @@ out_ctlr_put:
return ret;
}
-static struct driver_d fsl_dspi_driver = {
+static struct driver fsl_dspi_driver = {
.name = "fsl-dspi",
.probe = dspi_probe,
.of_compatible = DRV_OF_COMPAT(fsl_dspi_dt_ids),
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
index f9bf888603..17e6d1df86 100644
--- a/drivers/spi/spi-fsl-qspi.c
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -239,7 +239,7 @@ struct fsl_qspi {
void __iomem *ahb_addr;
u32 memmap_phy;
struct clk *clk, *clk_en;
- struct device_d *dev;
+ struct device *dev;
struct spi_controller ctlr;
const struct fsl_qspi_devtype_data *devtype_data;
struct mutex lock;
@@ -743,7 +743,7 @@ static int fsl_qspi_setup(struct spi_device *spi)
static const char *fsl_qspi_get_name(struct spi_mem *mem)
{
struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
- struct device_d *dev = &mem->spi->dev;
+ struct device *dev = &mem->spi->dev;
const char *name;
/*
@@ -751,7 +751,7 @@ static const char *fsl_qspi_get_name(struct spi_mem *mem)
* mtd/spi-nor/fsl-quadspi.c, we set a custom name derived from the
* platform_device of the controller.
*/
- if (of_get_available_child_count(q->dev->device_node) == 1)
+ if (of_get_available_child_count(q->dev->of_node) == 1)
return dev_name(q->dev);
name = basprintf("%s-%d", dev_name(q->dev), mem->spi->chip_select);
@@ -770,7 +770,7 @@ static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
.get_name = fsl_qspi_get_name,
};
-static int fsl_qspi_probe(struct device_d *dev)
+static int fsl_qspi_probe(struct device *dev)
{
struct spi_controller *ctlr;
struct resource *res;
@@ -860,8 +860,9 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
-static struct driver_d fsl_qspi_driver = {
+static struct driver fsl_qspi_driver = {
.name = "fsl-quadspi",
.probe = fsl_qspi_probe,
.of_compatible = DRV_OF_COMPAT(fsl_qspi_dt_ids),
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 673b91d6dc..d8675779da 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -38,7 +38,7 @@
#include <errno.h>
#include <init.h>
#include <io.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/clk.h>
@@ -355,7 +355,7 @@ struct nxp_fspi {
u32 memmap_start;
u32 memmap_len;
struct clk *clk, *clk_en;
- struct device_d *dev;
+ struct device *dev;
struct spi_controller ctlr;
const struct nxp_fspi_devtype_data *devtype_data;
struct mutex lock;
@@ -544,7 +544,7 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
for (i = 0; i < ARRAY_SIZE(lutval); i++)
fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
- dev_dbg((const struct device_d *)f->dev,
+ dev_dbg((const struct device *)f->dev,
"CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3],
op->data.nbytes);
@@ -642,7 +642,8 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
fspi_writel(f, size_kb, f->iobase + FSPI_FLSHA1CR0 +
4 * spi->chip_select);
- dev_dbg((const struct device_d *)f->dev, "Slave device [CS:%x] selected\n",
+ dev_dbg((const struct device *)f->dev,
+ "Slave device [CS:%x] selected\n",
spi->chip_select);
nxp_fspi_clk_disable_unprep(f);
@@ -937,12 +938,12 @@ static int nxp_fspi_setup(struct spi_device *spi)
static const char *nxp_fspi_get_name(struct spi_mem *mem)
{
struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
- struct device_d *dev = (struct device_d *)&mem->spi->dev;
+ struct device *dev = (struct device *)&mem->spi->dev;
const char *name;
/* Set custom name derived from the platform_device of the controller.
*/
- if (of_get_available_child_count(f->dev->device_node) == 1)
+ if (of_get_available_child_count(f->dev->of_node) == 1)
return dev_name(f->dev);
name = basprintf("%s-%d", dev_name(f->dev), mem->spi->chip_select);
@@ -961,7 +962,7 @@ static const struct spi_controller_mem_ops nxp_fspi_mem_ops = {
.get_name = nxp_fspi_get_name,
};
-static int nxp_fspi_probe(struct device_d *dev)
+static int nxp_fspi_probe(struct device *dev)
{
struct spi_controller *ctlr;
struct resource *res;
@@ -992,18 +993,19 @@ static int nxp_fspi_probe(struct device_d *dev)
/* find the resources */
res = dev_request_mem_resource(dev, 0);
- f->iobase = IOMEM(res->start);
- if (IS_ERR(f->iobase)) {
- ret = PTR_ERR(f->iobase);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
goto err_put_ctrl;
}
+ f->iobase = IOMEM(res->start);
+
res = dev_request_mem_resource(dev, 1);
- f->ahb_addr = IOMEM(res->start);
- if (IS_ERR(f->ahb_addr)) {
- ret = PTR_ERR(f->ahb_addr);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
goto err_put_ctrl;
}
+ f->ahb_addr = IOMEM(res->start);
/* assign memory mapped starting address and mapped size. */
f->memmap_phy = res->start;
@@ -1052,8 +1054,9 @@ static const struct of_device_id nxp_fspi_dt_ids[] = {
{ .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
-static struct driver_d nxp_fspi_driver = {
+static struct driver nxp_fspi_driver = {
.name = "nxp-fspi",
.probe = nxp_fspi_probe,
.of_compatible = DRV_OF_COMPAT(nxp_fspi_dt_ids),
diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
index 713bcc0c3b..fbe80718e4 100644
--- a/drivers/spi/spi-sifive.c
+++ b/drivers/spi/spi-sifive.c
@@ -301,7 +301,7 @@ static int sifive_spi_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct spi_device *spi_dev = mem->spi;
- struct device_d *dev = &spi_dev->dev;
+ struct device *dev = &spi_dev->dev;
struct sifive_spi *spi = spi_controller_get_devdata(spi_dev->controller);
u8 opcode = op->cmd.opcode;
int ret;
@@ -415,7 +415,7 @@ static int sifive_spi_setup(struct spi_device *spi_dev)
static void sifive_spi_init_hw(struct sifive_spi *spi)
{
- struct device_d *dev = spi->ctlr.dev;
+ struct device *dev = spi->ctlr.dev;
u32 cs_bits;
/* probe the number of CS lines */
@@ -457,7 +457,7 @@ static const struct spi_controller_mem_ops sifive_spi_mem_ops = {
static void sifive_spi_dt_probe(struct sifive_spi *spi)
{
- struct device_node *node = spi->ctlr.dev->device_node;
+ struct device_node *node = spi->ctlr.dev->of_node;
spi->fifo_depth = SIFIVE_SPI_DEFAULT_DEPTH;
of_property_read_u32(node, "sifive,fifo-depth", &spi->fifo_depth);
@@ -466,7 +466,7 @@ static void sifive_spi_dt_probe(struct sifive_spi *spi)
of_property_read_u32(node, "sifive,max-bits-per-word", &spi->bits_per_word);
}
-static int sifive_spi_probe(struct device_d *dev)
+static int sifive_spi_probe(struct device *dev)
{
struct sifive_spi *spi;
struct resource *iores;
@@ -512,8 +512,9 @@ static const struct of_device_id sifive_spi_ids[] = {
{ .compatible = "sifive,spi0" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, sifive_spi_ids);
-static struct driver_d sifive_spi_driver = {
+static struct driver sifive_spi_driver = {
.name = "sifive_spi",
.probe = sifive_spi_probe,
.of_compatible = sifive_spi_ids,
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b840dbe286..c627d88954 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -12,6 +12,7 @@
#include <spi/spi.h>
#include <xfuncs.h>
#include <malloc.h>
+#include <slice.h>
#include <errno.h>
#include <init.h>
#include <of.h>
@@ -29,6 +30,7 @@ struct boardinfo {
};
static LIST_HEAD(board_list);
+static LIST_HEAD(spi_controller_list);
/**
* spi_new_device - instantiate one new SPI device
@@ -71,7 +73,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
/* allocate a free id for this chip */
proxy->dev.id = DEVICE_ID_DYNAMIC;
proxy->dev.type_data = proxy;
- proxy->dev.device_node = chip->device_node;
+ proxy->dev.of_node = chip->device_node;
proxy->dev.parent = ctrl->dev;
proxy->master = proxy->controller = ctrl;
@@ -108,9 +110,8 @@ EXPORT_SYMBOL(spi_new_device);
static void spi_of_register_slaves(struct spi_controller *ctrl)
{
struct device_node *n;
- struct spi_board_info chip;
struct property *reg;
- struct device_node *node = ctrl->dev->device_node;
+ struct device_node *node = ctrl->dev->of_node;
if (!IS_ENABLED(CONFIG_OFDEVICE))
return;
@@ -119,7 +120,14 @@ static void spi_of_register_slaves(struct spi_controller *ctrl)
return;
for_each_available_child_of_node(node, n) {
- memset(&chip, 0, sizeof(chip));
+ struct spi_board_info chip = {};
+
+ if (n->dev) {
+ dev_dbg(ctrl->dev, "skipping already registered %s\n",
+ dev_name(n->dev));
+ continue;
+ }
+
chip.name = xstrdup(n->name);
chip.bus_num = ctrl->bus_num;
/* Mode (clock phase/polarity/etc.) */
@@ -138,7 +146,18 @@ static void spi_of_register_slaves(struct spi_controller *ctrl)
continue;
chip.chip_select = of_read_number(reg->value, 1);
chip.device_node = n;
- spi_register_board_info(&chip, 1);
+ spi_new_device(ctrl, &chip);
+ }
+}
+
+static void spi_controller_rescan(struct device *dev)
+{
+ struct spi_controller *ctrl;
+
+ list_for_each_entry(ctrl, &spi_controller_list, list) {
+ if (ctrl->dev != dev)
+ continue;
+ spi_of_register_slaves(ctrl);
}
}
@@ -196,8 +215,6 @@ static void scan_boardinfo(struct spi_controller *ctrl)
}
}
-static LIST_HEAD(spi_controller_list);
-
static int spi_controller_check_ops(struct spi_controller *ctlr)
{
/*
@@ -253,14 +270,16 @@ int spi_register_controller(struct spi_controller *ctrl)
if (status)
return status;
+ slice_init(&ctrl->slice, dev_name(ctrl->dev));
+
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (ctrl->num_chipselect == 0)
return -EINVAL;
- if ((ctrl->bus_num < 0) && ctrl->dev->device_node)
- ctrl->bus_num = of_alias_get_id(ctrl->dev->device_node, "spi");
+ if ((ctrl->bus_num < 0) && ctrl->dev->of_node)
+ ctrl->bus_num = of_alias_get_id(ctrl->dev->of_node, "spi");
/* convention: dynamically assigned bus IDs count down from the max */
if (ctrl->bus_num < 0)
@@ -274,6 +293,9 @@ int spi_register_controller(struct spi_controller *ctrl)
scan_boardinfo(ctrl);
status = 0;
+ if (!ctrl->dev->rescan)
+ ctrl->dev->rescan = spi_controller_rescan;
+
return status;
}
EXPORT_SYMBOL(spi_register_controller);
@@ -333,12 +355,19 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
int status;
+ int ret;
status = __spi_validate(spi, message);
if (status != 0)
return status;
- return spi->controller->transfer(spi, message);
+ slice_acquire(&spi->controller->slice);
+
+ ret = spi->controller->transfer(spi, message);
+
+ slice_release(&spi->controller->slice);
+
+ return ret;
}
/**
@@ -383,22 +412,9 @@ int spi_write_then_read(struct spi_device *spi,
}
EXPORT_SYMBOL(spi_write_then_read);
-static int spi_probe(struct device_d *dev)
-{
- return dev->driver->probe(dev);
-}
-
-static void spi_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
struct bus_type spi_bus = {
.name = "spi",
.match = device_match_of_modalias,
- .probe = spi_probe,
- .remove = spi_remove,
};
static int spi_bus_init(void)
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
index 639c4f1740..9ef405a788 100644
--- a/drivers/spi/stm32_spi.c
+++ b/drivers/spi/stm32_spi.c
@@ -111,6 +111,24 @@ static inline struct stm32_spi_priv *to_stm32_spi_priv(struct spi_master *master
return container_of(master, struct stm32_spi_priv, master);
}
+static int stm32_spi_get_bpw_mask(struct stm32_spi_priv *priv)
+{
+ u32 cfg1, max_bpw;
+
+ /*
+ * The most significant bit at DSIZE bit field is reserved when the
+ * maximum data size of periperal instances is limited to 16-bit
+ */
+ setbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE);
+
+ cfg1 = readl(priv->base + STM32_SPI_CFG1);
+ max_bpw = FIELD_GET(SPI_CFG1_DSIZE, cfg1) + 1;
+
+ dev_dbg(priv->master.dev, "%d-bit maximum data frame\n", max_bpw);
+
+ return SPI_BPW_RANGE_MASK(4, max_bpw);
+}
+
static void stm32_spi_write_txfifo(struct stm32_spi_priv *priv)
{
while ((priv->tx_len > 0) &&
@@ -190,7 +208,7 @@ static void stm32_spi_disable(struct stm32_spi_priv *priv)
static void stm32_spi_stopxfer(struct stm32_spi_priv *priv)
{
- struct device_d *dev = priv->master.dev;
+ struct device *dev = priv->master.dev;
u32 cr1, sr;
int ret;
@@ -261,19 +279,15 @@ static void stm32_spi_set_mode(struct stm32_spi_priv *priv, unsigned mode)
static void stm32_spi_set_fthlv(struct stm32_spi_priv *priv, u32 xfer_len)
{
- u32 fthlv, half_fifo;
+ u32 fthlv, packet, bpw;
/* data packet should not exceed 1/2 of fifo space */
- half_fifo = (priv->fifo_size / 2);
-
- /* data_packet should not exceed transfer length */
- fthlv = (half_fifo > xfer_len) ? xfer_len : half_fifo;
+ packet = clamp(xfer_len, 1U, priv->fifo_size / 2);
/* align packet size with data registers access */
- fthlv -= (fthlv % 4);
+ bpw = DIV_ROUND_UP(priv->cur_bpw, 8);
+ fthlv = DIV_ROUND_UP(packet, bpw);
- if (!fthlv)
- fthlv = 1;
clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_FTHLV,
(fthlv - 1) << SPI_CFG1_FTHLV_SHIFT);
}
@@ -339,14 +353,22 @@ out:
static int stm32_spi_transfer_one(struct stm32_spi_priv *priv,
struct spi_transfer *t)
{
- struct device_d *dev = priv->master.dev;
+ struct device *dev = priv->master.dev;
u32 sr;
u32 ifcr = 0;
u32 mode;
int xfer_status = 0;
+ int nb_words;
- if (t->len <= SPI_CR2_TSIZE)
- writel(t->len, priv->base + STM32_SPI_CR2);
+ if (t->bits_per_word <= 8)
+ nb_words = t->len;
+ else if (t->bits_per_word <= 16)
+ nb_words = DIV_ROUND_UP(t->len * 8, 16);
+ else
+ nb_words = DIV_ROUND_UP(t->len * 8, 32);
+
+ if (nb_words <= SPI_CR2_TSIZE)
+ writel(nb_words, priv->base + STM32_SPI_CR2);
else
return -EMSGSIZE;
@@ -361,9 +383,11 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv,
else if (!priv->rx_buf)
mode = SPI_SIMPLEX_TX;
- if (priv->cur_xferlen != t->len || priv->cur_mode != mode) {
+ if (priv->cur_xferlen != t->len || priv->cur_mode != mode ||
+ priv->cur_bpw != t->bits_per_word) {
priv->cur_mode = mode;
priv->cur_xferlen = t->len;
+ priv->cur_bpw = t->bits_per_word;
/* Disable the SPI hardware to unlock CFG1/CFG2 registers */
stm32_spi_disable(priv);
@@ -373,6 +397,9 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv,
stm32_spi_set_fthlv(priv, t->len);
+ clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
+ priv->cur_bpw - 1);
+
/* Enable the SPI hardware */
stm32_spi_enable(priv);
}
@@ -511,17 +538,17 @@ static int stm32_spi_get_fifo_size(struct stm32_spi_priv *priv)
static void stm32_spi_dt_probe(struct stm32_spi_priv *priv)
{
- struct device_node *node = priv->master.dev->device_node;
+ struct device_node *node = priv->master.dev->of_node;
int i;
- priv->master.num_chipselect = of_gpio_named_count(node, "cs-gpios");
+ priv->master.num_chipselect = of_gpio_count_csgpios(node);
priv->cs_gpios = xzalloc(sizeof(u32) * priv->master.num_chipselect);
for (i = 0; i < priv->master.num_chipselect; i++)
priv->cs_gpios[i] = of_get_named_gpio(node, "cs-gpios", i);
}
-static int stm32_spi_probe(struct device_d *dev)
+static int stm32_spi_probe(struct device *dev)
{
struct resource *iores;
struct spi_master *master;
@@ -560,6 +587,7 @@ static int stm32_spi_probe(struct device_d *dev)
if (ret)
return ret;
+ master->bits_per_word_mask = stm32_spi_get_bpw_mask(priv);
priv->fifo_size = stm32_spi_get_fifo_size(priv);
priv->cur_mode = SPI_FULL_DUPLEX;
@@ -588,7 +616,7 @@ static int stm32_spi_probe(struct device_d *dev)
return spi_register_master(master);
}
-static void stm32_spi_remove(struct device_d *dev)
+static void stm32_spi_remove(struct device *dev)
{
struct stm32_spi_priv *priv = dev->priv;
@@ -600,8 +628,9 @@ static const struct of_device_id stm32_spi_ids[] = {
{ .compatible = "st,stm32h7-spi", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_spi_ids);
-static struct driver_d stm32_spi_driver = {
+static struct driver stm32_spi_driver = {
.name = "stm32_spi",
.probe = stm32_spi_probe,
.remove = stm32_spi_remove,
diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
index f0edc41c37..3da245feb7 100644
--- a/drivers/spi/zynq_qspi.c
+++ b/drivers/spi/zynq_qspi.c
@@ -125,7 +125,7 @@
*/
struct zynq_qspi {
struct spi_controller ctlr;
- struct device_d *dev;
+ struct device *dev;
void __iomem *regs;
struct clk *refclk;
struct clk *pclk;
@@ -539,9 +539,9 @@ static const struct spi_controller_mem_ops zynq_qspi_mem_ops = {
.exec_op = zynq_qspi_exec_mem_op,
};
-static int zynq_qspi_probe(struct device_d *dev)
+static int zynq_qspi_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct spi_controller *ctlr;
struct zynq_qspi *xqspi;
struct resource *iores;
@@ -603,8 +603,9 @@ static const struct of_device_id zynq_qspi_of_match[] = {
{ .compatible = "xlnx,zynq-qspi-1.0", },
{ /* end of table */ }
};
+MODULE_DEVICE_TABLE(of, zynq_qspi_of_match);
-static struct driver_d zynq_qspi_driver = {
+static struct driver zynq_qspi_driver = {
.name = "zynq-qspi",
.probe = zynq_qspi_probe,
.of_compatible = DRV_OF_COMPAT(zynq_qspi_of_match),
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig
new file mode 100644
index 0000000000..6644ebce49
--- /dev/null
+++ b/drivers/tee/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Generic Trusted Execution Environment Configuration
+menuconfig TEE
+ tristate "Trusted Execution Environment support"
+ select ARM_SMCCC
+ help
+ This implements a generic interface towards a Trusted Execution
+ Environment (TEE). A TEE is a trusted OS running in some secure
+ environment, for example, TrustZone on ARM cpus, or a separate
+ secure co-processor etc. See also:
+ https://en.wikipedia.org/wiki/Trusted_execution_environment
+
+if TEE
+
+source "drivers/tee/optee/Kconfig"
+
+endif
diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile
new file mode 100644
index 0000000000..052f3f7c86
--- /dev/null
+++ b/drivers/tee/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_TEE) += tee.o
+tee-objs += tee_core.o
+tee-objs += tee_shm.o
+obj-$(CONFIG_HAVE_OPTEE) += optee/
diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
new file mode 100644
index 0000000000..3c791a10c4
--- /dev/null
+++ b/drivers/tee/optee/Kconfig
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# OP-TEE Trusted Execution Environment Configuration
+config OPTEE
+ tristate "OP-TEE communication"
+ select HAVE_OPTEE
+ select ARM_SMCCC
+ depends on MMU
+ help
+ This driver implements bidirectional communication with the OP-TEE
+ Trusted Execution Environment (TEE). OP-TEE is a Trusted OS designed
+ primarily to rely on the ARM TrustZone(R) technology as the
+ underlying hardware isolation mechanism.
+ This driver can request services from OP-TEE, but doesn't
+ yet provide a supplicant to handle Remote Procedure Calls (RPC).
+ For more information see: https://www.op-tee.org
+
+ This driver doesn't actually load OP-TEE. For that see
+ CONFIG_BOOTM_OPTEE and PBL_OPTEE.
+
+ If unsure, say n here.
+
+config OPTEE_DEVFS
+ bool "Provide /dev/tee0 interface"
+ depends on OPTEE && FS_DEVFS && EXPERIMENTAL
+ help
+ Userspace accesses OP-TEE via ioctls and mmaps of the /dev/tee0
+ device. This are no current in-tree users of this interface,
+ but it's useful for compiling libteeclient + optee_tests for
+ use inside barebox to verify proper operation of CONFIG_OPTEE.
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
new file mode 100644
index 0000000000..83f8e23b11
--- /dev/null
+++ b/drivers/tee/optee/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_HAVE_OPTEE) += of_fixup.o
+obj-$(CONFIG_OPTEE) += optee.o
+optee-objs += core.o
+optee-objs += call.o
+optee-objs += rpc.o
+optee-objs += device.o
+optee-objs += smc_abi.o
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
new file mode 100644
index 0000000000..7d949fdd1d
--- /dev/null
+++ b/drivers/tee/optee/call.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/types.h>
+#include "optee_private.h"
+
+#define MAX_ARG_PARAM_COUNT 6
+
+static struct optee_session *find_session(struct optee_context_data *ctxdata,
+ u32 session_id)
+{
+ struct optee_session *sess;
+
+ list_for_each_entry(sess, &ctxdata->sess_list, list_node)
+ if (sess->session_id == session_id)
+ return sess;
+
+ return NULL;
+}
+
+size_t optee_msg_arg_size(void)
+{
+ return OPTEE_MSG_GET_ARG_SIZE(MAX_ARG_PARAM_COUNT);
+}
+
+/**
+ * optee_get_msg_arg() - Provide shared memory for argument struct
+ * @ctx: Caller TEE context
+ * @num_params: Number of parameter to store
+ * @shm_ret: Shared memory buffer
+ *
+ * @returns a pointer to the argument struct in memory, else an ERR_PTR
+ */
+struct optee_msg_arg *optee_get_msg_arg(struct tee_context *ctx,
+ size_t num_params,
+ struct tee_shm **shm_ret)
+{
+
+ size_t sz = OPTEE_MSG_GET_ARG_SIZE(num_params);
+ struct optee_msg_arg *ma;
+ struct tee_shm *shm;
+
+ if (num_params > MAX_ARG_PARAM_COUNT)
+ return ERR_PTR(-EINVAL);
+
+ shm = tee_shm_alloc_priv_buf(ctx, sz);
+ if (IS_ERR(shm))
+ return ERR_CAST(shm);
+
+ ma = tee_shm_get_va(shm, 0);
+ if (IS_ERR(ma)) {
+ tee_shm_free(shm);
+ return ERR_CAST(ma);
+ }
+
+ memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params));
+ ma->num_params = num_params;
+
+ *shm_ret = shm;
+ return ma;
+}
+
+/**
+ * optee_free_msg_arg() - Free previsouly obtained shared memory
+ * @ctx: Caller TEE context
+ * @shm: Pointer returned when the shared memory was obtained
+ *
+ * This function frees the shared memory obtained with optee_get_msg_arg().
+ */
+void optee_free_msg_arg(struct tee_context *ctx,
+ struct tee_shm *shm)
+{
+ tee_shm_free(shm);
+}
+
+int optee_open_session(struct tee_context *ctx,
+ struct tee_ioctl_open_session_arg *arg,
+ struct tee_param *param)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_context_data *ctxdata = ctx->data;
+ struct tee_shm *shm;
+ struct optee_msg_arg *msg_arg;
+ struct optee_session *sess = NULL;
+ uuid_t client_uuid;
+ int rc;
+
+ /* +2 for the meta parameters added below */
+ msg_arg = optee_get_msg_arg(ctx, arg->num_params + 2, &shm);
+ if (IS_ERR(msg_arg))
+ return PTR_ERR(msg_arg);
+
+ msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION;
+
+ /*
+ * Initialize and add the meta parameters needed when opening a
+ * session.
+ */
+ msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
+ OPTEE_MSG_ATTR_META;
+ msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
+ OPTEE_MSG_ATTR_META;
+ memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid));
+ msg_arg->params[1].u.value.c = arg->clnt_login;
+
+ rc = tee_session_calc_client_uuid(&client_uuid, arg->clnt_login,
+ arg->clnt_uuid);
+ if (rc)
+ goto out;
+ export_uuid(msg_arg->params[1].u.octets, &client_uuid);
+
+ rc = optee->ops->to_msg_param(optee, msg_arg->params + 2,
+ arg->num_params, param);
+ if (rc)
+ goto out;
+
+ sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (optee->ops->do_call_with_arg(ctx, msg_arg)) {
+ msg_arg->ret = TEEC_ERROR_COMMUNICATION;
+ msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
+ }
+
+ if (msg_arg->ret == TEEC_SUCCESS) {
+ /* A new session has been created, add it to the list. */
+ sess->session_id = msg_arg->session;
+ list_add(&sess->list_node, &ctxdata->sess_list);
+ } else {
+ kfree(sess);
+ }
+
+ if (optee->ops->from_msg_param(optee, param, arg->num_params,
+ msg_arg->params + 2)) {
+ arg->ret = TEEC_ERROR_COMMUNICATION;
+ arg->ret_origin = TEEC_ORIGIN_COMMS;
+ /* Close session again to avoid leakage */
+ optee_close_session(ctx, msg_arg->session);
+ } else {
+ arg->session = msg_arg->session;
+ arg->ret = msg_arg->ret;
+ arg->ret_origin = msg_arg->ret_origin;
+ }
+
+out:
+ optee_free_msg_arg(ctx, shm);
+
+ return rc;
+}
+
+int optee_close_session_helper(struct tee_context *ctx, u32 session)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_msg_arg *msg_arg;
+ struct tee_shm *shm;
+
+ msg_arg = optee_get_msg_arg(ctx, 0, &shm);
+ if (IS_ERR(msg_arg))
+ return PTR_ERR(msg_arg);
+
+ msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
+ msg_arg->session = session;
+ optee->ops->do_call_with_arg(ctx, msg_arg);
+
+ optee_free_msg_arg(ctx, shm);
+
+ return 0;
+}
+
+int optee_close_session(struct tee_context *ctx, u32 session)
+{
+ struct optee_context_data *ctxdata = ctx->data;
+ struct optee_session *sess;
+
+ /* Check that the session is valid and remove it from the list */
+ sess = find_session(ctxdata, session);
+ if (sess)
+ list_del(&sess->list_node);
+ if (!sess)
+ return -EINVAL;
+ kfree(sess);
+
+ return optee_close_session_helper(ctx, session);
+}
+
+int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
+ struct tee_param *param)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_context_data *ctxdata = ctx->data;
+ struct optee_msg_arg *msg_arg;
+ struct optee_session *sess;
+ struct tee_shm *shm;
+ int rc;
+
+ /* Check that the session is valid */
+ sess = find_session(ctxdata, arg->session);
+ if (!sess)
+ return -EINVAL;
+
+ msg_arg = optee_get_msg_arg(ctx, arg->num_params,
+ &shm);
+ if (IS_ERR(msg_arg))
+ return PTR_ERR(msg_arg);
+ msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND;
+ msg_arg->func = arg->func;
+ msg_arg->session = arg->session;
+
+ rc = optee->ops->to_msg_param(optee, msg_arg->params, arg->num_params,
+ param);
+ if (rc)
+ goto out;
+
+ if (optee->ops->do_call_with_arg(ctx, msg_arg)) {
+ msg_arg->ret = TEEC_ERROR_COMMUNICATION;
+ msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
+ }
+
+ if (optee->ops->from_msg_param(optee, param, arg->num_params,
+ msg_arg->params)) {
+ msg_arg->ret = TEEC_ERROR_COMMUNICATION;
+ msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
+ }
+
+ arg->ret = msg_arg->ret;
+ arg->ret_origin = msg_arg->ret_origin;
+out:
+ optee_free_msg_arg(ctx, shm);
+ return rc;
+}
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
new file mode 100644
index 0000000000..753dc5552a
--- /dev/null
+++ b/drivers/tee/optee/core.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ * Copyright (c) 2016, EPAM Systems
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tee_drv.h>
+#include <linux/types.h>
+#include "optee_private.h"
+
+static void optee_release_helper(struct tee_context *ctx,
+ int (*close_session)(struct tee_context *ctx,
+ u32 session))
+{
+ struct optee_context_data *ctxdata = ctx->data;
+ struct optee_session *sess;
+ struct optee_session *sess_tmp;
+
+ if (!ctxdata)
+ return;
+
+ list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
+ list_node) {
+ list_del(&sess->list_node);
+ close_session(ctx, sess->session_id);
+ kfree(sess);
+ }
+ kfree(ctxdata);
+ ctx->data = NULL;
+}
+
+void optee_release(struct tee_context *ctx)
+{
+ optee_release_helper(ctx, optee_close_session_helper);
+}
+
+int optee_open(struct tee_context *ctx, bool cap_memref_null)
+{
+ struct optee_context_data *ctxdata;
+
+ ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
+ if (!ctxdata)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&ctxdata->sess_list);
+
+ ctx->cap_memref_null = cap_memref_null;
+ ctx->data = ctxdata;
+ return 0;
+}
+
+static int __init optee_core_init(void)
+{
+ return optee_smc_abi_register();
+}
+core_initcall(optee_core_init);
+
+MODULE_AUTHOR("Linaro");
+MODULE_DESCRIPTION("OP-TEE driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:optee");
diff --git a/drivers/tee/optee/device.c b/drivers/tee/optee/device.c
new file mode 100644
index 0000000000..100a877395
--- /dev/null
+++ b/drivers/tee/optee/device.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Linaro Ltd.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+#include "optee_private.h"
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+ return 1;
+ else
+ return 0;
+}
+
+static int get_devices(struct tee_context *ctx, u32 session,
+ struct tee_shm *device_shm, u32 *shm_size,
+ u32 func)
+{
+ int ret = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ inv_arg.func = func;
+ inv_arg.session = session;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param[0].u.memref.shm = device_shm;
+ param[0].u.memref.size = *shm_size;
+ param[0].u.memref.shm_offs = 0;
+
+ ret = tee_client_invoke_func(ctx, &inv_arg, param);
+ if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) &&
+ (inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) {
+ pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n",
+ inv_arg.ret);
+ return -EINVAL;
+ }
+
+ *shm_size = param[0].u.memref.size;
+
+ return 0;
+}
+
+static int optee_register_device(const uuid_t *device_uuid)
+{
+ struct tee_client_device *optee_device = NULL;
+ int rc;
+
+ optee_device = kzalloc(sizeof(*optee_device), GFP_KERNEL);
+ if (!optee_device)
+ return -ENOMEM;
+
+ optee_device->dev.bus = &tee_bus_type;
+ if (dev_set_name(&optee_device->dev, "optee-ta-%pUb", device_uuid)) {
+ kfree(optee_device);
+ return -ENOMEM;
+ }
+ uuid_copy(&optee_device->id.uuid, device_uuid);
+
+ rc = device_register(&optee_device->dev);
+ if (rc) {
+ pr_err("device registration failed, err: %d\n", rc);
+ put_device(&optee_device->dev);
+ }
+
+ return rc;
+}
+
+static int __optee_enumerate_devices(u32 func)
+{
+ const uuid_t pta_uuid =
+ UUID_INIT(0x7011a688, 0xddde, 0x4053,
+ 0xa5, 0xa9, 0x7b, 0x3c, 0x4d, 0xdf, 0x13, 0xb8);
+ struct tee_ioctl_open_session_arg sess_arg;
+ struct tee_shm *device_shm = NULL;
+ const uuid_t *device_uuid = NULL;
+ struct tee_context *ctx = NULL;
+ u32 shm_size = 0, idx, num_devices = 0;
+ int rc;
+
+ memset(&sess_arg, 0, sizeof(sess_arg));
+
+ /* Open context with OP-TEE driver */
+ ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
+ if (IS_ERR(ctx))
+ return -ENODEV;
+
+ /* Open session with device enumeration pseudo TA */
+ export_uuid(sess_arg.uuid, &pta_uuid);
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
+ sess_arg.num_params = 0;
+
+ rc = tee_client_open_session(ctx, &sess_arg, NULL);
+ if ((rc < 0) || (sess_arg.ret != TEEC_SUCCESS)) {
+ pr_debug("device enumeration pseudo TA not found\n");
+ rc = 0;
+ goto out_ctx;
+ }
+
+ rc = get_devices(ctx, sess_arg.session, NULL, &shm_size, func);
+ if (rc < 0)
+ goto out_sess;
+ if (!shm_size) {
+ pr_debug("device enumeration PTA found, but no devices!\n");
+ goto out_sess;
+ }
+
+ device_shm = tee_shm_alloc_kernel_buf(ctx, shm_size);
+ if (IS_ERR(device_shm)) {
+ pr_err("tee_shm_alloc_kernel_buf failed\n");
+ rc = PTR_ERR(device_shm);
+ goto out_sess;
+ }
+
+ rc = get_devices(ctx, sess_arg.session, device_shm, &shm_size, func);
+ if (rc < 0)
+ goto out_shm;
+
+ device_uuid = tee_shm_get_va(device_shm, 0);
+ if (IS_ERR(device_uuid)) {
+ pr_err("tee_shm_get_va failed\n");
+ rc = PTR_ERR(device_uuid);
+ goto out_shm;
+ }
+
+ num_devices = shm_size / sizeof(uuid_t);
+
+ for (idx = 0; idx < num_devices; idx++) {
+ rc = optee_register_device(&device_uuid[idx]);
+ if (rc)
+ goto out_shm;
+ }
+
+out_shm:
+ tee_shm_free(device_shm);
+out_sess:
+ tee_client_close_session(ctx, sess_arg.session);
+out_ctx:
+ tee_client_close_context(ctx);
+
+ return rc;
+}
+
+int optee_enumerate_devices(u32 func)
+{
+ return __optee_enumerate_devices(func);
+}
+
+static int __optee_unregister_device(struct device *dev, void *data)
+{
+ if (!strncmp(dev_name(dev), "optee-ta", strlen("optee-ta")))
+ device_unregister(dev);
+
+ return 0;
+}
+
+void optee_unregister_devices(void)
+{
+ bus_for_each_dev(&tee_bus_type, NULL, NULL,
+ __optee_unregister_device);
+}
diff --git a/drivers/tee/optee/of_fixup.c b/drivers/tee/optee/of_fixup.c
new file mode 100644
index 0000000000..e4d3c5f9b0
--- /dev/null
+++ b/drivers/tee/optee/of_fixup.c
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <of.h>
+#include <linux/ioport.h>
+#include <asm/barebox-arm.h>
+#include <asm/optee.h>
+#include <tee/optee.h>
+
+int of_optee_fixup(struct device_node *root, void *_data)
+{
+ struct of_optee_fixup_data *fixup_data = _data;
+ const char *optee_of_path = "/firmware/optee";
+ struct resource res = {};
+ struct device_node *node;
+ u64 optee_membase;
+ int ret;
+
+ if (of_find_node_by_path_from(root, optee_of_path))
+ return 0;
+
+ node = of_create_node(root, optee_of_path);
+ if (!node)
+ return -ENOMEM;
+
+ ret = of_property_write_string(node, "compatible", "linaro,optee-tz");
+ if (ret)
+ return ret;
+
+ ret = of_property_write_string(node, "method", fixup_data->method);
+ if (ret)
+ return ret;
+
+ if (!optee_get_membase(&optee_membase)) {
+ res.start = optee_membase;
+ res.end = optee_membase + OPTEE_SIZE - fixup_data->shm_size - 1;
+ } else {
+ res.start = arm_mem_endmem_get() - OPTEE_SIZE;
+ res.end = arm_mem_endmem_get() - fixup_data->shm_size - 1;
+ }
+ res.flags = IORESOURCE_BUSY;
+ res.name = "optee_core";
+
+ ret = of_fixup_reserved_memory(root, &res);
+ if (ret)
+ return ret;
+
+ if (!optee_get_membase(&optee_membase)) {
+ res.start = optee_membase + OPTEE_SIZE - fixup_data->shm_size;
+ res.end = optee_membase + OPTEE_SIZE - 1;
+ } else {
+ res.start = arm_mem_endmem_get() - fixup_data->shm_size;
+ res.end = arm_mem_endmem_get() - 1;
+ }
+ res.flags &= ~IORESOURCE_BUSY;
+ res.name = "optee_shm";
+
+ return of_fixup_reserved_memory(root, &res);
+}
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
new file mode 100644
index 0000000000..92878ce081
--- /dev/null
+++ b/drivers/tee/optee/optee_msg.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ */
+#ifndef _OPTEE_MSG_H
+#define _OPTEE_MSG_H
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+
+/*
+ * This file defines the OP-TEE message protocol (ABI) used to communicate
+ * with an instance of OP-TEE running in secure world.
+ *
+ * This file is divided into two sections.
+ * 1. Formatting of messages.
+ * 2. Requests from normal world
+ */
+
+/*****************************************************************************
+ * Part 1 - formatting of messages
+ *****************************************************************************/
+
+#define OPTEE_MSG_ATTR_TYPE_NONE 0x0
+#define OPTEE_MSG_ATTR_TYPE_VALUE_INPUT 0x1
+#define OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT 0x2
+#define OPTEE_MSG_ATTR_TYPE_VALUE_INOUT 0x3
+#define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5
+#define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6
+#define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7
+#define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9
+#define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa
+#define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb
+
+#define OPTEE_MSG_ATTR_TYPE_MASK GENMASK(7, 0)
+
+/*
+ * Meta parameter to be absorbed by the Secure OS and not passed
+ * to the Trusted Application.
+ *
+ * Currently only used with OPTEE_MSG_CMD_OPEN_SESSION.
+ */
+#define OPTEE_MSG_ATTR_META BIT(8)
+
+/*
+ * Pointer to a list of pages used to register user-defined SHM buffer.
+ * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*.
+ * buf_ptr should point to the beginning of the buffer. Buffer will contain
+ * list of page addresses. OP-TEE core can reconstruct contiguous buffer from
+ * that page addresses list. Page addresses are stored as 64 bit values.
+ * Last entry on a page should point to the next page of buffer.
+ * Every entry in buffer should point to a 4k page beginning (12 least
+ * significant bits must be equal to zero).
+ *
+ * 12 least significant bits of optee_msg_param.u.tmem.buf_ptr should hold
+ * page offset of user buffer.
+ *
+ * So, entries should be placed like members of this structure:
+ *
+ * struct page_data {
+ * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1];
+ * uint64_t next_page_data;
+ * };
+ *
+ * Structure is designed to exactly fit into the page size
+ * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page.
+ *
+ * The size of 4KB is chosen because this is the smallest page size for ARM
+ * architectures. If REE uses larger pages, it should divide them to 4KB ones.
+ */
+#define OPTEE_MSG_ATTR_NONCONTIG BIT(9)
+
+/*
+ * Memory attributes for caching passed with temp memrefs. The actual value
+ * used is defined outside the message protocol with the exception of
+ * OPTEE_MSG_ATTR_CACHE_PREDEFINED which means the attributes already
+ * defined for the memory range should be used. If optee_smc.h is used as
+ * bearer of this protocol OPTEE_SMC_SHM_* is used for values.
+ */
+#define OPTEE_MSG_ATTR_CACHE_SHIFT 16
+#define OPTEE_MSG_ATTR_CACHE_MASK GENMASK(2, 0)
+#define OPTEE_MSG_ATTR_CACHE_PREDEFINED 0
+
+/*
+ * Page size used in non-contiguous buffer entries
+ */
+#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
+
+/**
+ * struct optee_msg_param_tmem - temporary memory reference parameter
+ * @buf_ptr: Address of the buffer
+ * @size: Size of the buffer
+ * @shm_ref: Temporary shared memory reference, pointer to a struct tee_shm
+ *
+ * Secure and normal world communicates pointers as physical address
+ * instead of the virtual address. This is because secure and normal world
+ * have completely independent memory mapping. Normal world can even have a
+ * hypervisor which need to translate the guest physical address (AKA IPA
+ * in ARM documentation) to a real physical address before passing the
+ * structure to secure world.
+ */
+struct optee_msg_param_tmem {
+ u64 buf_ptr;
+ u64 size;
+ u64 shm_ref;
+};
+
+/**
+ * struct optee_msg_param_rmem - registered memory reference parameter
+ * @offs: Offset into shared memory reference
+ * @size: Size of the buffer
+ * @shm_ref: Shared memory reference, pointer to a struct tee_shm
+ */
+struct optee_msg_param_rmem {
+ u64 offs;
+ u64 size;
+ u64 shm_ref;
+};
+
+/**
+ * struct optee_msg_param_value - opaque value parameter
+ *
+ * Value parameters are passed unchecked between normal and secure world.
+ */
+struct optee_msg_param_value {
+ u64 a;
+ u64 b;
+ u64 c;
+};
+
+/**
+ * struct optee_msg_param - parameter used together with struct optee_msg_arg
+ * @attr: attributes
+ * @tmem: parameter by temporary memory reference
+ * @rmem: parameter by registered memory reference
+ * @value: parameter by opaque value
+ * @octets: parameter by octet string
+ *
+ * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
+ * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets,
+ * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
+ * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem.
+ * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
+ */
+struct optee_msg_param {
+ u64 attr;
+ union {
+ struct optee_msg_param_tmem tmem;
+ struct optee_msg_param_rmem rmem;
+ struct optee_msg_param_value value;
+ u8 octets[24];
+ } u;
+};
+
+/**
+ * struct optee_msg_arg - call argument
+ * @cmd: Command, one of OPTEE_MSG_CMD_* or OPTEE_MSG_RPC_CMD_*
+ * @func: Trusted Application function, specific to the Trusted Application,
+ * used if cmd == OPTEE_MSG_CMD_INVOKE_COMMAND
+ * @session: In parameter for all OPTEE_MSG_CMD_* except
+ * OPTEE_MSG_CMD_OPEN_SESSION where it's an output parameter instead
+ * @cancel_id: Cancellation id, a unique value to identify this request
+ * @ret: return value
+ * @ret_origin: origin of the return value
+ * @num_params: number of parameters supplied to the OS Command
+ * @params: the parameters supplied to the OS Command
+ *
+ * All normal calls to Trusted OS uses this struct. If cmd requires further
+ * information than what these fields hold it can be passed as a parameter
+ * tagged as meta (setting the OPTEE_MSG_ATTR_META bit in corresponding
+ * attrs field). All parameters tagged as meta have to come first.
+ */
+struct optee_msg_arg {
+ u32 cmd;
+ u32 func;
+ u32 session;
+ u32 cancel_id;
+ u32 pad;
+ u32 ret;
+ u32 ret_origin;
+ u32 num_params;
+
+ /* num_params tells the actual number of element in params */
+ struct optee_msg_param params[];
+};
+
+/**
+ * OPTEE_MSG_GET_ARG_SIZE - return size of struct optee_msg_arg
+ *
+ * @num_params: Number of parameters embedded in the struct optee_msg_arg
+ *
+ * Returns the size of the struct optee_msg_arg together with the number
+ * of embedded parameters.
+ */
+#define OPTEE_MSG_GET_ARG_SIZE(num_params) \
+ (sizeof(struct optee_msg_arg) + \
+ sizeof(struct optee_msg_param) * (num_params))
+
+/*****************************************************************************
+ * Part 2 - requests from normal world
+ *****************************************************************************/
+
+/*
+ * Return the following UID if using API specified in this file without
+ * further extensions:
+ * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b.
+ * Represented in 4 32-bit words in OPTEE_MSG_UID_0, OPTEE_MSG_UID_1,
+ * OPTEE_MSG_UID_2, OPTEE_MSG_UID_3.
+ */
+#define OPTEE_MSG_UID_0 0x384fb3e0
+#define OPTEE_MSG_UID_1 0xe7f811e3
+#define OPTEE_MSG_UID_2 0xaf630002
+#define OPTEE_MSG_UID_3 0xa5d5c51b
+#define OPTEE_MSG_FUNCID_CALLS_UID 0xFF01
+
+/*
+ * Returns 2.0 if using API specified in this file without further
+ * extensions. Represented in 2 32-bit words in OPTEE_MSG_REVISION_MAJOR
+ * and OPTEE_MSG_REVISION_MINOR
+ */
+#define OPTEE_MSG_REVISION_MAJOR 2
+#define OPTEE_MSG_REVISION_MINOR 0
+#define OPTEE_MSG_FUNCID_CALLS_REVISION 0xFF03
+
+/*
+ * Get UUID of Trusted OS.
+ *
+ * Used by non-secure world to figure out which Trusted OS is installed.
+ * Note that returned UUID is the UUID of the Trusted OS, not of the API.
+ *
+ * Returns UUID in 4 32-bit words in the same way as
+ * OPTEE_MSG_FUNCID_CALLS_UID described above.
+ */
+#define OPTEE_MSG_OS_OPTEE_UUID_0 0x486178e0
+#define OPTEE_MSG_OS_OPTEE_UUID_1 0xe7f811e3
+#define OPTEE_MSG_OS_OPTEE_UUID_2 0xbc5e0002
+#define OPTEE_MSG_OS_OPTEE_UUID_3 0xa5d5c51b
+#define OPTEE_MSG_FUNCID_GET_OS_UUID 0x0000
+
+/*
+ * Get revision of Trusted OS.
+ *
+ * Used by non-secure world to figure out which version of the Trusted OS
+ * is installed. Note that the returned revision is the revision of the
+ * Trusted OS, not of the API.
+ *
+ * Returns revision in 2 32-bit words in the same way as
+ * OPTEE_MSG_CALLS_REVISION described above.
+ */
+#define OPTEE_MSG_FUNCID_GET_OS_REVISION 0x0001
+
+/*
+ * Do a secure call with struct optee_msg_arg as argument
+ * The OPTEE_MSG_CMD_* below defines what goes in struct optee_msg_arg::cmd
+ *
+ * OPTEE_MSG_CMD_OPEN_SESSION opens a session to a Trusted Application.
+ * The first two parameters are tagged as meta, holding two value
+ * parameters to pass the following information:
+ * param[0].u.value.a-b uuid of Trusted Application
+ * param[1].u.value.a-b uuid of Client
+ * param[1].u.value.c Login class of client TEE_LOGIN_*
+ *
+ * OPTEE_MSG_CMD_INVOKE_COMMAND invokes a command a previously opened
+ * session to a Trusted Application. struct optee_msg_arg::func is Trusted
+ * Application function, specific to the Trusted Application.
+ *
+ * OPTEE_MSG_CMD_CLOSE_SESSION closes a previously opened session to
+ * Trusted Application.
+ *
+ * OPTEE_MSG_CMD_CANCEL cancels a currently invoked command.
+ *
+ * OPTEE_MSG_CMD_REGISTER_SHM registers a shared memory reference. The
+ * information is passed as:
+ * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_TMEM_INPUT
+ * [| OPTEE_MSG_ATTR_NONCONTIG]
+ * [in] param[0].u.tmem.buf_ptr physical address (of first fragment)
+ * [in] param[0].u.tmem.size size (of first fragment)
+ * [in] param[0].u.tmem.shm_ref holds shared memory reference
+ *
+ * OPTEE_MSG_CMD_UNREGISTER_SHM unregisters a previously registered shared
+ * memory reference. The information is passed as:
+ * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
+ * [in] param[0].u.rmem.shm_ref holds shared memory reference
+ * [in] param[0].u.rmem.offs 0
+ * [in] param[0].u.rmem.size 0
+ */
+#define OPTEE_MSG_CMD_OPEN_SESSION 0
+#define OPTEE_MSG_CMD_INVOKE_COMMAND 1
+#define OPTEE_MSG_CMD_CLOSE_SESSION 2
+#define OPTEE_MSG_CMD_CANCEL 3
+#define OPTEE_MSG_CMD_REGISTER_SHM 4
+#define OPTEE_MSG_CMD_UNREGISTER_SHM 5
+#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004
+
+#endif /* _OPTEE_MSG_H */
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
new file mode 100644
index 0000000000..637d3195be
--- /dev/null
+++ b/drivers/tee/optee/optee_private.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ */
+
+#ifndef OPTEE_PRIVATE_H
+#define OPTEE_PRIVATE_H
+
+#include <linux/arm-smccc.h>
+#include <linux/tee_drv.h>
+#include <linux/types.h>
+#include "optee_msg.h"
+
+#define DRIVER_NAME "optee"
+
+#define OPTEE_MAX_ARG_SIZE 1024
+
+/* Some Global Platform error codes used in this driver */
+#define TEEC_SUCCESS 0x00000000
+#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
+#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A
+#define TEEC_ERROR_COMMUNICATION 0xFFFF000E
+#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
+#define TEEC_ERROR_BUSY 0xFFFF000D
+#define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010
+
+#define TEEC_ORIGIN_COMMS 0x00000002
+
+/*
+ * This value should be larger than the number threads in secure world to
+ * meet the need from secure world. The number of threads in secure world
+ * are usually not even close to 255 so we should be safe for now.
+ */
+#define OPTEE_DEFAULT_MAX_NOTIF_VALUE 255
+
+typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long,
+ struct arm_smccc_res *);
+
+
+/*
+ * struct optee_smc - optee smc communication struct
+ * @invoke_fn handler function to invoke secure monitor
+ * @sec_caps: secure world capabilities defined by
+ * OPTEE_SMC_SEC_CAP_* in optee_smc.h
+ */
+struct optee_smc {
+ optee_invoke_fn *invoke_fn;
+ u32 sec_caps;
+};
+
+struct optee;
+
+/**
+ * struct optee_ops - OP-TEE driver internal operations
+ * @do_call_with_arg: enters OP-TEE in secure world
+ * @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters
+ * @from_msg_param: converts from OPTEE_MSG parameters to struct tee_param
+ *
+ * These OPs are only supposed to be used internally in the OP-TEE driver
+ * as a way of abstracting the different methogs of entering OP-TEE in
+ * secure world.
+ */
+struct optee_ops {
+ int (*do_call_with_arg)(struct tee_context *ctx,
+ struct optee_msg_arg *arg);
+ int (*to_msg_param)(struct optee *optee,
+ struct optee_msg_param *msg_params,
+ size_t num_params, const struct tee_param *params);
+ int (*from_msg_param)(struct optee *optee, struct tee_param *params,
+ size_t num_params,
+ const struct optee_msg_param *msg_params);
+};
+
+/**
+ * struct optee - main service struct
+ * @teedev: client device
+ * @ops: internal callbacks for different ways to reach secure
+ * world
+ * @ctx: driver internal TEE context
+ * @smc: specific to SMC ABI
+ */
+struct optee {
+ struct tee_device *teedev;
+ const struct optee_ops *ops;
+ struct tee_context *ctx;
+ union {
+ struct optee_smc smc;
+ };
+};
+
+struct optee_session {
+ struct list_head list_node;
+ u32 session_id;
+};
+
+struct optee_context_data {
+ /* Serializes access to this struct */
+ struct list_head sess_list;
+};
+
+struct optee_rpc_param {
+ u32 a0;
+ u32 a1;
+ u32 a2;
+ u32 a3;
+ u32 a4;
+ u32 a5;
+ u32 a6;
+ u32 a7;
+};
+
+int optee_open_session(struct tee_context *ctx,
+ struct tee_ioctl_open_session_arg *arg,
+ struct tee_param *param);
+int optee_close_session_helper(struct tee_context *ctx, u32 session);
+int optee_close_session(struct tee_context *ctx, u32 session);
+int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
+ struct tee_param *param);
+
+#define PTA_CMD_GET_DEVICES 0x0
+#define PTA_CMD_GET_DEVICES_SUPP 0x1
+int optee_enumerate_devices(u32 func);
+void optee_unregister_devices(void);
+
+int optee_open(struct tee_context *ctx, bool cap_memref_null);
+void optee_release(struct tee_context *ctx);
+
+static inline void optee_from_msg_param_value(struct tee_param *p, u32 attr,
+ const struct optee_msg_param *mp)
+{
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
+ attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
+ p->u.value.a = mp->u.value.a;
+ p->u.value.b = mp->u.value.b;
+ p->u.value.c = mp->u.value.c;
+}
+
+static inline void optee_to_msg_param_value(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+ mp->u.value.a = p->u.value.a;
+ mp->u.value.b = p->u.value.b;
+ mp->u.value.c = p->u.value.c;
+}
+
+struct optee_msg_arg *optee_get_msg_arg(struct tee_context *ctx,
+ size_t num_params,
+ struct tee_shm **shm_ret);
+void optee_free_msg_arg(struct tee_context *ctx,
+ struct tee_shm *shm);
+size_t optee_msg_arg_size(void);
+
+
+void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
+ struct optee_msg_arg *arg);
+
+/*
+ * Small helpers
+ */
+
+static inline void *reg_pair_to_ptr(u32 reg0, u32 reg1)
+{
+ return (void *)(unsigned long)(((u64)reg0 << 32) | reg1);
+}
+
+static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
+{
+ *reg0 = val >> 32;
+ *reg1 = val;
+}
+
+/* Registration of the ABIs */
+int optee_smc_abi_register(void);
+
+#endif /*OPTEE_PRIVATE_H*/
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
new file mode 100644
index 0000000000..b8e886b7e3
--- /dev/null
+++ b/drivers/tee/optee/optee_smc.h
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ */
+#ifndef OPTEE_SMC_H
+#define OPTEE_SMC_H
+
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+
+#define OPTEE_SMC_STD_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
+#define OPTEE_SMC_FAST_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
+
+/*
+ * Function specified by SMC Calling convention.
+ */
+#define OPTEE_SMC_FUNCID_CALLS_COUNT 0xFF00
+#define OPTEE_SMC_CALLS_COUNT \
+ ARM_SMCCC_CALL_VAL(OPTEE_SMC_FAST_CALL, SMCCC_SMC_32, \
+ SMCCC_OWNER_TRUSTED_OS_END, \
+ OPTEE_SMC_FUNCID_CALLS_COUNT)
+
+/*
+ * a0..a7 is used as register names in the descriptions below, on arm32
+ * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's
+ * 32-bit registers.
+ */
+
+/*
+ * Function specified by SMC Calling convention
+ *
+ * Return the following UID if using API specified in this file
+ * without further extensions:
+ * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b.
+ * see also OPTEE_MSG_UID_* in optee_msg.h
+ */
+#define OPTEE_SMC_FUNCID_CALLS_UID OPTEE_MSG_FUNCID_CALLS_UID
+#define OPTEE_SMC_CALLS_UID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_TRUSTED_OS_END, \
+ OPTEE_SMC_FUNCID_CALLS_UID)
+
+/*
+ * Function specified by SMC Calling convention
+ *
+ * Returns 2.0 if using API specified in this file without further extensions.
+ * see also OPTEE_MSG_REVISION_* in optee_msg.h
+ */
+#define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEE_MSG_FUNCID_CALLS_REVISION
+#define OPTEE_SMC_CALLS_REVISION \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_TRUSTED_OS_END, \
+ OPTEE_SMC_FUNCID_CALLS_REVISION)
+
+struct optee_smc_calls_revision_result {
+ unsigned long major;
+ unsigned long minor;
+ unsigned long reserved0;
+ unsigned long reserved1;
+};
+
+/*
+ * Get UUID of Trusted OS.
+ *
+ * Used by non-secure world to figure out which Trusted OS is installed.
+ * Note that returned UUID is the UUID of the Trusted OS, not of the API.
+ *
+ * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID
+ * described above.
+ */
+#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID
+#define OPTEE_SMC_CALL_GET_OS_UUID \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID)
+
+/*
+ * Get revision of Trusted OS.
+ *
+ * Used by non-secure world to figure out which version of the Trusted OS
+ * is installed. Note that the returned revision is the revision of the
+ * Trusted OS, not of the API.
+ *
+ * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION
+ * described above. May optionally return a 32-bit build identifier in a2,
+ * with zero meaning unspecified.
+ */
+#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION
+#define OPTEE_SMC_CALL_GET_OS_REVISION \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION)
+
+struct optee_smc_call_get_os_revision_result {
+ unsigned long major;
+ unsigned long minor;
+ unsigned long build_id;
+ unsigned long reserved1;
+};
+
+/*
+ * Call with struct optee_msg_arg as argument
+ *
+ * Call register usage:
+ * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG
+ * a1 Upper 32bit of a 64bit physical pointer to a struct optee_msg_arg
+ * a2 Lower 32bit of a 64bit physical pointer to a struct optee_msg_arg
+ * a3 Cache settings, not used if physical pointer is in a predefined shared
+ * memory area else per OPTEE_SMC_SHM_*
+ * a4-6 Not used
+ * a7 Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0 Return value, OPTEE_SMC_RETURN_*
+ * a1-3 Not used
+ * a4-7 Preserved
+ *
+ * OPTEE_SMC_RETURN_ETHREAD_LIMIT return register usage:
+ * a0 Return value, OPTEE_SMC_RETURN_ETHREAD_LIMIT
+ * a1-3 Preserved
+ * a4-7 Preserved
+ *
+ * RPC return register usage:
+ * a0 Return value, OPTEE_SMC_RETURN_IS_RPC(val)
+ * a1-2 RPC parameters
+ * a3-7 Resume information, must be preserved
+ *
+ * Possible return values:
+ * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
+ * function.
+ * OPTEE_SMC_RETURN_OK Call completed, result updated in
+ * the previously supplied struct
+ * optee_msg_arg.
+ * OPTEE_SMC_RETURN_ETHREAD_LIMIT Number of Trusted OS threads exceeded,
+ * try again later.
+ * OPTEE_SMC_RETURN_EBADADDR Bad physical pointer to struct
+ * optee_msg_arg.
+ * OPTEE_SMC_RETURN_EBADCMD Bad/unknown cmd in struct optee_msg_arg
+ * OPTEE_SMC_RETURN_IS_RPC() Call suspended by RPC call to normal
+ * world.
+ */
+#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG
+#define OPTEE_SMC_CALL_WITH_ARG \
+ OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG)
+
+/*
+ * Get Shared Memory Config
+ *
+ * Returns the Secure/Non-secure shared memory config.
+ *
+ * Call register usage:
+ * a0 SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG
+ * a1-6 Not used
+ * a7 Hypervisor Client ID register
+ *
+ * Have config return register usage:
+ * a0 OPTEE_SMC_RETURN_OK
+ * a1 Physical address of start of SHM
+ * a2 Size of SHM
+ * a3 Cache settings of memory, as defined by the
+ * OPTEE_SMC_SHM_* values above
+ * a4-7 Preserved
+ *
+ * Not available register usage:
+ * a0 OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-3 Not used
+ * a4-7 Preserved
+ */
+#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG 7
+#define OPTEE_SMC_GET_SHM_CONFIG \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG)
+
+struct optee_smc_get_shm_config_result {
+ unsigned long status;
+ unsigned long start;
+ unsigned long size;
+ unsigned long settings;
+};
+
+/*
+ * Exchanges capabilities between normal world and secure world
+ *
+ * Call register usage:
+ * a0 SMC Function ID, OPTEE_SMC_EXCHANGE_CAPABILITIES
+ * a1 bitfield of normal world capabilities OPTEE_SMC_NSEC_CAP_*
+ * a2-6 Not used
+ * a7 Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0 OPTEE_SMC_RETURN_OK
+ * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_*
+ * a2 The maximum secure world notification number
+ * a3 Bit[7:0]: Number of parameters needed for RPC to be supplied
+ * as the second MSG arg struct for
+ * OPTEE_SMC_CALL_WITH_ARG
+ * Bit[31:8]: Reserved (MBZ)
+ * a4-7 Preserved
+ *
+ * Error return register usage:
+ * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world
+ * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_*
+ * a2-7 Preserved
+ */
+/* Normal world works as a uniprocessor system */
+#define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR BIT(0)
+/* Secure world has reserved shared memory for normal world to use */
+#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0)
+/* Secure world can communicate via previously unregistered shared memory */
+#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1)
+
+/*
+ * Secure world supports commands "register/unregister shared memory",
+ * secure world accepts command buffers located in any parts of non-secure RAM
+ */
+#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2)
+/* Secure world supports Shared Memory with a NULL reference */
+#define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4)
+
+#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
+#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)
+
+struct optee_smc_exchange_capabilities_result {
+ unsigned long status;
+ unsigned long capabilities;
+ unsigned long max_notif_value;
+ unsigned long data;
+};
+
+/*
+ * Query OP-TEE about number of supported threads
+ *
+ * Normal World OS or Hypervisor issues this call to find out how many
+ * threads OP-TEE supports. That is how many standard calls can be issued
+ * in parallel before OP-TEE will return OPTEE_SMC_RETURN_ETHREAD_LIMIT.
+ *
+ * Call requests usage:
+ * a0 SMC Function ID, OPTEE_SMC_GET_THREAD_COUNT
+ * a1-6 Not used
+ * a7 Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0 OPTEE_SMC_RETURN_OK
+ * a1 Number of threads
+ * a2-7 Preserved
+ *
+ * Error return:
+ * a0 OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Requested call is not implemented
+ * a1-7 Preserved
+ */
+#define OPTEE_SMC_FUNCID_GET_THREAD_COUNT 15
+#define OPTEE_SMC_GET_THREAD_COUNT \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_THREAD_COUNT)
+
+/*
+ * Inform OP-TEE that normal world is able to receive asynchronous
+ * notifications.
+ *
+ * Call requests usage:
+ * a0 SMC Function ID, OPTEE_SMC_ENABLE_ASYNC_NOTIF
+ * a1-6 Not used
+ * a7 Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0 OPTEE_SMC_RETURN_OK
+ * a1-7 Preserved
+ *
+ * Not supported return register usage:
+ * a0 OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-7 Preserved
+ */
+#define OPTEE_SMC_FUNCID_ENABLE_ASYNC_NOTIF 16
+#define OPTEE_SMC_ENABLE_ASYNC_NOTIF \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_ASYNC_NOTIF)
+
+/*
+ * Retrieve a value of notifications pending since the last call of this
+ * function.
+ *
+ * OP-TEE keeps a record of all posted values. When an interrupt is
+ * received which indicates that there are posted values this function
+ * should be called until all pended values have been retrieved. When a
+ * value is retrieved, it's cleared from the record in secure world.
+ *
+ * It is expected that this function is called from an interrupt handler
+ * in normal world.
+ *
+ * Call requests usage:
+ * a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE
+ * a1-6 Not used
+ * a7 Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0 OPTEE_SMC_RETURN_OK
+ * a1 value
+ * a2 Bit[0]: OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID if the value in a1 is
+ * valid, else 0 if no values where pending
+ * a2 Bit[1]: OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING if another value is
+ * pending, else 0.
+ * Bit[31:2]: MBZ
+ * a3-7 Preserved
+ *
+ * Not supported return register usage:
+ * a0 OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-7 Preserved
+ */
+#define OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID BIT(0)
+#define OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING BIT(1)
+
+/*
+ * Notification that OP-TEE expects a yielding call to do some bottom half
+ * work in a driver.
+ */
+#define OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF 0
+
+#define OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE 17
+#define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \
+ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE)
+
+/* See OPTEE_SMC_CALL_WITH_REGD_ARG above */
+#define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG 19
+
+/*
+ * Resume from RPC (for example after processing a foreign interrupt)
+ *
+ * Call register usage:
+ * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC
+ * a1-3 Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned
+ * OPTEE_SMC_RETURN_RPC in a0
+ *
+ * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above.
+ *
+ * Possible return values
+ * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
+ * function.
+ * OPTEE_SMC_RETURN_OK Original call completed, result
+ * updated in the previously supplied.
+ * struct optee_msg_arg
+ * OPTEE_SMC_RETURN_RPC Call suspended by RPC call to normal
+ * world.
+ * OPTEE_SMC_RETURN_ERESUME Resume failed, the opaque resume
+ * information was corrupt.
+ */
+#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC 3
+#define OPTEE_SMC_CALL_RETURN_FROM_RPC \
+ OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC)
+
+#define OPTEE_SMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000
+#define OPTEE_SMC_RETURN_RPC_PREFIX 0xFFFF0000
+#define OPTEE_SMC_RETURN_RPC_FUNC_MASK 0x0000FFFF
+
+#define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \
+ ((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK)
+
+#define OPTEE_SMC_RPC_VAL(func) ((func) | OPTEE_SMC_RETURN_RPC_PREFIX)
+
+/*
+ * Allocate memory for RPC parameter passing. The memory is used to hold a
+ * struct optee_msg_arg.
+ *
+ * "Call" register usage:
+ * a0 This value, OPTEE_SMC_RETURN_RPC_ALLOC
+ * a1 Size in bytes of required argument memory
+ * a2 Not used
+ * a3 Resume information, must be preserved
+ * a4-5 Not used
+ * a6-7 Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1 Upper 32 bits of 64-bit physical pointer to allocated
+ * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
+ * be allocated.
+ * a2 Lower 32 bits of 64-bit physical pointer to allocated
+ * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
+ * be allocated
+ * a3 Preserved
+ * a4 Upper 32 bits of 64-bit Shared memory cookie used when freeing
+ * the memory or doing an RPC
+ * a5 Lower 32 bits of 64-bit Shared memory cookie used when freeing
+ * the memory or doing an RPC
+ * a6-7 Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_ALLOC 0
+#define OPTEE_SMC_RETURN_RPC_ALLOC \
+ OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC)
+
+/*
+ * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC
+ *
+ * "Call" register usage:
+ * a0 This value, OPTEE_SMC_RETURN_RPC_FREE
+ * a1 Upper 32 bits of 64-bit shared memory cookie belonging to this
+ * argument memory
+ * a2 Lower 32 bits of 64-bit shared memory cookie belonging to this
+ * argument memory
+ * a3-7 Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-2 Not used
+ * a3-7 Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_FREE 2
+#define OPTEE_SMC_RETURN_RPC_FREE \
+ OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE)
+
+/*
+ * Deliver a foreign interrupt in normal world.
+ *
+ * "Call" register usage:
+ * a0 OPTEE_SMC_RETURN_RPC_FOREIGN_INTR
+ * a1-7 Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-7 Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_FOREIGN_INTR 4
+#define OPTEE_SMC_RETURN_RPC_FOREIGN_INTR \
+ OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FOREIGN_INTR)
+
+/*
+ * Do an RPC request. The supplied struct optee_msg_arg tells which
+ * request to do and the parameters for the request. The following fields
+ * are used (the rest are unused):
+ * - cmd the Request ID
+ * - ret return value of the request, filled in by normal world
+ * - num_params number of parameters for the request
+ * - params the parameters
+ * - param_attrs attributes of the parameters
+ *
+ * "Call" register usage:
+ * a0 OPTEE_SMC_RETURN_RPC_CMD
+ * a1 Upper 32 bits of a 64-bit Shared memory cookie holding a
+ * struct optee_msg_arg, must be preserved, only the data should
+ * be updated
+ * a2 Lower 32 bits of a 64-bit Shared memory cookie holding a
+ * struct optee_msg_arg, must be preserved, only the data should
+ * be updated
+ * a3-7 Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-2 Not used
+ * a3-7 Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_CMD 5
+#define OPTEE_SMC_RETURN_RPC_CMD \
+ OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD)
+
+/* Returned in a0 */
+#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
+
+/* Returned in a0 only from Trusted OS functions */
+#define OPTEE_SMC_RETURN_OK 0x0
+#define OPTEE_SMC_RETURN_ETHREAD_LIMIT 0x1
+#define OPTEE_SMC_RETURN_EBUSY 0x2
+#define OPTEE_SMC_RETURN_ERESUME 0x3
+#define OPTEE_SMC_RETURN_EBADADDR 0x4
+#define OPTEE_SMC_RETURN_EBADCMD 0x5
+#define OPTEE_SMC_RETURN_ENOMEM 0x6
+#define OPTEE_SMC_RETURN_ENOTAVAIL 0x7
+#define OPTEE_SMC_RETURN_IS_RPC(ret) __optee_smc_return_is_rpc((ret))
+
+static inline bool __optee_smc_return_is_rpc(u32 ret)
+{
+ return ret != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION &&
+ (ret & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) ==
+ OPTEE_SMC_RETURN_RPC_PREFIX;
+}
+
+#endif /* OPTEE_SMC_H */
diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c
new file mode 100644
index 0000000000..3d0a7f2980
--- /dev/null
+++ b/drivers/tee/optee/rpc.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ */
+
+#define pr_fmt(fmt) "optee: " fmt
+
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+
+void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
+ struct optee_msg_arg *arg)
+{
+ pr_notice_once("optee: No supplicant or RPC handler for command 0x%x\n", arg->cmd);
+ arg->ret = TEEC_ERROR_NOT_SUPPORTED;
+}
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
new file mode 100644
index 0000000000..354a94a2f2
--- /dev/null
+++ b/drivers/tee/optee/smc_abi.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ * Copyright (c) 2016, EPAM Systems
+ */
+
+#define pr_fmt(fmt) "optee: smc_abi: " fmt
+
+#include <linux/arm-smccc.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <of.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tee_drv.h>
+#include <linux/types.h>
+#include "optee_private.h"
+#include "optee_smc.h"
+
+/*
+ * This file implement the SMC ABI used when communicating with secure world
+ * OP-TEE OS via raw SMCs.
+ * This file is divided into the following sections:
+ * 1. Convert between struct tee_param and struct optee_msg_param
+ * 2. Low level support functions to register shared memory in secure world
+ * 3. Do a normal scheduled call into secure world
+ * 4. Driver initialization.
+ */
+
+/*
+ * 1. Convert between struct tee_param and struct optee_msg_param
+ *
+ * optee_from_msg_param() and optee_to_msg_param() are the main
+ * functions.
+ */
+
+static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr,
+ const struct optee_msg_param *mp)
+{
+ struct tee_shm *shm;
+ phys_addr_t pa;
+ int rc;
+
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+ attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
+ p->u.memref.size = mp->u.tmem.size;
+ shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref;
+ if (!shm) {
+ p->u.memref.shm_offs = 0;
+ p->u.memref.shm = NULL;
+ return 0;
+ }
+
+ rc = tee_shm_get_pa(shm, 0, &pa);
+ if (rc)
+ return rc;
+
+ p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
+ p->u.memref.shm = shm;
+
+ return 0;
+}
+
+static void from_msg_param_reg_mem(struct tee_param *p, u32 attr,
+ const struct optee_msg_param *mp)
+{
+ struct tee_shm *shm;
+
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+ attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+ p->u.memref.size = mp->u.rmem.size;
+ shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref;
+
+ if (shm) {
+ p->u.memref.shm_offs = mp->u.rmem.offs;
+ p->u.memref.shm = shm;
+ } else {
+ p->u.memref.shm_offs = 0;
+ p->u.memref.shm = NULL;
+ }
+}
+
+/**
+ * optee_from_msg_param() - convert from OPTEE_MSG parameters to
+ * struct tee_param
+ * @optee: main service struct
+ * @params: subsystem internal parameter representation
+ * @num_params: number of elements in the parameter arrays
+ * @msg_params: OPTEE_MSG parameters
+ * Returns 0 on success or <0 on failure
+ */
+static int optee_from_msg_param(struct optee *optee, struct tee_param *params,
+ size_t num_params,
+ const struct optee_msg_param *msg_params)
+{
+ int rc;
+ size_t n;
+
+ for (n = 0; n < num_params; n++) {
+ struct tee_param *p = params + n;
+ const struct optee_msg_param *mp = msg_params + n;
+ u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
+
+ switch (attr) {
+ case OPTEE_MSG_ATTR_TYPE_NONE:
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+ memset(&p->u, 0, sizeof(p->u));
+ break;
+ case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
+ optee_from_msg_param_value(p, attr, mp);
+ break;
+ case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
+ rc = from_msg_param_tmp_mem(p, attr, mp);
+ if (rc)
+ return rc;
+ break;
+ case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
+ from_msg_param_reg_mem(p, attr, mp);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int to_msg_param_reg_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+ mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
+ mp->u.rmem.size = p->u.memref.size;
+ mp->u.rmem.offs = p->u.memref.shm_offs;
+ return 0;
+}
+
+static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ int rc;
+ phys_addr_t pa;
+
+ mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+ mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
+ mp->u.tmem.size = p->u.memref.size;
+
+ if (!p->u.memref.shm) {
+ mp->u.tmem.buf_ptr = 0;
+ return 0;
+ }
+
+ rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
+ if (rc)
+ return rc;
+
+ mp->u.tmem.buf_ptr = pa;
+ mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
+ OPTEE_MSG_ATTR_CACHE_SHIFT;
+
+ return 0;
+}
+
+/**
+ * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
+ * @optee: main service struct
+ * @msg_params: OPTEE_MSG parameters
+ * @num_params: number of elements in the parameter arrays
+ * @params: subsystem internal parameter representation
+ * Returns 0 on success or <0 on failure
+ */
+static int optee_to_msg_param(struct optee *optee,
+ struct optee_msg_param *msg_params,
+ size_t num_params, const struct tee_param *params)
+{
+ int rc;
+ size_t n;
+
+ for (n = 0; n < num_params; n++) {
+ const struct tee_param *p = params + n;
+ struct optee_msg_param *mp = msg_params + n;
+
+ switch (p->attr) {
+ case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+ mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+ memset(&mp->u, 0, sizeof(mp->u));
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+ optee_to_msg_param_value(mp, p);
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+ if (tee_shm_is_dynamic(p->u.memref.shm))
+ rc = to_msg_param_reg_mem(mp, p);
+ else
+ rc = to_msg_param_tmp_mem(mp, p);
+ if (rc)
+ return rc;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * 2. Low level support functions to register shared memory in secure world
+ *
+ * Functions to enable/disable shared memory caching in secure world, that
+ * is, lazy freeing of previously allocated shared memory. Freeing is
+ * performed when a request has been compled.
+ */
+
+#define PAGELIST_ENTRIES_PER_PAGE \
+ ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
+
+/**
+ * optee_alloc_and_init_page_list() - Provide page list of memory buffer
+ * @buf: Start of buffer
+ * @len: Length of buffer
+ * @phys_buf_ptr Physical pointer with coded offset to page list
+ *
+ * Secure world doesn't share mapping with Normal world (barebox in this case)
+ * so physical pointers are needed when sharing pointers.
+ *
+ * Returns a pointer page list on success or NULL on failure
+ */
+static void *optee_alloc_and_init_page_list(void *buf, unsigned long len, u64 *phys_buf_ptr)
+{
+ const unsigned int page_size = OPTEE_MSG_NONCONTIG_PAGE_SIZE;
+ const phys_addr_t page_mask = page_size - 1;
+ u8 *buf_base;
+ unsigned int page_offset;
+ unsigned int num_pages;
+ unsigned int list_size;
+ unsigned int n;
+ void *page_list;
+ struct {
+ u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
+ u64 next_page_data;
+ } *pages_data;
+
+ /*
+ * A Memory buffer is described in chunks of 4k. The list of
+ * physical addresses has to be represented by a physical pointer
+ * too and a single list has to start at a 4k page and fit into
+ * that page. In order to be able to describe large memory buffers
+ * these 4k pages carrying physical addresses are linked together
+ * in a list. See OPTEE_MSG_ATTR_NONCONTIG in
+ * drivers/tee/optee/optee_msg.h for more information.
+ */
+
+ page_offset = (unsigned long)buf & page_mask;
+ num_pages = roundup(page_offset + len, page_size) / page_size;
+ list_size = DIV_ROUND_UP(num_pages, PAGELIST_ENTRIES_PER_PAGE) *
+ page_size;
+ page_list = memalign(page_size, list_size);
+ if (!page_list)
+ return NULL;
+
+ pages_data = page_list;
+ buf_base = (u8 *)rounddown((unsigned long)buf, page_size);
+ n = 0;
+ while (num_pages) {
+ pages_data->pages_list[n] = virt_to_phys(buf_base);
+ n++;
+ buf_base += page_size;
+ num_pages--;
+
+ if (n == PAGELIST_ENTRIES_PER_PAGE) {
+ pages_data->next_page_data =
+ virt_to_phys(pages_data + 1);
+ pages_data++;
+ n = 0;
+ }
+ }
+
+ *phys_buf_ptr = virt_to_phys(page_list) | page_offset;
+ return page_list;
+}
+
+static int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_msg_arg *msg_arg;
+ struct tee_shm *shm_arg;
+ u64 *pages_list;
+ u64 ph_ptr;
+ int rc = 0;
+
+ pages_list = optee_alloc_and_init_page_list(shm->kaddr, shm->size, &ph_ptr);
+ if (!pages_list)
+ return -ENOMEM;
+
+ /*
+ * We don't use a cache for shared memory allocation like Linux,
+ * so it's safe to directly call optee_get_msg_arg here.
+ */
+ msg_arg = optee_get_msg_arg(ctx, 1, &shm_arg);
+ if (IS_ERR(msg_arg)) {
+ rc = PTR_ERR(msg_arg);
+ goto free_pages_list;
+ }
+
+ msg_arg->num_params = 1;
+ msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
+ msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
+ OPTEE_MSG_ATTR_NONCONTIG;
+ msg_arg->params->u.tmem.buf_ptr = ph_ptr;
+ msg_arg->params->u.tmem.shm_ref = (unsigned long)shm;
+ msg_arg->params->u.tmem.size = tee_shm_get_size(shm);
+
+ if (optee->ops->do_call_with_arg(ctx, msg_arg) ||
+ msg_arg->ret != TEEC_SUCCESS)
+ rc = -EINVAL;
+
+ optee_free_msg_arg(ctx, shm_arg);
+free_pages_list:
+ free(pages_list);
+
+ return rc;
+}
+
+static int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_msg_arg *msg_arg;
+ struct tee_shm *shm_arg;
+ int rc = 0;
+
+ /*
+ * We don't use a cache for shared memory allocation like Linux,
+ * so it's safe to directly call optee_get_msg_arg here.
+ */
+ msg_arg = optee_get_msg_arg(ctx, 1, &shm_arg);
+ if (IS_ERR(msg_arg))
+ return PTR_ERR(msg_arg);
+
+ msg_arg->num_params = 1;
+ msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
+ msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+ msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm;
+
+ if (optee->ops->do_call_with_arg(ctx, msg_arg) ||
+ msg_arg->ret != TEEC_SUCCESS)
+ rc = -EINVAL;
+
+ optee_free_msg_arg(ctx, shm_arg);
+ return rc;
+}
+
+/*
+ * 3. Do a normal scheduled call into secure world
+ *
+ * The function optee_smc_do_call_with_arg() performs a normal scheduled
+ * call into secure world. During this call may normal world request help
+ * from normal world using RPCs, Remote Procedure Calls. This includes
+ * delivery of non-secure interrupts to for instance allow rescheduling of
+ * the current task.
+ */
+
+
+/**
+ * optee_handle_rpc() - handle RPC from secure world
+ * @ctx: context doing the RPC
+ * @param: value of registers for the RPC
+ * @call_ctx: call context. Preserved during one OP-TEE invocation
+ *
+ * Result of RPC is written back into @param.
+ */
+static void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
+ void *page_list)
+{
+ struct tee_device *teedev = ctx->teedev;
+ struct optee *optee = tee_get_drvdata(teedev);
+ struct optee_msg_arg *arg;
+ struct tee_shm *shm;
+ phys_addr_t pa;
+
+ switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
+ case OPTEE_SMC_RPC_FUNC_ALLOC:
+ shm = tee_shm_alloc_priv_buf(optee->ctx, param->a1);
+ if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) {
+ reg_pair_from_64(&param->a1, &param->a2, pa);
+ /* "cookie" */
+ reg_pair_from_64(&param->a4, &param->a5,
+ (unsigned long)shm);
+ } else {
+ param->a1 = 0;
+ param->a2 = 0;
+ param->a4 = 0;
+ param->a5 = 0;
+ }
+ break;
+ case OPTEE_SMC_RPC_FUNC_FREE:
+ shm = reg_pair_to_ptr(param->a1, param->a2);
+ tee_shm_free(shm);
+ break;
+ case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR:
+ break;
+ case OPTEE_SMC_RPC_FUNC_CMD:
+ shm = reg_pair_to_ptr(param->a1, param->a2);
+ arg = tee_shm_get_va(shm, 0);
+ if (IS_ERR(arg)) {
+ pr_err("%s: tee_shm_get_va %p failed\n",
+ __func__, shm);
+ break;
+ }
+
+ optee_rpc_cmd(ctx, optee, arg);
+ break;
+ default:
+ pr_warn("Unknown RPC func 0x%x\n",
+ (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0));
+ break;
+ }
+
+ param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
+}
+
+/**
+ * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world
+ * @ctx: calling context
+ * @arg: argument to pass to secure world
+ *
+ * Does and SMC to OP-TEE in secure world and handles eventual resulting
+ * Remote Procedure Calls (RPC) from OP-TEE.
+ *
+ * Returns return code from secure world, 0 is OK
+ */
+static int optee_smc_do_call_with_arg(struct tee_context *ctx,
+ struct optee_msg_arg *arg)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_rpc_param param = { .a0 = OPTEE_SMC_CALL_WITH_ARG };
+ void *page_list = NULL;
+
+ reg_pair_from_64(&param.a1, &param.a2, virt_to_phys(arg));
+ while (true) {
+ struct arm_smccc_res res;
+
+ /* MMU will always be enabled at this moment and with matching caching
+ * attributes, we need not worry about flushing
+ */
+
+ optee->smc.invoke_fn(param.a0, param.a1, param.a2, param.a3,
+ param.a4, param.a5, param.a6, param.a7, &res);
+
+ free(page_list);
+ page_list = NULL;
+
+ if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
+ param.a0 = res.a0;
+ param.a1 = res.a1;
+ param.a2 = res.a2;
+ param.a3 = res.a3;
+ optee_handle_rpc(ctx, &param, &page_list);
+ } else {
+ return res.a0;
+ }
+ }
+}
+
+/*
+ * 4. Driver initialization
+ *
+ * During driver initialization is secure world probed to find out which
+ * features it supports so the driver can be initialized with a matching
+ * configuration. This involves for instance support for dynamic shared
+ * memory instead of a static memory carvout.
+ */
+
+static void optee_get_version(struct tee_device *teedev,
+ struct tee_ioctl_version_data *vers)
+{
+ struct tee_ioctl_version_data v = {
+ .impl_id = TEE_IMPL_ID_OPTEE,
+ .impl_caps = TEE_OPTEE_CAP_TZ,
+ .gen_caps = TEE_GEN_CAP_GP,
+ };
+ struct optee *optee = tee_get_drvdata(teedev);
+
+ if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
+ v.gen_caps |= TEE_GEN_CAP_REG_MEM;
+ if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
+ v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL;
+ *vers = v;
+}
+
+static int optee_smc_open(struct tee_context *ctx)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ u32 sec_caps = optee->smc.sec_caps;
+
+ return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL);
+}
+
+static const struct tee_driver_ops optee_clnt_ops = {
+ .get_version = optee_get_version,
+ .open = optee_smc_open,
+ .release = optee_release,
+ .open_session = optee_open_session,
+ .close_session = optee_close_session,
+ .invoke_func = optee_invoke_func,
+ .shm_register = optee_shm_register,
+ .shm_unregister = optee_shm_unregister,
+};
+
+static const struct tee_desc optee_clnt_desc = {
+ .name = DRIVER_NAME "-clnt",
+ .ops = &optee_clnt_ops,
+ .owner = THIS_MODULE,
+};
+
+static const struct optee_ops optee_ops = {
+ .do_call_with_arg = optee_smc_do_call_with_arg,
+ .to_msg_param = optee_to_msg_param,
+ .from_msg_param = optee_from_msg_param,
+};
+
+static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
+{
+ struct arm_smccc_res res;
+
+ invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
+
+ if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
+ res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
+ return true;
+ return false;
+}
+
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+{
+ union {
+ struct arm_smccc_res smccc;
+ struct optee_smc_call_get_os_revision_result result;
+ } res = {
+ .result = {
+ .build_id = 0
+ }
+ };
+
+ invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
+ &res.smccc);
+
+ if (res.result.build_id)
+ pr_info("revision %lu.%lu (%08lx)\n", res.result.major,
+ res.result.minor, res.result.build_id);
+ else
+ pr_info("revision %lu.%lu\n", res.result.major, res.result.minor);
+}
+
+static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
+{
+ union {
+ struct arm_smccc_res smccc;
+ struct optee_smc_calls_revision_result result;
+ } res;
+
+ invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
+
+ if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
+ (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
+ return true;
+ return false;
+}
+
+static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
+ u32 *sec_caps)
+{
+ union {
+ struct arm_smccc_res smccc;
+ struct optee_smc_exchange_capabilities_result result;
+ } res;
+
+ invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES,
+ OPTEE_SMC_NSEC_CAP_UNIPROCESSOR, 0, 0, 0, 0, 0, 0,
+ &res.smccc);
+
+ if (res.result.status != OPTEE_SMC_RETURN_OK)
+ return false;
+
+ *sec_caps = res.result.capabilities;
+
+ return true;
+}
+
+/* Simple wrapper functions to be able to use a function pointer */
+static void optee_smccc_smc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+static optee_invoke_fn *get_invoke_func(struct device *dev)
+{
+ const char *method;
+
+ pr_info("probing for conduit method.\n");
+
+ if (of_property_read_string(dev->of_node, "method", &method)) {
+ pr_warn("missing \"method\" property\n");
+ return ERR_PTR(-ENXIO);
+ }
+
+ if (!strcmp("hvc", method))
+ return optee_smccc_hvc;
+ else if (!strcmp("smc", method))
+ return optee_smccc_smc;
+
+ pr_warn("invalid \"method\" property: %s\n", method);
+ return ERR_PTR(-EINVAL);
+}
+
+static int optee_probe(struct device *dev)
+{
+ optee_invoke_fn *invoke_fn;
+ struct optee *optee = NULL;
+ struct tee_device *teedev;
+ struct tee_context *ctx;
+ u32 sec_caps;
+ int rc;
+
+ invoke_fn = get_invoke_func(dev);
+ if (IS_ERR(invoke_fn))
+ return PTR_ERR(invoke_fn);
+
+ if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
+ pr_warn("api uid mismatch\n");
+ return -EINVAL;
+ }
+
+ optee_msg_get_os_revision(invoke_fn);
+
+ if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
+ pr_warn("api revision mismatch\n");
+ return -EINVAL;
+ }
+
+ if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
+ pr_warn("capabilities mismatch\n");
+ return -EINVAL;
+ }
+
+ /*
+ * OP-TEE can use both shared memory via predefined pool or as
+ * dynamic shared memory provided by normal world. To keep things
+ * simple we're only using dynamic shared memory in this driver.
+ */
+ if (!(sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)) {
+ pr_err("driver requires OP-TEE dynamic shared memory support\n");
+ return -ENOSYS;
+ }
+
+ optee = kzalloc(sizeof(*optee), GFP_KERNEL);
+ if (!optee)
+ return -ENOMEM;
+
+ optee->ops = &optee_ops;
+ optee->smc.invoke_fn = invoke_fn;
+ optee->smc.sec_caps = sec_caps;
+
+ teedev = tee_device_alloc(&optee_clnt_desc, dev, optee);
+ if (IS_ERR(teedev)) {
+ rc = PTR_ERR(teedev);
+ goto err_free_optee;
+ }
+ optee->teedev = teedev;
+
+ rc = tee_device_register(optee->teedev);
+ if (rc)
+ goto err_release_teedev;
+
+ ctx = teedev_open(optee->teedev);
+ if (IS_ERR(ctx)) {
+ rc = PTR_ERR(ctx);
+ goto err_unreg_teedev;
+ }
+ optee->ctx = ctx;
+ if (rc)
+ goto err_close_ctx;
+
+ rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
+ if (!rc)
+ rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP);
+ if (rc)
+ goto err_optee_unregister_devices;
+
+ pr_debug("initialized driver with dynamic shared memory\n");
+ return 0;
+
+err_optee_unregister_devices:
+ optee_unregister_devices();
+err_close_ctx:
+ teedev_close_context(ctx);
+err_unreg_teedev:
+ tee_device_unregister(optee->teedev);
+err_release_teedev:
+ tee_device_release(optee->teedev);
+err_free_optee:
+ kfree(optee);
+ return rc;
+}
+
+static const struct of_device_id optee_dt_match[] = {
+ { .compatible = "linaro,optee-tz" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, optee_dt_match);
+
+static struct driver optee_driver = {
+ .probe = optee_probe,
+ .name = "optee",
+ .of_match_table = optee_dt_match,
+};
+
+int optee_smc_abi_register(void)
+{
+ return platform_driver_register(&optee_driver);
+}
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
new file mode 100644
index 0000000000..45fa9b5670
--- /dev/null
+++ b/drivers/tee/tee_core.c
@@ -0,0 +1,788 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2016, Linaro Limited
+ */
+
+#define pr_fmt(fmt) "tee_core: " fmt
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uaccess.h>
+#include <linux/printk.h>
+#include "tee_private.h"
+
+#define TEE_NUM_DEVICES 32
+
+#define TEE_IOCTL_PARAM_SIZE(x) (sizeof(struct tee_param) * (x))
+
+static LIST_HEAD(tee_clients);
+
+struct tee_context *teedev_open(struct tee_device *teedev)
+{
+ struct tee_context *ctx;
+ int rc;
+
+ if (!tee_device_get(teedev))
+ return ERR_PTR(-EINVAL);
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&ctx->refcount);
+ ctx->teedev = teedev;
+ rc = teedev->desc->ops->open(ctx);
+ if (rc)
+ goto err;
+
+ INIT_LIST_HEAD(&ctx->list_shm);
+
+ pr_debug("%s ctx=%p teedev=%p\n", __func__, ctx, teedev);
+
+ return ctx;
+err:
+ kfree(ctx);
+ tee_device_put(teedev);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(teedev_open);
+
+void teedev_ctx_get(struct tee_context *ctx)
+{
+ if (ctx->releasing)
+ return;
+
+ kref_get(&ctx->refcount);
+}
+
+static void teedev_ctx_release(struct kref *ref)
+{
+ struct tee_context *ctx = container_of(ref, struct tee_context,
+ refcount);
+ ctx->releasing = true;
+ ctx->teedev->desc->ops->release(ctx);
+ kfree(ctx);
+}
+
+void teedev_ctx_put(struct tee_context *ctx)
+{
+ if (ctx->releasing)
+ return;
+
+ kref_put(&ctx->refcount, teedev_ctx_release);
+}
+
+void teedev_close_context(struct tee_context *ctx)
+{
+ struct tee_device *teedev = ctx->teedev;
+
+ teedev_ctx_put(ctx);
+ tee_device_put(teedev);
+}
+EXPORT_SYMBOL_GPL(teedev_close_context);
+
+static int tee_open(struct cdev *cdev, unsigned long flags)
+{
+ struct tee_context *ctx;
+
+ if (cdev->priv)
+ return -EBUSY;
+
+ ctx = teedev_open(container_of(cdev, struct tee_device, cdev));
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ cdev->priv = ctx;
+
+ return 0;
+}
+
+static int tee_release(struct cdev *cdev)
+{
+ struct tee_context *ctx = cdev->priv;
+
+ teedev_close_context(ctx);
+ cdev->priv = NULL;
+
+ return 0;
+}
+
+int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method,
+ const u8 connection_data[TEE_IOCTL_UUID_LEN])
+{
+ /* Linux could generate a UUIDv5 here out of UID or GID, but in barebox,
+ * we just mimic what it would do for LOGIN_PUBLIC and LOGIN_REE_KERNEL,
+ * namely pass the nil UUID into the TEE environment
+ */
+
+ uuid_copy(uuid, &uuid_null);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(tee_session_calc_client_uuid);
+
+static int tee_ioctl_version(struct tee_context *ctx,
+ struct tee_ioctl_version_data __user *uvers)
+{
+ struct tee_ioctl_version_data vers;
+
+ ctx->teedev->desc->ops->get_version(ctx->teedev, &vers);
+
+ if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED)
+ vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED;
+
+ if (copy_to_user(uvers, &vers, sizeof(vers)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int tee_ioctl_shm_alloc(struct tee_context *ctx,
+ struct tee_ioctl_shm_alloc_data *data)
+{
+ struct tee_shm *shm;
+
+ /* Currently no input flags are supported */
+ if (data->flags)
+ return -EINVAL;
+
+ shm = tee_shm_alloc_user_buf(ctx, data->size);
+ if (IS_ERR(shm))
+ return PTR_ERR(shm);
+
+ data->id = shm->dev.id;
+ data->flags = shm->flags;
+ data->size = shm->size;
+
+ return tee_shm_get_fd(shm);
+}
+
+static int
+tee_ioctl_shm_register(struct tee_context *ctx,
+ struct tee_ioctl_shm_register_data *data)
+{
+ struct tee_shm *shm;
+
+ /* Currently no input flags are supported */
+ if (data->flags)
+ return -EINVAL;
+
+ shm = tee_shm_register_user_buf(ctx, data->addr, data->length);
+ if (IS_ERR(shm))
+ return PTR_ERR(shm);
+
+ data->id = shm->dev.id;
+ data->flags = shm->flags;
+ data->length = shm->size;
+
+ return tee_shm_get_fd(shm);
+}
+
+static int params_from_user(struct tee_context *ctx, struct tee_param *params,
+ size_t num_params,
+ struct tee_ioctl_param __user *uparams)
+{
+ size_t n;
+
+ for (n = 0; n < num_params; n++) {
+ struct tee_shm *shm;
+ struct tee_ioctl_param ip;
+
+ if (copy_from_user(&ip, uparams + n, sizeof(ip)))
+ return -EFAULT;
+
+ /* All unused attribute bits has to be zero */
+ if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
+ return -EINVAL;
+
+ params[n].attr = ip.attr;
+ switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
+ case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+ params[n].u.value.a = ip.a;
+ params[n].u.value.b = ip.b;
+ params[n].u.value.c = ip.c;
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+ /*
+ * If a NULL pointer is passed to a TA in the TEE,
+ * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL
+ * indicating a NULL memory reference.
+ */
+ if (ip.c != TEE_MEMREF_NULL) {
+ /*
+ * If we fail to get a pointer to a shared
+ * memory object (and increase the ref count)
+ * from an identifier we return an error. All
+ * pointers that has been added in params have
+ * an increased ref count. It's the callers
+ * responibility to do tee_shm_put() on all
+ * resolved pointers.
+ */
+ shm = tee_shm_get_from_id(ctx, ip.c);
+ if (IS_ERR(shm))
+ return PTR_ERR(shm);
+
+ /*
+ * Ensure offset + size does not overflow
+ * offset and does not overflow the size of
+ * the referred shared memory object.
+ */
+ if ((ip.a + ip.b) < ip.a ||
+ (ip.a + ip.b) > shm->size) {
+ tee_shm_put(shm);
+ return -EINVAL;
+ }
+ } else if (ctx->cap_memref_null) {
+ /* Pass NULL pointer to OP-TEE */
+ shm = NULL;
+ } else {
+ return -EINVAL;
+ }
+
+ params[n].u.memref.shm_offs = ip.a;
+ params[n].u.memref.size = ip.b;
+ params[n].u.memref.shm = shm;
+ break;
+ default:
+ /* Unknown attribute */
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int params_to_user(struct tee_ioctl_param __user *uparams,
+ size_t num_params, struct tee_param *params)
+{
+ size_t n;
+
+ for (n = 0; n < num_params; n++) {
+ struct tee_ioctl_param __user *up = uparams + n;
+ struct tee_param *p = params + n;
+
+ switch (p->attr) {
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+ if (put_user(p->u.value.a, &up->a) ||
+ put_user(p->u.value.b, &up->b) ||
+ put_user(p->u.value.c, &up->c))
+ return -EFAULT;
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+ if (put_user((u64)p->u.memref.size, &up->b))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static int tee_ioctl_open_session(struct tee_context *ctx,
+ struct tee_ioctl_buf_data __user *ubuf)
+{
+ int rc;
+ size_t n;
+ struct tee_ioctl_buf_data buf;
+ struct tee_ioctl_open_session_arg __user *uarg;
+ struct tee_ioctl_open_session_arg arg;
+ struct tee_ioctl_param __user *uparams = NULL;
+ struct tee_param *params = NULL;
+ bool have_session = false;
+
+ if (!ctx->teedev->desc->ops->open_session)
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, sizeof(buf)))
+ return -EFAULT;
+
+ if (buf.buf_len > TEE_MAX_ARG_SIZE ||
+ buf.buf_len < sizeof(struct tee_ioctl_open_session_arg))
+ return -EINVAL;
+
+ uarg = u64_to_user_ptr(buf.buf_ptr);
+ if (copy_from_user(&arg, uarg, sizeof(arg)))
+ return -EFAULT;
+
+ if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len)
+ return -EINVAL;
+
+ if (arg.num_params) {
+ params = kcalloc(arg.num_params, sizeof(struct tee_param),
+ GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ uparams = uarg->params;
+ rc = params_from_user(ctx, params, arg.num_params, uparams);
+ if (rc)
+ goto out;
+ }
+
+ if (arg.clnt_login >= TEE_IOCTL_LOGIN_REE_KERNEL_MIN &&
+ arg.clnt_login <= TEE_IOCTL_LOGIN_REE_KERNEL_MAX) {
+ pr_debug("login method not allowed for user-space client\n");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params);
+ if (rc)
+ goto out;
+ have_session = true;
+
+ if (put_user(arg.session, &uarg->session) ||
+ put_user(arg.ret, &uarg->ret) ||
+ put_user(arg.ret_origin, &uarg->ret_origin)) {
+ rc = -EFAULT;
+ goto out;
+ }
+ rc = params_to_user(uparams, arg.num_params, params);
+out:
+ /*
+ * If we've succeeded to open the session but failed to communicate
+ * it back to user space, close the session again to avoid leakage.
+ */
+ if (rc && have_session && ctx->teedev->desc->ops->close_session)
+ ctx->teedev->desc->ops->close_session(ctx, arg.session);
+
+ if (params) {
+ /* Decrease ref count for all valid shared memory pointers */
+ for (n = 0; n < arg.num_params; n++)
+ if (tee_param_is_memref(params + n) &&
+ params[n].u.memref.shm)
+ tee_shm_put(params[n].u.memref.shm);
+ kfree(params);
+ }
+
+ return rc;
+}
+
+static int tee_ioctl_invoke(struct tee_context *ctx,
+ struct tee_ioctl_buf_data __user *ubuf)
+{
+ int rc;
+ size_t n;
+ struct tee_ioctl_buf_data buf;
+ struct tee_ioctl_invoke_arg __user *uarg;
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_ioctl_param __user *uparams = NULL;
+ struct tee_param *params = NULL;
+
+ if (!ctx->teedev->desc->ops->invoke_func)
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, sizeof(buf)))
+ return -EFAULT;
+
+ if (buf.buf_len > TEE_MAX_ARG_SIZE ||
+ buf.buf_len < sizeof(struct tee_ioctl_invoke_arg))
+ return -EINVAL;
+
+ uarg = u64_to_user_ptr(buf.buf_ptr);
+ if (copy_from_user(&arg, uarg, sizeof(arg)))
+ return -EFAULT;
+
+ if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len)
+ return -EINVAL;
+
+ if (arg.num_params) {
+ params = kcalloc(arg.num_params, sizeof(struct tee_param),
+ GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ uparams = uarg->params;
+ rc = params_from_user(ctx, params, arg.num_params, uparams);
+ if (rc)
+ goto out;
+ }
+
+ rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params);
+ if (rc)
+ goto out;
+
+ if (put_user(arg.ret, &uarg->ret) ||
+ put_user(arg.ret_origin, &uarg->ret_origin)) {
+ rc = -EFAULT;
+ goto out;
+ }
+ rc = params_to_user(uparams, arg.num_params, params);
+out:
+ if (params) {
+ /* Decrease ref count for all valid shared memory pointers */
+ for (n = 0; n < arg.num_params; n++)
+ if (tee_param_is_memref(params + n) &&
+ params[n].u.memref.shm)
+ tee_shm_put(params[n].u.memref.shm);
+ kfree(params);
+ }
+ return rc;
+}
+
+static int tee_ioctl_cancel(struct tee_context *ctx,
+ struct tee_ioctl_cancel_arg __user *uarg)
+{
+ return -EINVAL;
+}
+
+static int
+tee_ioctl_close_session(struct tee_context *ctx,
+ struct tee_ioctl_close_session_arg __user *uarg)
+{
+ struct tee_ioctl_close_session_arg arg;
+
+ if (!ctx->teedev->desc->ops->close_session)
+ return -EINVAL;
+
+ if (copy_from_user(&arg, uarg, sizeof(arg)))
+ return -EFAULT;
+
+ return ctx->teedev->desc->ops->close_session(ctx, arg.session);
+}
+
+static int tee_ioctl(struct cdev *cdev, int cmd, void *arg)
+{
+ struct tee_context *ctx = cdev->priv;
+ void __user *uarg = (void __user *)arg;
+
+ switch (cmd) {
+ case TEE_IOC_VERSION:
+ return tee_ioctl_version(ctx, uarg);
+ case TEE_IOC_SHM_ALLOC:
+ return tee_ioctl_shm_alloc(ctx, uarg);
+ case TEE_IOC_SHM_REGISTER:
+ return tee_ioctl_shm_register(ctx, uarg);
+ case TEE_IOC_OPEN_SESSION:
+ return tee_ioctl_open_session(ctx, uarg);
+ case TEE_IOC_INVOKE:
+ return tee_ioctl_invoke(ctx, uarg);
+ case TEE_IOC_CANCEL:
+ return tee_ioctl_cancel(ctx, uarg);
+ case TEE_IOC_CLOSE_SESSION:
+ return tee_ioctl_close_session(ctx, uarg);
+ case TEE_IOC_SUPPL_RECV:
+ return -ENOSYS;
+ case TEE_IOC_SUPPL_SEND:
+ return -ENOSYS;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct cdev_operations tee_cdev_ops = {
+ .open = tee_open,
+ .close = tee_release,
+ .ioctl = tee_ioctl,
+};
+
+static void tee_devinfo(struct device *dev)
+{
+ struct tee_device *teedev = dev->priv;
+ struct tee_ioctl_version_data vers;
+
+ teedev->desc->ops->get_version(teedev, &vers);
+ printf("Implementation ID: %d\n", vers.impl_id);
+}
+
+/**
+ * tee_device_alloc() - Allocate a new struct tee_device instance
+ * @teedesc: Descriptor for this driver
+ * @dev: Parent device for this device
+ * @driver_data: Private driver data for this device
+ *
+ * Allocates a new struct tee_device instance. The device is
+ * removed by tee_device_unregister().
+ *
+ * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure
+ */
+struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
+ struct device *dev,
+ void *driver_data)
+{
+ struct tee_device *teedev;
+ void *ret;
+ int rc;
+
+ if (!teedesc || !teedesc->name || !teedesc->ops ||
+ !teedesc->ops->get_version || !teedesc->ops->open ||
+ !teedesc->ops->release)
+ return ERR_PTR(-EINVAL);
+
+ teedev = kzalloc(sizeof(*teedev), GFP_KERNEL);
+ if (!teedev) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ teedev->dev.id = DEVICE_ID_DYNAMIC;
+ teedev->dev.parent = dev;
+ teedev->dev.type_data = driver_data;
+ teedev->dev.priv = teedev;
+ teedev->dev.info = tee_devinfo;
+
+ rc = dev_set_name(&teedev->dev, "tee%s",
+ teedesc->flags & TEE_DESC_PRIVILEGED ? "priv" : "");
+ if (rc) {
+ ret = ERR_PTR(rc);
+ goto err;
+ }
+
+ if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+ teedev->cdev.dev = &teedev->dev;
+ teedev->cdev.ops = &tee_cdev_ops;
+ }
+
+ /* 1 as tee_device_unregister() does one final tee_device_put() */
+ teedev->num_users = 1;
+ mutex_init(&teedev->mutex);
+
+ teedev->desc = teedesc;
+
+ return teedev;
+err:
+ pr_err("could not register %s driver\n",
+ teedesc->flags & TEE_DESC_PRIVILEGED ? "privileged" : "client");
+ kfree(teedev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tee_device_alloc);
+
+void tee_device_release(struct tee_device *teedev)
+{
+ kfree(teedev);
+}
+
+/**
+ * tee_device_register() - Registers a TEE device
+ * @teedev: Device to register
+ *
+ * tee_device_unregister() need to be called to remove the @teedev if
+ * this function fails.
+ *
+ * @returns < 0 on failure
+ */
+int tee_device_register(struct tee_device *teedev)
+{
+ int rc;
+
+ if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) {
+ dev_err(&teedev->dev, "attempt to register twice\n");
+ return -EINVAL;
+ }
+
+ rc = register_device(&teedev->dev);
+ if (rc)
+ return rc;
+
+ if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+ teedev->cdev.name = teedev->dev.unique_name;
+
+ rc = devfs_create(&teedev->cdev);
+ if (rc)
+ goto out;
+ }
+
+ list_add_tail(&teedev->list, &tee_clients);
+
+ teedev->flags |= TEE_DEVICE_FLAG_REGISTERED;
+ return 0;
+
+out:
+ unregister_device(&teedev->dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tee_device_register);
+
+void tee_device_put(struct tee_device *teedev)
+{
+ mutex_lock(&teedev->mutex);
+ /* Shouldn't put in this state */
+ if (!WARN_ON(!teedev->desc)) {
+ teedev->num_users--;
+ if (!teedev->num_users) {
+ teedev->desc = NULL;
+ }
+ }
+ mutex_unlock(&teedev->mutex);
+}
+
+bool tee_device_get(struct tee_device *teedev)
+{
+ mutex_lock(&teedev->mutex);
+ if (!teedev->desc) {
+ mutex_unlock(&teedev->mutex);
+ return false;
+ }
+ teedev->num_users++;
+ mutex_unlock(&teedev->mutex);
+ return true;
+}
+
+/**
+ * tee_device_unregister() - Removes a TEE device
+ * @teedev: Device to unregister
+ *
+ * This function should be called to remove the @teedev even if
+ * tee_device_register() hasn't been called yet. Does nothing if
+ * @teedev is NULL.
+ */
+void tee_device_unregister(struct tee_device *teedev)
+{
+ if (!teedev)
+ return;
+
+ list_del(&teedev->list);
+ if (IS_ENABLED(CONFIG_OPTEE_DEVFS))
+ devfs_remove(&teedev->cdev);
+ unregister_device(&teedev->dev);
+}
+EXPORT_SYMBOL_GPL(tee_device_unregister);
+
+/**
+ * tee_get_drvdata() - Return driver_data pointer
+ * @teedev: Device containing the driver_data pointer
+ * @returns the driver_data pointer supplied to tee_device_alloc().
+ */
+void *tee_get_drvdata(struct tee_device *teedev)
+{
+ return teedev->dev.type_data;
+}
+EXPORT_SYMBOL_GPL(tee_get_drvdata);
+
+struct match_dev_data {
+ struct tee_ioctl_version_data *vers;
+ const void *data;
+ int (*match)(struct tee_ioctl_version_data *, const void *);
+};
+
+static int match_dev(struct device *dev, const void *data)
+{
+ const struct match_dev_data *match_data = data;
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ teedev->desc->ops->get_version(teedev, match_data->vers);
+ return match_data->match(match_data->vers, match_data->data);
+}
+
+struct tee_context *
+tee_client_open_context(struct tee_context *start,
+ int (*match)(struct tee_ioctl_version_data *,
+ const void *),
+ const void *data, struct tee_ioctl_version_data *vers)
+{
+ struct device *startdev = NULL;
+ struct tee_device *teedev;
+ struct tee_ioctl_version_data v;
+ struct match_dev_data match_data = { vers ? vers : &v, data, match };
+
+ if (start)
+ startdev = &start->teedev->dev;
+
+ list_for_each_entry(teedev, &tee_clients, list) {
+ struct device *dev = &teedev->dev;
+ struct tee_context *ctx ;
+
+ if (startdev) {
+ if (dev == startdev)
+ startdev = NULL;
+ continue;
+ }
+
+ if (!match_dev(dev, &match_data))
+ continue;
+
+ ctx = teedev_open(teedev);
+ if (IS_ERR(ctx) && PTR_ERR(ctx) != -ENOMEM)
+ continue;
+
+ /* On success or -ENOMEM, early exit the iteration */
+ return ctx;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_GPL(tee_client_open_context);
+
+void tee_client_close_context(struct tee_context *ctx)
+{
+ teedev_close_context(ctx);
+}
+EXPORT_SYMBOL_GPL(tee_client_close_context);
+
+void tee_client_get_version(struct tee_context *ctx,
+ struct tee_ioctl_version_data *vers)
+{
+ ctx->teedev->desc->ops->get_version(ctx->teedev, vers);
+}
+EXPORT_SYMBOL_GPL(tee_client_get_version);
+
+int tee_client_open_session(struct tee_context *ctx,
+ struct tee_ioctl_open_session_arg *arg,
+ struct tee_param *param)
+{
+ if (!ctx->teedev->desc->ops->open_session)
+ return -EINVAL;
+ return ctx->teedev->desc->ops->open_session(ctx, arg, param);
+}
+EXPORT_SYMBOL_GPL(tee_client_open_session);
+
+int tee_client_close_session(struct tee_context *ctx, u32 session)
+{
+ if (!ctx->teedev->desc->ops->close_session)
+ return -EINVAL;
+ return ctx->teedev->desc->ops->close_session(ctx, session);
+}
+EXPORT_SYMBOL_GPL(tee_client_close_session);
+
+int tee_client_invoke_func(struct tee_context *ctx,
+ struct tee_ioctl_invoke_arg *arg,
+ struct tee_param *param)
+{
+ if (!ctx->teedev->desc->ops->invoke_func)
+ return -EINVAL;
+ return ctx->teedev->desc->ops->invoke_func(ctx, arg, param);
+}
+EXPORT_SYMBOL_GPL(tee_client_invoke_func);
+
+static int tee_client_device_match(struct device *dev,
+ struct device_driver *drv)
+{
+ const struct tee_client_device_id *id_table;
+ struct tee_client_device *tee_device;
+
+ id_table = to_tee_client_driver(drv)->id_table;
+ tee_device = to_tee_client_device(dev);
+
+ while (!uuid_is_null(&id_table->uuid)) {
+ if (uuid_equal(&tee_device->id.uuid, &id_table->uuid))
+ return 0;
+ id_table++;
+ }
+
+ return -1;
+}
+
+struct bus_type tee_bus_type = {
+ .name = "tee",
+ .match = tee_client_device_match,
+};
+EXPORT_SYMBOL_GPL(tee_bus_type);
+
+static int __init tee_init(void)
+{
+ return bus_register(&tee_bus_type);
+}
+pure_initcall(tee_init);
+
+MODULE_AUTHOR("Linaro");
+MODULE_DESCRIPTION("TEE Driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h
new file mode 100644
index 0000000000..045f2df9f3
--- /dev/null
+++ b/drivers/tee/tee_private.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015-2016, Linaro Limited
+ */
+#ifndef TEE_PRIVATE_H
+#define TEE_PRIVATE_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#define TEE_DEVICE_FLAG_REGISTERED 0x1
+#define TEE_MAX_DEV_NAME_LEN 32
+
+struct tee_shm;
+struct tee_context;
+
+/**
+ * struct tee_device - TEE Device representation
+ * @name: name of device
+ * @desc: description of device
+ * @id: unique id of device
+ * @flags: represented by TEE_DEVICE_FLAG_REGISTERED above
+ * @dev: embedded basic device structure
+ * @cdev: embedded cdev
+ * @num_users: number of active users of this device
+ * @mutex: mutex protecting @num_users and @idr
+ */
+struct tee_device {
+ char name[TEE_MAX_DEV_NAME_LEN];
+ const struct tee_desc *desc;
+ struct list_head list;
+ unsigned int flags;
+
+ struct device dev;
+ struct cdev cdev;
+
+ size_t num_users;
+ struct mutex mutex; /* protects num_users and idr */
+};
+
+int tee_shm_get_fd(struct tee_shm *shm);
+
+bool tee_device_get(struct tee_device *teedev);
+void tee_device_put(struct tee_device *teedev);
+
+void teedev_ctx_get(struct tee_context *ctx);
+void teedev_ctx_put(struct tee_context *ctx);
+
+#endif /*TEE_PRIVATE_H*/
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
new file mode 100644
index 0000000000..ea16c9cdd2
--- /dev/null
+++ b/drivers/tee/tee_shm.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2017, 2019-2021 Linaro Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uaccess.h>
+#include <linux/sizes.h>
+#include <fcntl.h>
+#include "tee_private.h"
+
+static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm)
+{
+ if (shm->flags & TEE_SHM_DYNAMIC)
+ teedev->desc->ops->shm_unregister(shm->ctx, shm);
+
+ if (!(shm->flags & TEE_SHM_PRIV)) {
+ list_del(&shm->link);
+ if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+ devfs_remove(&shm->cdev);
+ unregister_device(&shm->dev);
+ }
+ }
+
+ if (shm->flags & TEE_SHM_POOL)
+ free(shm->kaddr);
+
+ teedev_ctx_put(shm->ctx);
+
+ kfree(shm);
+
+ tee_device_put(teedev);
+}
+
+static const struct cdev_operations tee_shm_ops = {
+ .read = mem_read,
+ .memmap = generic_memmap_ro,
+};
+
+static struct tee_shm *
+register_shm_helper(struct tee_context *ctx, void *addr,
+ size_t size, u32 flags)
+{
+ struct tee_device *teedev = ctx->teedev;
+ struct tee_shm *shm;
+ int rc;
+
+ if (!addr)
+ return ERR_PTR(-ENOMEM);
+
+ if (!tee_device_get(teedev))
+ return ERR_PTR(-EINVAL);
+
+ teedev_ctx_get(ctx);
+
+ shm = calloc(1, sizeof(*shm));
+ if (!shm) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ shm->fd = -EBADF;
+ shm->dev.id = -EACCES;
+ shm->ctx = ctx;
+ shm->kaddr = addr;
+ shm->paddr = virt_to_phys(shm->kaddr);
+ shm->size = size;
+ shm->flags = flags;
+
+ if (!(flags & TEE_SHM_PRIV)) {
+ if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+ shm->res.start = (resource_size_t)addr;
+ shm->res.end = (resource_size_t)(addr + size - 1);
+ shm->res.flags = IORESOURCE_MEM;
+
+ shm->dev.id = DEVICE_ID_DYNAMIC;
+ shm->dev.parent = &ctx->teedev->dev;
+ shm->dev.resource = &shm->res;
+ shm->dev.num_resources = 1;
+ rc = dev_set_name(&shm->dev, "%s-shm", ctx->teedev->dev.unique_name);
+ if (rc)
+ goto err;
+
+ rc = register_device(&shm->dev);
+ if (rc)
+ goto err;
+
+ shm->res.name = shm->dev.unique_name;
+
+ shm->cdev.dev = &shm->dev;
+ shm->cdev.ops = &tee_shm_ops;
+ shm->cdev.size = size;
+ shm->cdev.name = shm->dev.unique_name;
+ rc = devfs_create(&shm->cdev);
+ if (rc)
+ goto err;
+ }
+
+ list_add(&shm->link, &ctx->list_shm);
+ }
+
+ if (flags & TEE_SHM_DYNAMIC) {
+ rc = ctx->teedev->desc->ops->shm_register(ctx, shm);
+ if (rc)
+ goto err;
+ }
+
+ refcount_set(&shm->refcount, 1);
+
+ pr_debug("%s: shm=%p cdev=%s addr=%p size=%zu\n", __func__, shm,
+ shm->cdev.name ?: "(priv)", addr, size);
+
+ return shm;
+err:
+ if (!(flags & TEE_SHM_PRIV)) {
+ list_del(&shm->link);
+ if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+ devfs_remove(&shm->cdev);
+ unregister_device(&shm->dev);
+ }
+ }
+
+ free(shm);
+ teedev_ctx_put(ctx);
+ tee_device_put(teedev);
+
+ return ERR_PTR(rc);
+}
+
+/**
+ * tee_shm_register_user_buf() - Register a userspace shared memory buffer
+ * @ctx: Context that registers the shared memory
+ * @addr: The userspace address of the shared buffer
+ * @length: Length of the shared buffer
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx,
+ unsigned long addr, size_t length)
+{
+ u32 flags = TEE_SHM_USER_MAPPED | TEE_SHM_DYNAMIC;
+
+ return register_shm_helper(ctx, (void *)addr, length, flags);
+}
+
+static struct tee_shm *shm_alloc_helper(struct tee_context *ctx, size_t size,
+ size_t align, u32 flags)
+{
+ struct tee_shm *shm;
+ void *addr;
+
+ addr = align ? memalign(align, size) : malloc(size);
+ if (!addr)
+ return ERR_PTR(-ENOMEM);
+
+ flags |= TEE_SHM_POOL;
+
+ shm = register_shm_helper(ctx, addr, size, flags);
+ if (IS_ERR(shm))
+ free(addr);
+
+ return shm;
+}
+
+/**
+ * tee_shm_alloc_user_buf() - Allocate shared memory for user space
+ * @ctx: Context that allocates the shared memory
+ * @size: Requested size of shared memory
+ *
+ * Memory allocated as user space shared memory is automatically freed when
+ * the TEE file pointer is closed. The primary usage of this function is
+ * when the TEE driver doesn't support registering ordinary user space
+ * memory.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size)
+ __alias(tee_shm_alloc_kernel_buf);
+
+/**
+ * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer
+ * @ctx: Context that allocates the shared memory
+ * @size: Requested size of shared memory
+ *
+ * The returned memory registered in secure world and is suitable to be
+ * passed as a memory buffer in parameter argument to
+ * tee_client_invoke_func(). The memory allocated is later freed with a
+ * call to tee_shm_free().
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
+{
+ u32 flags = TEE_SHM_DYNAMIC | TEE_SHM_POOL;
+
+ return shm_alloc_helper(ctx, size, SZ_4K, flags);
+}
+
+/**
+ * tee_shm_alloc_priv_buf() - Allocate shared memory for a privately shared
+ * kernel buffer
+ * @ctx: Context that allocates the shared memory
+ * @size: Requested size of shared memory
+ *
+ * This function returns similar shared memory as
+ * tee_shm_alloc_kernel_buf(), but with the difference that the memory
+ * might not be registered in secure world in case the driver supports
+ * passing memory not registered in advance.
+ *
+ * This function should normally only be used internally in the TEE
+ * drivers.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size)
+{
+ u32 flags = TEE_SHM_PRIV | TEE_SHM_POOL;
+
+ return shm_alloc_helper(ctx, size, SZ_4K, flags);
+}
+EXPORT_SYMBOL_GPL(tee_shm_alloc_priv_buf);
+
+/**
+ * tee_shm_get_fd() - Increase reference count and return file descriptor
+ * @shm: Shared memory handle
+ * @returns user space file descriptor to shared memory
+ */
+int tee_shm_get_fd(struct tee_shm *shm)
+{
+ int fd;
+
+ if (!IS_ENABLED(CONFIG_OPTEE_DEVFS))
+ return -ENOSYS;
+
+ refcount_inc(&shm->refcount);
+
+ if (shm->fd < 0) {
+ char *tmp;
+
+ tmp = basprintf("/dev/%s", shm->cdev.name);
+ if (!tmp)
+ return -ENOMEM;
+
+ shm->fd = open(tmp, O_RDONLY);
+ free(tmp);
+ }
+
+ fd = shm->fd;
+
+ if (shm->fd < 0)
+ tee_shm_put(shm);
+
+ return fd;
+}
+
+/**
+ * tee_shm_free() - Free shared memory
+ * @shm: Handle to shared memory to free
+ */
+void tee_shm_free(struct tee_shm *shm)
+{
+ tee_shm_put(shm);
+}
+EXPORT_SYMBOL_GPL(tee_shm_free);
+
+/**
+ * tee_shm_get_va() - Get virtual address of a shared memory plus an offset
+ * @shm: Shared memory handle
+ * @offs: Offset from start of this shared memory
+ * @returns virtual address of the shared memory + offs if offs is within
+ * the bounds of this shared memory, else an ERR_PTR
+ */
+void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
+{
+ if (!shm->kaddr)
+ return ERR_PTR(-EINVAL);
+ if (offs >= shm->size)
+ return ERR_PTR(-EINVAL);
+ return (char *)shm->kaddr + offs;
+}
+EXPORT_SYMBOL_GPL(tee_shm_get_va);
+
+/**
+ * tee_shm_get_pa() - Get physical address of a shared memory plus an offset
+ * @shm: Shared memory handle
+ * @offs: Offset from start of this shared memory
+ * @pa: Physical address to return
+ * @returns 0 if offs is within the bounds of this shared memory, else an
+ * error code.
+ */
+int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa)
+{
+ if (offs >= shm->size)
+ return -EINVAL;
+ if (pa)
+ *pa = shm->paddr + offs;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tee_shm_get_pa);
+
+/**
+ * tee_shm_get_from_id() - Find shared memory object and increase reference
+ * count
+ * @ctx: Context owning the shared memory
+ * @id: Id of shared memory object
+ * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
+ */
+struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
+{
+ struct tee_shm *shm;
+
+ list_for_each_entry(shm, &ctx->list_shm, link) {
+ if (shm->dev.id == id) {
+ refcount_inc(&shm->refcount);
+ return shm;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(tee_shm_get_from_id);
+
+/**
+ * tee_shm_put() - Decrease reference count on a shared memory handle
+ * @shm: Shared memory handle
+ */
+void tee_shm_put(struct tee_shm *shm)
+{
+ struct tee_device *teedev = shm->ctx->teedev;
+
+ if (refcount_dec_and_test(&shm->refcount))
+ tee_shm_release(teedev, shm);
+}
+EXPORT_SYMBOL_GPL(tee_shm_put);
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index e43c28113f..d66a75635d 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -24,6 +24,8 @@ source "drivers/usb/misc/Kconfig"
endif
+source "drivers/usb/typec/Kconfig"
+
source "drivers/usb/gadget/Kconfig"
source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 8f1557d5d4..0cac50c0f3 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_USB_STORAGE) += storage/
obj-y += host/
obj-y += otg/
obj-y += gadget/
+obj-y += typec/
obj-$(CONFIG_USB) += misc/
diff --git a/drivers/usb/core/common.c b/drivers/usb/core/common.c
index d562b963be..61ccc13024 100644
--- a/drivers/usb/core/common.c
+++ b/drivers/usb/core/common.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
-#include <usb/ch9.h>
+#include <linux/usb/ch9.h>
static const char *const speed_names[] = {
[USB_SPEED_UNKNOWN] = "UNKNOWN",
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 6fbe37fccf..bef428f7fb 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -9,9 +9,9 @@
#include <init.h>
#include <malloc.h>
#include <errno.h>
-#include <usb/phy.h>
-#include <usb/usb.h>
-#include <usb/usb_defs.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usb_defs.h>
#include "usb.h"
@@ -86,18 +86,25 @@ static int usb_get_hub_status(struct usb_device *dev, void *data)
data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
}
-static int usb_get_port_status(struct usb_device *dev, int port, void *data)
+static int usb_get_port_status(struct usb_device *dev, int port,
+ struct usb_port_status *status)
{
+ struct usb_port_status *data;
int ret;
+ data = dma_alloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
if (ret < 0)
- return ret;
+ goto out;
+
+ *status = *data;
if (!usb_hub_is_root_hub(dev) && usb_hub_is_superspeed(dev)) {
- struct usb_port_status *status = data;
u16 tmp = status->wPortStatus & USB_SS_PORT_STAT_MASK;
if (status->wPortStatus & USB_SS_PORT_STAT_POWER)
@@ -109,6 +116,8 @@ static int usb_get_port_status(struct usb_device *dev, int port, void *data)
status->wPortStatus = tmp;
}
+out:
+ dma_free(data);
return ret;
}
@@ -434,12 +443,16 @@ out:
static int usb_hub_configure(struct usb_device *dev)
{
- unsigned char buffer[USB_BUFSIZ], *bitmap;
+ unsigned char *buffer, *bitmap;
struct usb_hub_descriptor *descriptor;
struct usb_hub_status *hubsts;
int i, ret;
struct usb_hub_device *hub;
+ buffer = dma_alloc(USB_BUFSIZ);
+ if (!buffer)
+ return -ENOMEM;
+
hub = xzalloc(sizeof (*hub));
dev->hub = hub;
@@ -448,7 +461,8 @@ static int usb_hub_configure(struct usb_device *dev)
if (usb_get_hub_descriptor(dev, buffer, 4) < 0) {
dev_dbg(&dev->dev, "%s: failed to get hub " \
"descriptor, giving up %lX\n", __func__, dev->status);
- return -1;
+ ret = -1;
+ goto out;
}
descriptor = (struct usb_hub_descriptor *)buffer;
@@ -458,13 +472,15 @@ static int usb_hub_configure(struct usb_device *dev)
dev_dbg(&dev->dev, "%s: failed to get hub " \
"descriptor - too long: %d\n", __func__,
descriptor->bLength);
- return -1;
+ ret = -1;
+ goto out;
}
if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) {
dev_dbg(&dev->dev, "%s: failed to get hub " \
"descriptor 2nd giving up %lX\n", __func__, dev->status);
- return -1;
+ ret = -1;
+ goto out;
}
memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength);
/* adjust 16bit values */
@@ -580,13 +596,15 @@ static int usb_hub_configure(struct usb_device *dev)
if (sizeof(struct usb_hub_status) > USB_BUFSIZ) {
dev_dbg(&dev->dev, "%s: failed to get Status - " \
"too long: %d\n", __func__, descriptor->bLength);
- return -1;
+ ret = -1;
+ goto out;
}
if (usb_get_hub_status(dev, buffer) < 0) {
dev_dbg(&dev->dev, "%s: failed to get Status %lX\n", __func__,
dev->status);
- return -1;
+ ret = -1;
+ goto out;
}
hubsts = (struct usb_hub_status *)buffer;
@@ -601,16 +619,12 @@ static int usb_hub_configure(struct usb_device *dev)
"" : "no ");
if (dev->host->update_hub_device) {
- int ret;
-
ret = dev->host->update_hub_device(dev);
if (ret)
- return ret;
+ goto out;
}
if (!usb_hub_is_root_hub(dev) && usb_hub_is_superspeed(dev)) {
- int ret;
-
/*
* This request sets the value that the hub uses to
* determine the index into the 'route string index'
@@ -620,13 +634,16 @@ static int usb_hub_configure(struct usb_device *dev)
if (ret < 0) {
dev_dbg(&dev->dev, "failed to set hub depth (0x%08lx)\n",
dev->status);
- return ret;
+ goto out;
}
}
usb_hub_power_on(hub);
- return 0;
+ ret = 0;
+out:
+ dma_free(buffer);
+ return ret;
}
static int usb_hub_configure_ports(struct usb_device *dev)
@@ -660,7 +677,7 @@ static int usb_hub_configure_ports(struct usb_device *dev)
return usb_device_list_scan();
}
-static int usb_hub_detect(struct device_d *dev)
+static int usb_hub_detect(struct device *dev)
{
struct usb_device *usbdev = container_of(dev, struct usb_device, dev);
int i;
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index acf95320c0..25203c6064 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -4,8 +4,8 @@
*/
#include <common.h>
-#include <usb/usb.h>
-#include <usb/phy.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/phy.h>
#include <of.h>
static const char *usb_dr_modes[] = {
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 34a0f004f7..1f6f1d7c41 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -38,8 +38,8 @@
#include <init.h>
#include <dma.h>
-#include <usb/usb.h>
-#include <usb/ch9.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/ch9.h>
#include "usb.h"
@@ -77,7 +77,7 @@ static inline void usb_host_release(struct usb_host *host)
slice_release(&host->slice);
}
-static int usb_hw_detect(struct device_d *dev)
+static int usb_hw_detect(struct device *dev)
{
struct usb_host *host;
@@ -477,8 +477,8 @@ int usb_new_device(struct usb_device *dev)
/* we set the default configuration here */
err = usb_set_configuration(dev, dev->config.desc.bConfigurationValue);
if (err) {
- dev_err(&dev->dev, "Setting default configuration failed with: %s\n" \
- "len %d, status %lX\n", strerror(-err),
+ dev_err(&dev->dev, "Setting default configuration failed with: %pe\n" \
+ "len %d, status %lX\n", ERR_PTR(err),
dev->act_len, dev->status);
goto err_out;
}
@@ -502,7 +502,7 @@ int usb_new_device(struct usb_device *dev)
err = register_device(&dev->dev);
if (err) {
- dev_err(&dev->dev, "Failed to register device: %s\n", strerror(-err));
+ dev_err(&dev->dev, "Failed to register device: %pe\n", ERR_PTR(err));
goto err_out;
}
@@ -576,6 +576,8 @@ int usb_host_detect(struct usb_host *host)
{
int ret;
+ of_usb_host_probe_hubs(host);
+
if (!host->root_dev) {
if (host->init) {
ret = host->init(host);
@@ -599,7 +601,7 @@ int usb_host_detect(struct usb_host *host)
return 0;
}
-void usb_rescan(void)
+int usb_rescan(void)
{
struct usb_host *host;
int ret;
@@ -613,6 +615,22 @@ void usb_rescan(void)
}
pr_info("%d USB Device(s) found\n", dev_count);
+
+ if (IS_ENABLED(CONFIG_USB_OTGDEV)) {
+ unsigned int skipped_otg = 0;
+ struct device *dev;
+
+ bus_for_each_device(&otg_bus_type, dev) {
+ if (otg_device_get_mode(dev) == USB_DR_MODE_OTG)
+ skipped_otg++;
+ }
+
+ if (skipped_otg)
+ pr_notice("%u unconfigured OTG controller(s) were not scanned\n",
+ skipped_otg);
+ }
+
+ return dev_count;
}
/*-------------------------------------------------------------------
@@ -960,15 +978,15 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
*/
int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
{
- unsigned char mybuf[USB_BUFSIZ];
unsigned char *tbuf;
- int err;
+ int err = 0;
unsigned int u, idx;
if (size <= 0 || !buf || !index)
return -1;
+
+ tbuf = dma_alloc(USB_BUFSIZ);
buf[0] = 0;
- tbuf = &mybuf[0];
/* get langid for strings if it's not yet known */
if (!dev->have_langid) {
@@ -976,10 +994,12 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
if (err < 0) {
dev_dbg(&dev->dev, "error getting string descriptor 0 " \
"(error=%lx)\n", dev->status);
- return -1;
+ err = -1;
+ goto fail;
} else if (tbuf[0] < 4) {
pr_debug("string descriptor 0 too short\n");
- return -1;
+ err = -1;
+ goto fail;
} else {
dev->have_langid = -1;
dev->string_langid = tbuf[2] | (tbuf[3] << 8);
@@ -992,7 +1012,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
err = usb_string_sub(dev, dev->string_langid, index, tbuf);
if (err < 0)
- return err;
+ goto fail;
size--; /* leave room for trailing NULL char in output buffer */
for (idx = 0, u = 2; u < err; u += 2) {
@@ -1005,6 +1025,8 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
}
buf[idx] = 0;
err = idx;
+fail:
+ dma_free(tbuf);
return err;
}
@@ -1100,7 +1122,7 @@ static const struct usb_device_id *usb_match_id(struct usb_device *usbdev,
}
EXPORT_SYMBOL(usb_match_id);
-static int usb_match(struct device_d *dev, struct driver_d *drv)
+static int usb_match(struct device *dev, struct driver *drv)
{
struct usb_device *usbdev = container_of(dev, struct usb_device, dev);
struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver);
@@ -1117,7 +1139,7 @@ static int usb_match(struct device_d *dev, struct driver_d *drv)
return 1;
}
-static int usb_probe(struct device_d *dev)
+static int usb_probe(struct device *dev)
{
struct usb_device *usbdev = container_of(dev, struct usb_device, dev);
struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver);
@@ -1128,7 +1150,7 @@ static int usb_probe(struct device_d *dev)
return usbdrv->probe(usbdev, id);
}
-static void usb_remove(struct device_d *dev)
+static void usb_remove(struct device *dev)
{
struct usb_device *usbdev = container_of(dev, struct usb_device, dev);
struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver);
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 459ebc6537..b198ba6bf8 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -188,6 +188,33 @@ void dwc2_set_default_params(struct dwc2 *dwc2)
}
}
+void dwc2_get_device_properties(struct dwc2 *dwc2)
+{
+ struct dwc2_core_params *p = &dwc2->params;
+ struct device_node *np = dwc2->dev->of_node;
+ int num;
+
+ if ((dwc2->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+ (dwc2->dr_mode == USB_DR_MODE_OTG)) {
+ of_property_read_u32(np, "g-rx-fifo-size",
+ &p->g_rx_fifo_size);
+
+ of_property_read_u32(np, "g-np-tx-fifo-size",
+ &p->g_np_tx_fifo_size);
+
+ num = of_property_count_elems_of_size(np, "g-tx-fifo-size", sizeof(u32));
+ if (num > 0) {
+ num = min(num, 15);
+ memset(p->g_tx_fifo_size, 0,
+ sizeof(p->g_tx_fifo_size));
+ of_property_read_u32_array(np,
+ "g-tx-fifo-size",
+ &p->g_tx_fifo_size[1],
+ num);
+ }
+ }
+}
+
int dwc2_check_core_version(struct dwc2 *dwc2)
{
struct dwc2_hw_params *hw = &dwc2->hw_params;
@@ -645,7 +672,7 @@ int dwc2_get_dr_mode(struct dwc2 *dwc2)
{
enum usb_dr_mode mode;
- mode = of_usb_get_dr_mode(dwc2->dev->device_node, NULL);
+ mode = of_usb_get_dr_mode(dwc2->dev->of_node, NULL);
dwc2->dr_mode = mode;
if (dwc2_hw_is_device(dwc2)) {
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 84cb698dc8..a9526a8c5d 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -460,7 +460,7 @@ enum dwc2_ep0_state {
#define DWC2_CTRL_BUFF_SIZE 8
struct dwc2 {
- struct device_d *dev;
+ struct device *dev;
void __iomem *regs;
enum usb_dr_mode dr_mode;
struct dwc2_hw_params hw_params;
diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c
index 8cb99446e4..a35fd0e717 100644
--- a/drivers/usb/dwc2/dwc2.c
+++ b/drivers/usb/dwc2/dwc2.c
@@ -85,18 +85,17 @@ static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode)
typedef void (*set_params_cb)(struct dwc2 *dwc2);
-static int dwc2_probe(struct device_d *dev)
+static int dwc2_probe(struct device *dev)
{
struct resource *iores;
struct dwc2 *dwc2;
set_params_cb set_params;
int ret;
- dwc2 = xzalloc(sizeof(*dwc2));
-
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
+ dwc2 = xzalloc(sizeof(*dwc2));
dwc2->regs = IOMEM(iores->start);
dwc2->dev = dev;
@@ -149,6 +148,7 @@ static int dwc2_probe(struct device_d *dev)
goto error;
dwc2_set_default_params(dwc2);
+ dwc2_get_device_properties(dwc2);
set_params = of_device_get_match_data(dev);
if (set_params)
@@ -177,10 +177,12 @@ clk_put:
release_region:
release_region(iores);
+ free(dwc2);
+
return ret;
}
-static void dwc2_remove(struct device_d *dev)
+static void dwc2_remove(struct device *dev)
{
struct dwc2 *dwc2 = dev->priv;
@@ -201,8 +203,9 @@ static const struct of_device_id dwc2_platform_dt_ids[] = {
.data = dwc2_set_stm32mp15_hsotg_params },
{ }
};
+MODULE_DEVICE_TABLE(of, dwc2_platform_dt_ids);
-static struct driver_d dwc2_driver = {
+static struct driver dwc2_driver = {
.name = "dwc2",
.probe = dwc2_probe,
.remove = dwc2_remove,
diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h
index 3ecc359f8b..2e740a890e 100644
--- a/drivers/usb/dwc2/dwc2.h
+++ b/drivers/usb/dwc2/dwc2.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include <usb/usb.h>
-#include <usb/usb_defs.h>
-#include <usb/gadget.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usb_defs.h>
+#include <linux/usb/gadget.h>
#include <linux/phy/phy.h>
#include "regs.h"
@@ -9,6 +9,7 @@
/* Core functions */
void dwc2_set_default_params(struct dwc2 *dwc2);
+void dwc2_get_device_properties(struct dwc2 *dwc2);
int dwc2_check_core_version(struct dwc2 *dwc2);
void dwc2_get_hwparams(struct dwc2 *dwc2);
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 7070485410..3c06c438b6 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1,25 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <dma.h>
-#include <usb/gadget.h>
+#include <linux/usb/gadget.h>
+#include <linux/spinlock.h>
#include "dwc2.h"
#define to_dwc2 gadget_to_dwc2
#define dwc2_set_bit(d, r, b) dwc2_writel(d, (b) | dwc2_readl(d, r), r)
#define dwc2_clear_bit(d, r, b) dwc2_writel(d, ~(b) & dwc2_readl(d, r), r)
-#define spin_lock(lock)
-#define spin_unlock(lock)
#define local_irq_save(flags)(void)(flags)
#define local_irq_restore(flags) (void)(flags)
-#define spin_lock_irqsave(lock, flags) (void)(flags)
-#define spin_unlock_irqrestore(lock, flags) (void)(flags)
-
-#ifndef USB_ENDPOINT_MAXP_MASK
-#define USB_ENDPOINT_MAXP_MASK 0x07ff
-#endif
-#ifndef USB_EP_MAXP_MULT
-#define USB_EP_MAXP_MULT(m) (((m) & 0x1800) >> 11)
-#endif
static void kill_all_requests(struct dwc2 *, struct dwc2_ep *, int);
@@ -484,7 +474,7 @@ static int dwc2_ep_enable(struct usb_ep *ep,
ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
mps = usb_endpoint_maxp(desc) & USB_ENDPOINT_MAXP_MASK;
- mc = USB_EP_MAXP_MULT(usb_endpoint_maxp(desc));
+ mc = usb_endpoint_maxp_mult(desc);
/* note, we handle this here instead of dwc2_set_ep_maxpacket */
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
@@ -1549,9 +1539,8 @@ static void dwc2_gadget_setup_fifo(struct dwc2 *dwc2)
u32 np_tx_fifo_size = dwc2->params.g_np_tx_fifo_size;
u32 rx_fifo_size = dwc2->params.g_rx_fifo_size;
u32 fifo_size = dwc2->hw_params.total_fifo_size;
- u32 *tx_fifo_size = dwc2->params.g_tx_fifo_size;
- u32 size, depth;
- u32 txfsz;
+ u32 *txfsz = dwc2->params.g_tx_fifo_size;
+ u32 size, val;
/* Reset fifo map if not correctly cleared during previous session */
WARN_ON(dwc2->fifo_map);
@@ -1578,19 +1567,17 @@ static void dwc2_gadget_setup_fifo(struct dwc2 *dwc2)
* them to endpoints dynamically according to maxpacket size value of
* given endpoint.
*/
+ for (ep = 1; ep < DWC2_MAX_EPS_CHANNELS; ep++) {
+ if (!txfsz[ep])
+ continue;
+ val = addr;
+ val |= txfsz[ep] << FIFOSIZE_DEPTH_SHIFT;
+ WARN_ONCE(addr + txfsz[ep] > fifo_size,
+ "insufficient fifo memory");
+ addr += txfsz[ep];
- for (ep = 1; ep < dwc2->num_eps; ep++) {
- txfsz = dwc2_readl(dwc2, DPTXFSIZN(ep));
- depth = tx_fifo_size[ep];
-
- if (addr + depth > fifo_size)
- dwc2_err(dwc2, "insufficient fifo memory\n");
-
- txfsz = depth << FIFOSIZE_DEPTH_SHIFT;
- txfsz |= addr & 0xffff;
- dwc2_writel(dwc2, txfsz, DPTXFSIZN(ep));
-
- addr += depth;
+ dwc2_writel(dwc2, val, DPTXFSIZN(ep));
+ val = dwc2_readl(dwc2, DPTXFSIZN(ep));
}
dwc2_writel(dwc2, dwc2->hw_params.total_fifo_size |
@@ -2533,8 +2520,7 @@ static int dwc2_gadget_udc_start(struct usb_gadget *gadget,
return 0;
}
-static int dwc2_gadget_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int dwc2_gadget_udc_stop(struct usb_gadget *gadget)
{
struct dwc2 *dwc2 = to_dwc2(gadget);
unsigned long flags = 0;
diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c
index bf9366273d..a9b25aeeaa 100644
--- a/drivers/usb/dwc2/host.c
+++ b/drivers/usb/dwc2/host.c
@@ -209,9 +209,9 @@ static int transfer_chunk(struct dwc2 *dwc2, u8 hc,
if (ret < 0)
goto exit;
- if (in)
- xfer_len -= sub;
*actual_len = xfer_len;
+ if (in)
+ *actual_len -= sub;
exit:
if (xfer_len)
@@ -616,7 +616,7 @@ static void dwc2_config_fifos(struct dwc2 *dwc2)
* @param regs Programming view of DWC2 controller
*
*/
-static void dwc2_core_host_init(struct device_d *dev,
+static void dwc2_core_host_init(struct device *dev,
struct dwc2 *dwc2)
{
uint32_t hcchar, hcfg, hprt0, hotgctl, usbcfg;
@@ -721,7 +721,7 @@ static void dwc2_core_host_init(struct device_d *dev,
static int dwc2_host_init(struct usb_host *host)
{
struct dwc2 *dwc2 = to_dwc2(host);
- struct device_d *dev = dwc2->dev;
+ struct device *dev = dwc2->dev;
uint32_t hprt0, gusbcfg;
int i, j;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 7be3b18df9..8e6dc59a5d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/**
+// SPDX-License-Identifier: GPL-2.0
+/*
* core.c - DesignWare USB3 DRD Controller Core file
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -16,18 +16,14 @@
#include <init.h>
#include <linux/reset.h>
-#include "gadget.h"
#include "core.h"
+#include "gadget.h"
#include "io.h"
+#include "debug.h"
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
-struct dwc3_match_data {
- const struct clk_bulk_data *clks;
- const int num_clks;
-};
-
/**
* dwc3_get_dr_mode - Validates and sets dr_mode
* @dwc: pointer to our context structure
@@ -35,7 +31,7 @@ struct dwc3_match_data {
static int dwc3_get_dr_mode(struct dwc3 *dwc)
{
enum usb_dr_mode mode;
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
unsigned int hw_mode;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
@@ -72,8 +68,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
* mode. If the controller supports DRD but the dr_mode is not
* specified or set to OTG, then set the mode to peripheral.
*/
- if (mode == USB_DR_MODE_OTG &&
- dwc->revision >= DWC3_REVISION_330A)
+ if (mode == USB_DR_MODE_OTG && !dwc->edev &&
+ (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
+ !of_property_read_bool(dwc->dev->of_node, "usb-role-switch")) &&
+ !DWC3_VER_IS_PRIOR(DWC3, 330A))
mode = USB_DR_MODE_PERIPHERAL;
}
@@ -100,25 +98,28 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
dwc->current_dr_role = mode;
}
+u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+
+ dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
+ DWC3_GDBGFIFOSPACE_NUM(dep->number) |
+ DWC3_GDBGFIFOSPACE_TYPE(type));
+
+ reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
+
+ return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
+}
+
/**
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
-static int dwc3_core_soft_reset(struct dwc3 *dwc)
+int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
int retries = 1000;
- int ret;
-
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- return ret;
-
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0) {
- phy_exit(dwc->usb2_generic_phy);
- return ret;
- }
/*
* We're resetting only the device side because, if we're in host mode,
@@ -130,33 +131,94 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ reg &= ~DWC3_DCTL_RUN_STOP;
+ dwc3_gadget_dctl_write_safe(dwc, reg);
+
+ /*
+ * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
+ * is cleared only after all the clocks are synchronized. This can
+ * take a little more than 50ms. Set the polling rate at 20ms
+ * for 10 times instead.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
+ retries = 10;
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
- udelay(1);
+ if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
+ mdelay(20);
+ else
+ udelay(1);
} while (--retries);
- phy_exit(dwc->usb3_generic_phy);
- phy_exit(dwc->usb2_generic_phy);
-
+ dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n");
return -ETIMEDOUT;
done:
/*
- * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
- * we must wait at least 50ms before accessing the PHY domain
- * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ * For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit
+ * is cleared, we must wait at least 50ms before accessing the PHY
+ * domain (synchronization delay).
*/
- if (dwc3_is_usb31(dwc))
+ if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
mdelay(50);
return 0;
}
+/*
+ * dwc3_frame_length_adjustment - Adjusts frame length if required
+ * @dwc3: Pointer to our controller context structure
+ */
+static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
+{
+ u32 reg;
+ u32 dft;
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
+ return;
+
+ if (dwc->fladj == 0)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ dft = reg & DWC3_GFLADJ_30MHZ_MASK;
+ if (dft != dwc->fladj) {
+ reg &= ~DWC3_GFLADJ_30MHZ_MASK;
+ reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ }
+}
+
+/**
+ * dwc3_ref_clk_period - Reference clock period configuration
+ * Default reference clock period depends on hardware
+ * configuration. For systems with reference clock that differs
+ * from the default, this will set clock period in DWC3_GUCTL
+ * register.
+ * @dwc: Pointer to our controller context structure
+ */
+static void dwc3_ref_clk_period(struct dwc3 *dwc)
+{
+ u32 reg;
+ u32 dft;
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
+ return;
+
+ if (dwc->fladj == 0)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ dft = reg & DWC3_GFLADJ_30MHZ_MASK;
+ reg &= ~DWC3_GFLADJ_30MHZ_MASK;
+ reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+}
+
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
@@ -165,7 +227,7 @@ done:
static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
- dma_free_coherent(evt->buf, 0, sizeof(dma_addr_t));
+ dma_free_coherent(evt->buf, evt->dma, evt->length);
}
/**
@@ -177,16 +239,20 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
* otherwise ERR_PTR(errno).
*/
static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
- unsigned length)
+ unsigned int length)
{
struct dwc3_event_buffer *evt;
- evt = xzalloc(sizeof(*evt));
+ evt = kzalloc(sizeof(*evt), GFP_KERNEL);
if (!evt)
return ERR_PTR(-ENOMEM);
evt->dwc = dwc;
evt->length = length;
+ evt->cache = kzalloc(length, GFP_KERNEL);
+ if (!evt->cache)
+ return ERR_PTR(-ENOMEM);
+
evt->buf = dma_alloc_coherent(length, &evt->dma);
if (!evt->buf)
return ERR_PTR(-ENOMEM);
@@ -215,7 +281,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
* Returns 0 on success otherwise negative errno. In the error case, dwc
* may contain some buffers allocated but not all which were requested.
*/
-static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
+static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)
{
struct dwc3_event_buffer *evt;
@@ -235,7 +301,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
*
* Returns 0 on success otherwise negative errno.
*/
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -252,8 +318,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
return 0;
}
-
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -296,11 +361,15 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
if (!dwc->nr_scratch)
return 0;
- scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
- dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dwc->dev, scratch_addr)) {
- dev_err(dwc->dev, "failed to map scratch buffer\n");
+ /* should never fall here */
+ if (!WARN_ON(dwc->scratchbuf))
+ return 0;
+
+ scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf,
+ dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dwc->sysdev, scratch_addr)) {
+ dev_err(dwc->sysdev, "failed to map scratch buffer\n");
ret = -EFAULT;
goto err0;
}
@@ -324,33 +393,28 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
return 0;
err1:
- dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
- DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+ dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
err0:
return ret;
}
-/*
- * dwc3_frame_length_adjustment - Adjusts frame length if required
- * @dwc3: Pointer to our controller context structure
- */
-static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
+static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
{
- u32 reg;
- u32 dft;
+ if (!dwc->has_hibernation)
+ return;
- if (dwc->revision < DWC3_REVISION_250A)
+ if (!dwc->nr_scratch)
return;
- if (dwc->fladj == 0)
+ /* should never fall here */
+ if (!WARN_ON(dwc->scratchbuf))
return;
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
- dft = reg & DWC3_GFLADJ_30MHZ_MASK;
- reg &= ~DWC3_GFLADJ_30MHZ_MASK;
- reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+ kfree(dwc->scratchbuf);
}
static void dwc3_core_num_eps(struct dwc3 *dwc)
@@ -373,6 +437,9 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+
+ if (DWC3_IP_IS(DWC32))
+ parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
@@ -401,8 +468,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
*/
static int dwc3_phy_setup(struct dwc3 *dwc)
{
+ unsigned int hw_mode;
u32 reg;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
@@ -417,9 +487,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
* will be '0' when the core is reset. Application needs to set it
* to '1' after the core initialization is completed.
*/
- if (dwc->revision > DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ /*
+ * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after
+ * power-on reset, and it can be set after core initialization, which is
+ * after device soft-reset during initialization.
+ */
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
@@ -470,9 +548,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
- /* FALLTHROUGH */
default:
break;
}
@@ -500,9 +577,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
* be '0' when the core is reset. Application needs to set it to
* '1' after the core initialization is completed.
*/
- if (dwc->revision > DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ /*
+ * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after
+ * power-on reset, and it can be set after core initialization, which is
+ * after device soft-reset during initialization.
+ */
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -511,7 +596,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
else
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
- if (dwc->dis_u2_freeclk_exists_quirk)
+ if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel)
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
@@ -523,14 +608,17 @@ static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
- clk_bulk_disable(dwc->num_clks, dwc->clks);
- dwc3_free_event_buffers(dwc);
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
+ reset_control_assert(dwc->reset);
}
static bool dwc3_core_is_valid(struct dwc3 *dwc)
@@ -538,15 +626,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ dwc->ip = DWC3_GSNPS_ID(reg);
/* This should read as U3 followed by revision number */
- if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
- /* Detected DWC_usb3 IP */
+ if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg;
- } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
- /* Detected DWC_usb31 IP */
+ } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
- dwc->revision |= DWC3_REVISION_IS_DWC31;
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
} else {
return false;
@@ -579,8 +665,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
*/
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
dwc->dr_mode == USB_DR_MODE_OTG) &&
- (dwc->revision >= DWC3_REVISION_210A &&
- dwc->revision <= DWC3_REVISION_250A))
+ DWC3_VER_IS_WITHIN(DWC3, 210A, 250A))
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
else
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
@@ -606,8 +691,8 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
dwc->is_fpga = true;
}
- WARN(dwc->disable_scramble_quirk && !dwc->is_fpga,
- "disable_scramble cannot be used on non-FPGA builds\n");
+ WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga,
+ "disable_scramble cannot be used on non-FPGA builds\n");
if (dwc->disable_scramble_quirk && dwc->is_fpga)
reg |= DWC3_GCTL_DISSCRAMBLE;
@@ -623,25 +708,26 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
- if (dwc->revision < DWC3_REVISION_190A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
static int dwc3_core_get_phy(struct dwc3 *dwc);
+static int dwc3_core_ulpi_init(struct dwc3 *dwc);
/* set global incr burst type configuration registers */
static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
{
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
/* incrx_mode : for INCR burst type. */
bool incrx_mode;
/* incrx_size : for size of INCRX burst. */
u32 incrx_size;
u32 *vals;
u32 cfg;
- int ntype = 0;
+ int ntype;
int ret;
int i;
@@ -654,24 +740,19 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
* result = 1, means INCRx burst mode supported.
* result > 1, means undefined length burst mode supported.
*/
- of_find_property(dev->device_node, "snps,incr-burst-type-adjustment",
- &ntype);
-
- ntype /= sizeof(u32);
-
+ ntype = of_property_count_elems_of_size(dev->of_node,
+ "snps,incr-burst-type-adjustment",
+ sizeof(u32));
if (ntype <= 0)
return;
vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL);
- if (!vals) {
- dev_err(dev, "Error to get memory\n");
+ if (!vals)
return;
- }
/* Get INCR burst type, and parse it */
- ret = of_property_read_u32_array(dev->device_node,
- "snps,incr-burst-type-adjustment",
- vals, ntype);
+ ret = of_property_read_u32_array(dev->of_node,
+ "snps,incr-burst-type-adjustment", vals, ntype);
if (ret) {
kfree(vals);
dev_err(dev, "Error to get property\n");
@@ -738,14 +819,11 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
+ unsigned int hw_mode;
u32 reg;
int ret;
- if (!dwc3_core_is_valid(dwc)) {
- dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
- ret = -ENODEV;
- goto err0;
- }
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
/*
* Write Linux Version Code to our GUID register so it's easy to figure
@@ -753,21 +831,19 @@ static int dwc3_core_init(struct dwc3 *dwc)
*/
dwc3_writel(dwc->regs, DWC3_GUID, 0xdeadbeef);
- /* Handle USB2.0-only core configuration */
- if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
- if (dwc->maximum_speed == USB_SPEED_SUPER)
- dwc->maximum_speed = USB_SPEED_HIGH;
- }
-
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
if (!dwc->ulpi_ready) {
ret = dwc3_core_ulpi_init(dwc);
- if (ret)
+ if (ret) {
+ if (ret == -ETIMEDOUT) {
+ dwc3_core_soft_reset(dwc);
+ ret = -EPROBE_DEFER;
+ }
goto err0;
+ }
dwc->ulpi_ready = true;
}
@@ -778,34 +854,66 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->phys_ready = true;
}
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err0a;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0) {
+ phy_exit(dwc->usb2_generic_phy);
+ goto err0a;
+ }
+
ret = dwc3_core_soft_reset(dwc);
if (ret)
- goto err0a;
+ goto err1;
+
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
+ !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
+ if (!dwc->dis_u3_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ }
+
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+ }
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
ret = dwc3_setup_scratch_buffers(dwc);
if (ret)
- goto err0a;
+ goto err1;
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc);
+ /* Adjust Reference Clock Period */
+ dwc3_ref_clk_period(dwc);
+
dwc3_set_incr_burst_type(dwc);
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0)
- goto err1;
+ goto err2;
ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0)
- goto err2;
+ goto err3;
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err3;
+ goto err4;
}
/*
@@ -813,25 +921,57 @@ static int dwc3_core_init(struct dwc3 *dwc)
* the DWC_usb3 controller. It is NOT available in the
* DWC_usb31 controller.
*/
- if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+ if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
- if (dwc->revision >= DWC3_REVISION_250A) {
+ /*
+ * When configured in HOST mode, after issuing U3/L2 exit controller
+ * fails to send proper CRC checksum in CRC5 feild. Because of this
+ * behaviour Transaction Error is generated, resulting in reset and
+ * re-enumeration of usb device attached. All the termsel, xcvrsel,
+ * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1
+ * will correct this problem. This option is to support certain
+ * legacy ULPI PHYs.
+ */
+ if (dwc->resume_hs_terminations) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
+ if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
/*
* Enable hardware control of sending remote wakeup
* in HS when the device is in the L1 state.
*/
- if (dwc->revision >= DWC3_REVISION_290A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ /*
+ * Decouple USB 2.0 L1 & L2 events which will allow for
+ * gadget driver to only receive U3/L2 suspend & wakeup
+ * events and prevent the more frequent L1 LPM transitions
+ * from interrupting the driver.
+ */
+ if (!DWC3_VER_IS_PRIOR(DWC3, 300A))
+ reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT;
+
if (dwc->dis_tx_ipgap_linecheck_quirk)
reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+ if (dwc->parkmode_disable_ss_quirk)
+ reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+
+ if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
+ (dwc->maximum_speed == USB_SPEED_HIGH ||
+ dwc->maximum_speed == USB_SPEED_FULL))
+ reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@@ -855,7 +995,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
*/
- if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
u8 rx_maxburst = dwc->rx_max_burst_prd;
u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
@@ -890,80 +1030,88 @@ static int dwc3_core_init(struct dwc3 *dwc)
return 0;
-err3:
+err4:
phy_power_off(dwc->usb3_generic_phy);
-err2:
+
+err3:
phy_power_off(dwc->usb2_generic_phy);
+
+err2:
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+
err1:
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
+
err0a:
+ dwc3_ulpi_exit(dwc);
+
err0:
return ret;
}
static int dwc3_core_get_phy(struct dwc3 *dwc)
{
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
int ret;
dwc->usb2_generic_phy = phy_get(dev, "usb2-phy");
if (IS_ERR(dwc->usb2_generic_phy)) {
ret = PTR_ERR(dwc->usb2_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV) {
+ if (ret == -ENOSYS || ret == -ENODEV)
dwc->usb2_generic_phy = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
- } else {
- dev_err(dev, "no usb2 phy configured\n");
- return ret;
- }
+ else
+ return dev_err_probe(dev, ret, "no usb2 phy configured\n");
}
dwc->usb3_generic_phy = phy_get(dev, "usb3-phy");
if (IS_ERR(dwc->usb3_generic_phy)) {
ret = PTR_ERR(dwc->usb3_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV) {
+ if (ret == -ENOSYS || ret == -ENODEV)
dwc->usb3_generic_phy = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
- } else {
- dev_err(dev, "no usb3 phy configured\n");
- return ret;
- }
+ else
+ return dev_err_probe(dev, ret, "no usb3 phy configured\n");
}
return 0;
}
-static int dwc3_set_mode(void *ctx, enum usb_dr_mode mode)
+static int dwc3_set_dr_mode(void *ctx, enum usb_dr_mode mode)
{
struct dwc3 *dwc = ctx;
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
int ret;
switch (mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+// if (dwc->usb2_phy)
+// otg_set_vbus(dwc->usb2_phy->otg, false);
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
+
ret = dwc3_gadget_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize gadget\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize gadget\n");
break;
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+// if (dwc->usb2_phy)
+// otg_set_vbus(dwc->usb2_phy->otg, true);
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+ phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+
ret = dwc3_host_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize host\n");
break;
default:
+ dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
return -EINVAL;
}
@@ -973,20 +1121,43 @@ static int dwc3_set_mode(void *ctx, enum usb_dr_mode mode)
static int dwc3_core_init_mode(struct dwc3 *dwc)
{
if (dwc->dr_mode == USB_DR_MODE_OTG)
- return usb_register_otg_device(dwc->dev, dwc3_set_mode, dwc);
+ return usb_register_otg_device(dwc->dev, dwc3_set_dr_mode, dwc);
else
- return dwc3_set_mode(dwc, dwc->dr_mode);
+ return dwc3_set_dr_mode(dwc, dwc->dr_mode);
+}
+
+static void dwc3_core_exit_mode(struct dwc3 *dwc)
+{
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_gadget_exit(dwc);
+ break;
+ case USB_DR_MODE_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* de-assert DRVVBUS for HOST and OTG mode */
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
static void dwc3_get_properties(struct dwc3 *dwc)
{
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd = 0;
+ u8 rx_max_burst_prd = 0;
+ u8 tx_thr_num_pkt_prd = 0;
+ u8 tx_max_burst_prd = 0;
+ u8 tx_fifo_resize_max_num;
/* default to highest possible threshold */
- lpm_nyet_threshold = 0xff;
+ lpm_nyet_threshold = 0xf;
/* default to -3.5dB de-emphasis */
tx_de_emphasis = 1;
@@ -997,41 +1168,142 @@ static void dwc3_get_properties(struct dwc3 *dwc)
*/
hird_threshold = 12;
- dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL);
- dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL);
- dwc->hsphy_mode = of_usb_get_phy_mode(dev->device_node, NULL);
+ /*
+ * default to a TXFIFO size large enough to fit 6 max packets. This
+ * allows for systems with larger bus latencies to have some headroom
+ * for endpoints that have a large bMaxBurst value.
+ */
+ tx_fifo_resize_max_num = 6;
+
+ dwc->maximum_speed = of_usb_get_maximum_speed(dev->of_node, NULL);
+// dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
+ dwc->dr_mode = of_usb_get_dr_mode(dev->of_node, NULL);
+ dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node, NULL);
+
+ dwc->sysdev_is_parent = of_property_read_bool(dev->of_node,
+ "linux,sysdev_is_parent");
+ if (dwc->sysdev_is_parent)
+ dwc->sysdev = dwc->dev->parent;
+ else
+ dwc->sysdev = dwc->dev;
+
+ dwc->has_lpm_erratum = of_property_read_bool(dev->of_node,
+ "snps,has-lpm-erratum");
+ of_property_read_u8(dev->of_node, "snps,lpm-nyet-threshold",
+ &lpm_nyet_threshold);
+ dwc->is_utmi_l1_suspend = of_property_read_bool(dev->of_node,
+ "snps,is-utmi-l1-suspend");
+ of_property_read_u8(dev->of_node, "snps,hird-threshold",
+ &hird_threshold);
+ dwc->dis_start_transfer_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-start-transfer-quirk");
+ dwc->usb3_lpm_capable = of_property_read_bool(dev->of_node,
+ "snps,usb3_lpm_capable");
+ dwc->usb2_lpm_disable = of_property_read_bool(dev->of_node,
+ "snps,usb2-lpm-disable");
+ dwc->usb2_gadget_lpm_disable = of_property_read_bool(dev->of_node,
+ "snps,usb2-gadget-lpm-disable");
+ of_property_read_u8(dev->of_node, "snps,rx-thr-num-pkt-prd",
+ &rx_thr_num_pkt_prd);
+ of_property_read_u8(dev->of_node, "snps,rx-max-burst-prd",
+ &rx_max_burst_prd);
+ of_property_read_u8(dev->of_node, "snps,tx-thr-num-pkt-prd",
+ &tx_thr_num_pkt_prd);
+ of_property_read_u8(dev->of_node, "snps,tx-max-burst-prd",
+ &tx_max_burst_prd);
+ dwc->do_fifo_resize = of_property_read_bool(dev->of_node,
+ "tx-fifo-resize");
+ if (dwc->do_fifo_resize)
+ of_property_read_u8(dev->of_node, "tx-fifo-max-num",
+ &tx_fifo_resize_max_num);
+
+ dwc->disable_scramble_quirk = of_property_read_bool(dev->of_node,
+ "snps,disable_scramble_quirk");
+ dwc->u2exit_lfps_quirk = of_property_read_bool(dev->of_node,
+ "snps,u2exit_lfps_quirk");
+ dwc->u2ss_inp3_quirk = of_property_read_bool(dev->of_node,
+ "snps,u2ss_inp3_quirk");
+ dwc->req_p1p2p3_quirk = of_property_read_bool(dev->of_node,
+ "snps,req_p1p2p3_quirk");
+ dwc->del_p1p2p3_quirk = of_property_read_bool(dev->of_node,
+ "snps,del_p1p2p3_quirk");
+ dwc->del_phy_power_chg_quirk = of_property_read_bool(dev->of_node,
+ "snps,del_phy_power_chg_quirk");
+ dwc->lfps_filter_quirk = of_property_read_bool(dev->of_node,
+ "snps,lfps_filter_quirk");
+ dwc->rx_detect_poll_quirk = of_property_read_bool(dev->of_node,
+ "snps,rx_detect_poll_quirk");
+ dwc->dis_u3_susphy_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis_u3_susphy_quirk");
+ dwc->dis_u2_susphy_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis_u2_susphy_quirk");
+ dwc->dis_enblslpm_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis_enblslpm_quirk");
+ dwc->dis_u1_entry_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-u1-entry-quirk");
+ dwc->dis_u2_entry_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-u2-entry-quirk");
+ dwc->dis_rxdet_inp3_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis_rxdet_inp3_quirk");
+ dwc->dis_u2_freeclk_exists_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-u2-freeclk-exists-quirk");
+ dwc->dis_del_phy_power_chg_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-del-phy-power-chg-quirk");
+ dwc->dis_tx_ipgap_linecheck_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-tx-ipgap-linecheck-quirk");
+ dwc->resume_hs_terminations = of_property_read_bool(dev->of_node,
+ "snps,resume-hs-terminations");
+ dwc->parkmode_disable_ss_quirk = of_property_read_bool(dev->of_node,
+ "snps,parkmode-disable-ss-quirk");
+ dwc->gfladj_refclk_lpm_sel = of_property_read_bool(dev->of_node,
+ "snps,gfladj-refclk-lpm-sel-quirk");
+
+ dwc->tx_de_emphasis_quirk = of_property_read_bool(dev->of_node,
+ "snps,tx_de_emphasis_quirk");
+ of_property_read_u8(dev->of_node, "snps,tx_de_emphasis",
+ &tx_de_emphasis);
+ of_property_read_string(dev->of_node, "snps,hsphy_interface",
+ &dwc->hsphy_interface);
+ of_property_read_u32(dev->of_node, "snps,quirk-frame-length-adjustment",
+ &dwc->fladj);
+ of_property_read_u32(dev->of_node, "snps,ref-clock-period-ns",
+ &dwc->ref_clk_per);
+
+ dwc->dis_metastability_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis_metastability_quirk");
+
+ dwc->dis_split_quirk = of_property_read_bool(dev->of_node,
+ "snps,dis-split-quirk");
- dwc->dis_u2_freeclk_exists_quirk = of_property_read_bool(dev->device_node,
- "snps,dis-u2-freeclk-exists-quirk");
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
- if (of_get_property(dev->device_node, "snps,dis_rxdet_inp3_quirk",
- NULL))
- dwc->dis_rxdet_inp3_quirk = 1;
+ dwc->hird_threshold = hird_threshold;
- of_property_read_u32_array(dev->device_node,
- "snps,quirk-frame-length-adjustment",
- &dwc->fladj, 1);
+ dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
+ dwc->rx_max_burst_prd = rx_max_burst_prd;
- dwc->hird_threshold = hird_threshold
- | (dwc->is_utmi_l1_suspend << 4);
+ dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
+ dwc->tx_max_burst_prd = tx_max_burst_prd;
dwc->imod_interval = 0;
+
+ dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
}
/* check whether the core supports IMOD */
bool dwc3_has_imod(struct dwc3 *dwc)
{
- return ((dwc3_is_usb3(dwc) &&
- dwc->revision >= DWC3_REVISION_300A) ||
- (dwc3_is_usb31(dwc) &&
- dwc->revision >= DWC3_USB31_REVISION_120A));
+ return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
+ DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
+ DWC3_IP_IS(DWC32);
}
static void dwc3_check_params(struct dwc3 *dwc)
{
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
+ unsigned int hwparam_gen =
+ DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
/* Check for proper value of imod_interval */
if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
@@ -1047,81 +1319,106 @@ static void dwc3_check_params(struct dwc3 *dwc)
* affected version.
*/
if (!dwc->imod_interval &&
- (dwc->revision == DWC3_REVISION_300A))
+ DWC3_VER_IS(DWC3, 300A))
dwc->imod_interval = 1;
/* Check the maximum_speed parameter */
switch (dwc->maximum_speed) {
- case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
+ break;
case USB_SPEED_SUPER:
+ if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS)
+ dev_warn(dev, "UDC doesn't support Gen 1\n");
+ break;
case USB_SPEED_SUPER_PLUS:
+ if ((DWC3_IP_IS(DWC32) &&
+ hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) ||
+ (!DWC3_IP_IS(DWC32) &&
+ hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dev_warn(dev, "UDC doesn't support SSP\n");
break;
default:
dev_err(dev, "invalid maximum_speed parameter %d\n",
dwc->maximum_speed);
- /* fall through */
+ fallthrough;
case USB_SPEED_UNKNOWN:
- /* default to superspeed */
- dwc->maximum_speed = USB_SPEED_SUPER;
-
- /*
- * default to superspeed plus if we are capable.
- */
- if (dwc3_is_usb31(dwc) &&
- (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ switch (hwparam_gen) {
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
-
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
+ if (DWC3_IP_IS(DWC32))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+ else
+ dwc->maximum_speed = USB_SPEED_SUPER;
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_DIS:
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ break;
+ default:
+ dwc->maximum_speed = USB_SPEED_SUPER;
+ break;
+ }
break;
}
-}
-
-static void dwc3_coresoft_reset(struct dwc3 *dwc)
-{
- u32 reg;
-
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg |= DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
/*
- * Similar reset sequence in U-Boot has a 100ms delay here. In
- * practice reset sequence seem to work as expected even
- * without a delay.
+ * Currently the controller does not have visibility into the HW
+ * parameter to determine the maximum number of lanes the HW supports.
+ * If the number of lanes is not specified in the device property, then
+ * set the default to support dual-lane for DWC_usb32 and single-lane
+ * for DWC_usb31 for super-speed-plus.
*/
-
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg &= ~DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ if (dwc->maximum_speed == USB_SPEED_SUPER_PLUS) {
+ switch (dwc->max_ssp_rate) {
+ case USB_SSP_GEN_2x1:
+ if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_GEN1)
+ dev_warn(dev, "UDC only supports Gen 1\n");
+ break;
+ case USB_SSP_GEN_1x2:
+ case USB_SSP_GEN_2x2:
+ if (DWC3_IP_IS(DWC31))
+ dev_warn(dev, "UDC only supports single lane\n");
+ break;
+ case USB_SSP_GEN_UNKNOWN:
+ default:
+ switch (hwparam_gen) {
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
+ if (DWC3_IP_IS(DWC32))
+ dwc->max_ssp_rate = USB_SSP_GEN_2x2;
+ else
+ dwc->max_ssp_rate = USB_SSP_GEN_2x1;
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
+ if (DWC3_IP_IS(DWC32))
+ dwc->max_ssp_rate = USB_SSP_GEN_1x2;
+ break;
+ }
+ break;
+ }
+ }
}
-static int dwc3_probe(struct device_d *dev)
+static int dwc3_probe(struct device *dev)
{
- const struct dwc3_match_data *match;
struct dwc3 *dwc;
int ret;
dwc = xzalloc(sizeof(*dwc));
dev->priv = dwc;
- match = device_get_match_data(dev);
- dwc->clks = xmemdup(match->clks, match->num_clks *
- sizeof(struct clk_bulk_data));
dwc->dev = dev;
dwc->regs = dev_get_mem_region(dwc->dev, 0) + DWC3_GLOBALS_REGS_START;
dwc3_get_properties(dwc);
- if (dev->device_node) {
- dwc->num_clks = match->num_clks;
+ if (dev->of_node) {
+ ret = clk_bulk_get_all(dev, &dwc->clks);
+ if (ret < 0)
+ return ret;
- if (of_find_property(dev->device_node, "clocks", NULL)) {
- ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
- if (ret)
- return ret;
- }
+ dwc->num_clks = ret;
}
ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
@@ -1138,7 +1435,12 @@ static int dwc3_probe(struct device_d *dev)
mdelay(1);
reset_control_deassert(dwc->reset);
- dwc3_coresoft_reset(dwc);
+ if (!dwc3_core_is_valid(dwc)) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ return -ENODEV;
+ }
+
+ dwc3_core_soft_reset(dwc);
dwc3_cache_hwparams(dwc);
@@ -1172,53 +1474,32 @@ static int dwc3_probe(struct device_d *dev)
return 0;
}
-static void dwc3_remove(struct device_d *dev)
+static void dwc3_remove(struct device *dev)
{
struct dwc3 *dwc = dev->priv;
+ dwc3_core_exit_mode(dwc);
dwc3_core_exit(dwc);
clk_bulk_put(dwc->num_clks, dwc->clks);
+ dwc3_free_event_buffers(dwc);
+ dwc3_free_scratch_buffers(dwc);
}
-static const struct clk_bulk_data dwc3_core_clks[] = {
- { .id = "ref" },
- { .id = "bus_early" },
- { .id = "suspend" },
-};
-
-static const struct dwc3_match_data dwc3_default = {
- .clks = dwc3_core_clks,
- .num_clks = ARRAY_SIZE(dwc3_core_clks),
-};
-
-static const struct clk_bulk_data dwc3_core_clks_rk3568[] = {
- { .id = "ref_clk" },
- { .id = "bus_clk" },
- { .id = "suspend_clk" },
-};
-
-static const struct dwc3_match_data dwc3_rk3568 = {
- .clks = dwc3_core_clks_rk3568,
- .num_clks = ARRAY_SIZE(dwc3_core_clks_rk3568),
-};
-
static const struct of_device_id of_dwc3_match[] = {
{
.compatible = "snps,dwc3",
- .data = &dwc3_default,
},
{
.compatible = "synopsys,dwc3",
- .data = &dwc3_default,
},
{
.compatible = "rockchip,rk3568-dwc3",
- .data = &dwc3_rk3568,
},
{ },
};
+MODULE_DEVICE_TABLE(of, of_dwc3_match);
-static struct driver_d dwc3_driver = {
+static struct driver dwc3_driver = {
.probe = dwc3_probe,
.remove = dwc3_remove,
.name = "dwc3",
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 96e8d0d250..52853a4370 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0-only
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* core.h - DesignWare USB3 DRD Core Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -12,9 +12,11 @@
#define __DRIVERS_USB_DWC3_CORE_H
#include <linux/spinlock.h>
-#include <usb/usb.h>
-#include <usb/phy.h>
-#include <usb/gadget.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/gadget.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
#define DWC3_MSG_MAX 500
@@ -40,7 +42,7 @@
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
#define DWC3_DEVICE_EVENT_WAKEUP 4
#define DWC3_DEVICE_EVENT_HIBER_REQ 5
-#define DWC3_DEVICE_EVENT_EOPF 6
+#define DWC3_DEVICE_EVENT_SUSPEND 6
#define DWC3_DEVICE_EVENT_SOF 7
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
@@ -55,6 +57,7 @@
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
+#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)
/* DWC3 registers memory space boundries */
#define DWC3_XHCI_REGS_START 0x0
@@ -123,7 +126,9 @@
#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
#define DWC3_GHWPARAMS8 0xc600
+#define DWC3_GUCTL3 0xc60c
#define DWC3_GFLADJ 0xc630
+#define DWC3_GHWPARAMS9 0xc6e0
/* Device Registers */
#define DWC3_DCFG 0xc700
@@ -133,6 +138,7 @@
#define DWC3_DGCMDPAR 0xc710
#define DWC3_DGCMD 0xc714
#define DWC3_DALEPENA 0xc720
+#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
#define DWC3_DEPCMDPAR2 0x00
@@ -210,6 +216,7 @@
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
+#define DWC3_GCTL_PWRDNSCALE_MASK GENMASK(31, 19)
#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
@@ -236,8 +243,12 @@
#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
/* Global User Control 1 Register */
+#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
-#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
+#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10)
/* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10)
@@ -268,6 +279,7 @@
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25)
+#define DWC3_GUSB2PHYACC_DONE BIT(24)
#define DWC3_GUSB2PHYACC_BUSY BIT(23)
#define DWC3_GUSB2PHYACC_WRITE BIT(22)
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
@@ -292,10 +304,14 @@
/* Global TX Fifo Size Register */
#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
-#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
+#define DWC31_GTXFIFOSIZ_TXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
+#define DWC3_GTXFIFOSIZ_TXFDEP(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+/* Global RX Fifo Size Register */
+#define DWC31_GRXFIFOSIZ_RXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
+#define DWC3_GRXFIFOSIZ_RXFDEP(n) ((n) & 0xffff)
+
/* Global Event Size Registers */
#define DWC3_GEVNTSIZ_INTMASK BIT(31)
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
@@ -346,18 +362,38 @@
#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
+/* DWC_usb32 only */
+#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8))
+
/* Global HWPARAMS7 Register */
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
+/* Global HWPARAMS9 Register */
+#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS BIT(0)
+#define DWC3_GHWPARAMS9_DEV_MST BIT(1)
+
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
+#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23)
+#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
+#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
+
+/* Global User Control Register*/
+#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
+#define DWC3_GUCTL_REFCLKPER_SEL 22
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
+/* Global User Control Register 3 */
+#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
+
/* Device Configuration Register */
+#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */
+
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -366,13 +402,12 @@
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
#define DWC3_DCFG_FULLSPEED BIT(0)
-#define DWC3_DCFG_LOWSPEED (2 << 0)
-#define DWC3_DCFG_FULLSPEED1 (3 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
#define DWC3_DCFG_LPM_CAP BIT(22)
+#define DWC3_DCFG_IGNSTRMPP BIT(23)
/* Device Control Register */
#define DWC3_DCTL_RUN_STOP BIT(31)
@@ -394,8 +429,7 @@
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
/* These apply for core versions 1.94a and later */
-#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
-#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
+#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20)
#define DWC3_DCTL_KEEP_CONNECT BIT(19)
#define DWC3_DCTL_L1_HIBER_EN BIT(18)
@@ -425,7 +459,7 @@
#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
#define DWC3_DEVTEN_ERRTICERREN BIT(9)
#define DWC3_DEVTEN_SOFEN BIT(7)
-#define DWC3_DEVTEN_EOPFEN BIT(6)
+#define DWC3_DEVTEN_U3L2L1SUSPEN BIT(6)
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
@@ -433,6 +467,8 @@
#define DWC3_DEVTEN_USBRSTEN BIT(1)
#define DWC3_DEVTEN_DISCONNEVTEN BIT(0)
+#define DWC3_DSTS_CONNLANES(n) (((n) >> 30) & 0x3) /* DWC_usb32 only */
+
/* Device Status Register */
#define DWC3_DSTS_DCNRD BIT(29)
@@ -460,8 +496,6 @@
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
#define DWC3_DSTS_FULLSPEED BIT(0)
-#define DWC3_DSTS_LOWSPEED (2 << 0)
-#define DWC3_DSTS_FULLSPEED1 (3 << 0)
/* Device Generic Command Register */
#define DWC3_DGCMD_SET_LMP 0x01
@@ -475,6 +509,7 @@
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
+#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
@@ -517,6 +552,9 @@
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
#define DWC3_DALEPENA_EP(n) BIT(n)
+/* DWC_usb32 DCFG1 config */
+#define DWC3_DCFG1_DIS_MST_ENH BIT(1)
+
#define DWC3_DEPCMD_TYPE_CONTROL 0
#define DWC3_DEPCMD_TYPE_ISOC 1
#define DWC3_DEPCMD_TYPE_BULK 2
@@ -602,16 +640,26 @@ struct dwc3_trb;
/**
* struct dwc3_event_buffer - Software event buffer representation
* @buf: _THE_ buffer
+ * @cache: The buffer cache used in the threaded interrupt
* @length: size of this buffer
* @lpos: event offset
+ * @count: cache of last read event count register
+ * @flags: flags related to this event buffer
* @dma: dma_addr_t
* @dwc: pointer to DWC controller
*/
struct dwc3_event_buffer {
void *buf;
- unsigned length;
+ void *cache;
+ unsigned int length;
unsigned int lpos;
+ unsigned int count;
+ unsigned int flags;
+
+#define DWC3_EVENT_PENDING BIT(0)
+
dma_addr_t dma;
+
struct dwc3 *dwc;
};
@@ -622,57 +670,66 @@ struct dwc3_event_buffer {
#define DWC3_EP_DIRECTION_RX false
#define DWC3_TRB_NUM 256
-#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1)
/**
* struct dwc3_ep - device side endpoint representation
* @endpoint: usb endpoint
- * @pending_list: list of requests for this endpoint
+ * @cancelled_list: list of cancelled requests for this endpoint
+ * @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
+ * @regs: pointer to first endpoint register
* @trb_pool: array of transaction buffers
* @trb_pool_dma: dma address of @trb_pool
- * @free_slot: next slot which is going to be used
- * @busy_slot: first slot which is owned by HW
- * @desc: usb_endpoint_descriptor pointer
+ * @trb_enqueue: enqueue 'pointer' into TRB array
+ * @trb_dequeue: dequeue 'pointer' into TRB array
* @dwc: pointer to DWC controller
* @saved_state: ep state saved during hibernation
* @flags: endpoint flags (wedged, stalled, ...)
- * @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
+ * @frame_number: set to the frame number we want this transfer to start (ISOC)
* @interval: the interval on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
* @stream_capable: true when streams are enabled
+ * @combo_num: the test combination BIT[15:14] of the frame number to test
+ * isochronous START TRANSFER command failure workaround
+ * @start_cmd_status: the status of testing START TRANSFER command with
+ * combo_num = 'b00
*/
struct dwc3_ep {
- struct usb_ep endpoint;
- struct list_head cancelled_list;
- struct list_head pending_list;
- struct list_head started_list;
-
- void __iomem *regs;
-
- struct dwc3_trb *trb_pool;
- dma_addr_t trb_pool_dma;
- u32 free_slot;
- u32 busy_slot;
- const struct usb_ss_ep_comp_descriptor *comp_desc;
- struct dwc3 *dwc;
-
- u32 saved_state;
- unsigned flags;
+ struct usb_ep endpoint;
+ struct list_head cancelled_list;
+ struct list_head pending_list;
+ struct list_head started_list;
+
+ void __iomem *regs;
+
+ struct dwc3_trb *trb_pool;
+ dma_addr_t trb_pool_dma;
+ struct dwc3 *dwc;
+
+ u32 saved_state;
+ unsigned int flags;
#define DWC3_EP_ENABLED BIT(0)
-#define DWC3_EP_STALL BIT(1)
-#define DWC3_EP_WEDGE BIT(2)
-#define DWC3_EP_TRANSFER_STARTED BIT(3)
-#define DWC3_EP_PENDING_REQUEST BIT(4)
+#define DWC3_EP_STALL BIT(1)
+#define DWC3_EP_WEDGE BIT(2)
+#define DWC3_EP_TRANSFER_STARTED BIT(3)
+#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
+#define DWC3_EP_PENDING_REQUEST BIT(5)
+#define DWC3_EP_DELAY_START BIT(6)
+#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
+#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
+#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
+#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
+#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
+#define DWC3_EP_TXFIFO_RESIZED BIT(12)
+#define DWC3_EP_DELAY_STOP BIT(13)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
- unsigned current_trb;
/*
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
* use a u8 type here. If anybody decides to increase number of TRBs to
@@ -682,23 +739,23 @@ struct dwc3_ep {
* By using u8 types we ensure that our % operator when incrementing
* enqueue and dequeue get optimized away by the compiler.
*/
- u8 trb_enqueue;
- u8 trb_dequeue;
+ u8 trb_enqueue;
+ u8 trb_dequeue;
- u8 number;
- u8 type;
- u8 resource_index;
- u32 frame_number;
- u32 interval;
+ u8 number;
+ u8 type;
+ u8 resource_index;
+ u32 frame_number;
+ u32 interval;
- char name[20];
+ char name[20];
- unsigned direction:1;
- unsigned stream_capable:1;
+ unsigned direction:1;
+ unsigned stream_capable:1;
/* For isochronous START TRANSFER workaround only */
- u8 combo_num;
- int start_cmd_status;
+ u8 combo_num;
+ int start_cmd_status;
};
enum dwc3_phy {
@@ -797,6 +854,7 @@ struct dwc3_trb {
* @hwparams6: GHWPARAMS6
* @hwparams7: GHWPARAMS7
* @hwparams8: GHWPARAMS8
+ * @hwparams9: GHWPARAMS9
*/
struct dwc3_hwparams {
u32 hwparams0;
@@ -808,13 +866,12 @@ struct dwc3_hwparams {
u32 hwparams6;
u32 hwparams7;
u32 hwparams8;
+ u32 hwparams9;
};
/* HWPARAMS0 */
#define DWC3_MODE(n) ((n) & 0x7)
-#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8)
-
/* HWPARAMS1 */
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
@@ -829,31 +886,67 @@ struct dwc3_hwparams {
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
-struct dwc3_request {
- struct usb_request request;
- struct list_head list;
- struct dwc3_ep *dep;
- u32 start_slot;
-
- unsigned remaining;
-
- unsigned int status;
-#define DWC3_REQUEST_STATUS_QUEUED 0
-#define DWC3_REQUEST_STATUS_STARTED 1
-#define DWC3_REQUEST_STATUS_CANCELLED 2
-#define DWC3_REQUEST_STATUS_COMPLETED 3
-#define DWC3_REQUEST_STATUS_UNKNOWN -1
+/* HWPARAMS9 */
+#define DWC3_MST_CAPABLE(p) (!!((p)->hwparams9 & \
+ DWC3_GHWPARAMS9_DEV_MST))
- u8 epnum;
+/**
+ * struct dwc3_request - representation of a transfer request
+ * @request: struct usb_request to be transferred
+ * @list: a list_head used for request queueing
+ * @dep: struct dwc3_ep owning this request
+ * @sg: pointer to first incomplete sg
+ * @start_sg: pointer to the sg which should be queued next
+ * @num_pending_sgs: counter to pending sgs
+ * @num_queued_sgs: counter to the number of sgs which already got queued
+ * @remaining: amount of data remaining
+ * @status: internal dwc3 request status tracking
+ * @epnum: endpoint number to which this request refers
+ * @trb: pointer to struct dwc3_trb
+ * @trb_dma: DMA address of @trb
+ * @num_trbs: number of TRBs used by this request
+ * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
+ * or unaligned OUT)
+ * @direction: IN or OUT direction flag
+ * @mapped: true when request has been dma-mapped
+ */
+struct dwc3_request {
+ struct usb_request request;
+ struct list_head list;
+ struct dwc3_ep *dep;
+ struct scatterlist *sg;
+ struct scatterlist *start_sg;
+
+ unsigned int num_pending_sgs;
+ unsigned int num_queued_sgs;
+ unsigned int remaining;
+
+ unsigned int status;
+#define DWC3_REQUEST_STATUS_QUEUED 0
+#define DWC3_REQUEST_STATUS_STARTED 1
+#define DWC3_REQUEST_STATUS_DISCONNECTED 2
+#define DWC3_REQUEST_STATUS_DEQUEUED 3
+#define DWC3_REQUEST_STATUS_STALLED 4
+#define DWC3_REQUEST_STATUS_COMPLETED 5
+#define DWC3_REQUEST_STATUS_UNKNOWN -1
+
+ u8 epnum;
struct dwc3_trb *trb;
- dma_addr_t trb_dma;
+ dma_addr_t trb_dma;
+
+ unsigned int num_trbs;
- unsigned num_trbs;
+ unsigned int needs_extra_trb:1;
+ unsigned int direction:1;
+ unsigned int mapped:1;
+};
- unsigned needs_extra_trb:1;
- unsigned direction:1;
- unsigned mapped:1;
- unsigned queued:1;
+/*
+ * struct dwc3_scratchpad_array - hibernation scratchpad array
+ * (format defined by hw)
+ */
+struct dwc3_scratchpad_array {
+ __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
};
/**
@@ -869,6 +962,7 @@ struct dwc3_request {
* @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
+ * @mutex: for mode switching
* @dev: pointer to our struct device
* @sysdev: pointer to the DMA-capable device
* @xhci: pointer to our xHCI child
@@ -877,12 +971,14 @@ struct dwc3_request {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
- * @clks: array of clocks
- * @num_clks: number of clocks
+ * @bus_clk: clock for accessing the registers
+ * @ref_clk: reference clock
+ * @susp_clk: clock used when the SS phy is in low power (S3) state
* @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
* @fladj: frame length adjustment
+ * @ref_clk_per: reference clock period configuration
* @irq_gadget: peripheral controller's IRQ number
* @otg_irq: IRQ number for OTG IRQs
* @current_otg_role: current role of operation while using the OTG block
@@ -891,7 +987,12 @@ struct dwc3_request {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
- * @revision: revision register contents
+ * @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count
+ * @gadget_max_speed: maximum gadget speed requested
+ * @gadget_ssp_rate: Gadget driver's maximum supported SuperSpeed Plus signaling
+ * rate and lane count.
+ * @ip: controller's ID
+ * @revision: controller's version of an IP
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
* @current_dr_role: current role of operation when in dual-role mode
@@ -901,6 +1002,10 @@ struct dwc3_request {
* @hsphy_mode: UTMI phy mode, one of following:
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
+ * @role_sw: usb_role_switch handle
+ * @role_switch_default_mode: default operation mode of controller while
+ * usb role is USB_ROLE_NONE.
+ * @usb_psy: pointer to power supply interface.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -918,7 +1023,6 @@ struct dwc3_request {
* @link_state: link state
* @speed: device speed (super, high, full, low)
* @hwparams: copy of hwparams registers
- * @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
* @dbg_lsp_select: current debug lsp mux register selection
* @test_mode: true when we're entering a USB test mode
@@ -929,8 +1033,11 @@ struct dwc3_request {
* @rx_max_burst_prd: max periodic ESS receive burst size
* @tx_thr_num_pkt_prd: periodic ESS transmit packet count
* @tx_max_burst_prd: max periodic ESS transmit burst size
+ * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
+ * @clear_stall_protocol: endpoint number that requires a delayed status phase
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
+ * @softconnect: true when gadget connect is called, false when disconnect runs
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
@@ -939,17 +1046,19 @@ struct dwc3_request {
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
- * 0 - utmi_sleep_n
- * 1 - utmi_l1_suspend_n
+ * 0 - utmi_sleep_n
+ * 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
* @pending_events: true when we have pending IRQs to be handled
+ * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints
* @pullups_connected: true when Run/Stop bit is set
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @three_stage_setup: set if we perform a three phase setup
* @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
* not needed for DWC_usb31 version 1.70a-ea06 and below
* @usb3_lpm_capable: set if hadrware supports Link Power Management
- * @usb2_lpm_disable: set to disable usb2 lpm
+ * @usb2_lpm_disable: set to disable usb2 lpm for host
+ * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -962,7 +1071,11 @@ struct dwc3_request {
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
* disabling the suspend signal to the PHY.
+ * @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled.
+ * @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled.
* @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3
+ * @async_callbacks: if set, indicate that async callbacks will be used.
+ *
* @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
* in GUSB2PHYCFG, specify that USB2 PHY doesn't
* provide a free-running PHY clock.
@@ -970,55 +1083,89 @@ struct dwc3_request {
* change quirk.
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
* check during HS transmit.
+ * @resume-hs-terminations: Set if we enable quirk for fixing improper crc
+ * generation after resume from suspend.
+ * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
+ * instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
- * 0 - -6dB de-emphasis
- * 1 - -3.5dB de-emphasis
- * 2 - No de-emphasis
- * 3 - Reserved
+ * 0 - -6dB de-emphasis
+ * 1 - -3.5dB de-emphasis
+ * 2 - No de-emphasis
+ * 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
+ * @dis_split_quirk: set to disable split boundary.
* @imod_interval: set the interrupt moderation interval in 250ns
- * increments or 0 to disable.
+ * increments or 0 to disable.
+ * @max_cfg_eps: current max number of IN eps used across all USB configs.
+ * @last_fifo_depth: last fifo depth used to determine next fifo ram start
+ * address.
+ * @num_ep_resized: carries the current number endpoints which have had its tx
+ * fifo resized.
+ * @debug_root: root debugfs directory for this device to put its files in.
*/
struct dwc3 {
+ struct work_struct drd_work;
struct dwc3_trb *ep0_trb;
- void *bounce;
- void *scratchbuf;
- u8 *setup_buf;
- dma_addr_t ep0_trb_addr;
- dma_addr_t bounce_addr;
- dma_addr_t scratch_addr;
- struct dwc3_request ep0_usb_req;
+ void *bounce;
+ void *scratchbuf;
+ u8 *setup_buf;
+ dma_addr_t ep0_trb_addr;
+ dma_addr_t bounce_addr;
+ dma_addr_t scratch_addr;
+ struct dwc3_request ep0_usb_req;
+ struct completion ep0_in_setup;
- struct device_d *dev;
+ /* device lock */
+ spinlock_t lock;
- struct device_d *xhci;
+ /* mode switching lock */
+ struct mutex mutex;
- struct dwc3_event_buffer *ev_buf;
- struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
+ struct device *dev;
+ struct device *sysdev;
- struct usb_gadget gadget;
- struct usb_gadget_driver *gadget_driver;
+ struct device *xhci;
+ struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
+
+ struct dwc3_event_buffer *ev_buf;
+ struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
+
+ struct usb_gadget *gadget;
+ struct usb_gadget_driver *gadget_driver;
struct clk_bulk_data *clks;
int num_clks;
struct reset_control *reset;
+ struct usb_phy *usb2_phy;
+ struct usb_phy *usb3_phy;
+
struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy;
bool phys_ready;
+
+ struct ulpi *ulpi;
bool ulpi_ready;
void __iomem *regs;
+ size_t regs_size;
enum usb_dr_mode dr_mode;
u32 current_dr_role;
u32 desired_dr_role;
+ struct extcon_dev *edev;
+ struct notifier_block edev_nb;
enum usb_phy_interface hsphy_mode;
+ struct usb_role_switch *role_sw;
+ enum usb_dr_mode role_switch_default_mode;
+
+ struct power_supply *usb_psy;
u32 fladj;
+ u32 ref_clk_per;
u32 irq_gadget;
u32 otg_irq;
u32 current_otg_role;
@@ -1027,16 +1174,19 @@ struct dwc3 {
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
+ u32 gadget_max_speed;
+ enum usb_ssp_rate max_ssp_rate;
+ enum usb_ssp_rate gadget_ssp_rate;
+
+ u32 ip;
+
+#define DWC3_IP 0x5533
+#define DWC31_IP 0x3331
+#define DWC32_IP 0x3332
- /*
- * All 3.1 IP version constants are greater than the 3.0 IP
- * version constants. This works for most version checks in
- * dwc3. However, in the future, this may not apply as
- * features may be developed on newer versions of the 3.0 IP
- * that are not in the 3.1 IP.
- */
u32 revision;
+#define DWC3_REVISION_ANY 0x0
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
#define DWC3_REVISION_180A 0x5533180a
@@ -1061,18 +1211,20 @@ struct dwc3 {
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_330A 0x5533330a
-/*
- * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
- * just so dwc31 revisions are always larger than dwc3.
- */
-#define DWC3_REVISION_IS_DWC31 0x80000000
-#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
+#define DWC31_REVISION_ANY 0x0
+#define DWC31_REVISION_110A 0x3131302a
+#define DWC31_REVISION_120A 0x3132302a
+#define DWC31_REVISION_160A 0x3136302a
+#define DWC31_REVISION_170A 0x3137302a
+#define DWC31_REVISION_180A 0x3138302a
+#define DWC31_REVISION_190A 0x3139302a
+
+#define DWC32_REVISION_ANY 0x0
+#define DWC32_REVISION_100A 0x3130302a
u32 version_type;
+#define DWC31_VERSIONTYPE_ANY 0x0
#define DWC31_VERSIONTYPE_EA01 0x65613031
#define DWC31_VERSIONTYPE_EA02 0x65613032
#define DWC31_VERSIONTYPE_EA03 0x65613033
@@ -1084,7 +1236,6 @@ struct dwc3 {
enum dwc3_ep0_state ep0state;
enum dwc3_link_state link_state;
- u16 isoch_delay;
u16 u2sel;
u16 u2pel;
u8 u1sel;
@@ -1095,6 +1246,7 @@ struct dwc3 {
u8 num_eps;
struct dwc3_hwparams hwparams;
+ struct debugfs_regset32 *regset;
u32 dbg_lsp_select;
@@ -1106,10 +1258,13 @@ struct dwc3 {
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
+ u8 tx_fifo_resize_max_num;
+ u8 clear_stall_protocol;
const char *hsphy_interface;
unsigned connected:1;
+ unsigned softconnect:1;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
@@ -1117,18 +1272,16 @@ struct dwc3 {
unsigned sysdev_is_parent:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
- unsigned is_selfpowered:1;
unsigned is_fpga:1;
unsigned pending_events:1;
- unsigned needs_fifo_resize:1;
+ unsigned do_fifo_resize:1;
unsigned pullups_connected:1;
- unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
- unsigned start_config_issued:1;
unsigned three_stage_setup:1;
unsigned dis_start_transfer_quirk:1;
unsigned usb3_lpm_capable:1;
unsigned usb2_lpm_disable:1;
+ unsigned usb2_gadget_lpm_disable:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
@@ -1141,17 +1294,30 @@ struct dwc3 {
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1;
+ unsigned dis_u1_entry_quirk:1;
+ unsigned dis_u2_entry_quirk:1;
unsigned dis_rxdet_inp3_quirk:1;
unsigned dis_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1;
unsigned dis_tx_ipgap_linecheck_quirk:1;
+ unsigned resume_hs_terminations:1;
+ unsigned parkmode_disable_ss_quirk:1;
+ unsigned gfladj_refclk_lpm_sel:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
unsigned dis_metastability_quirk:1;
+ unsigned dis_split_quirk:1;
+ unsigned async_callbacks:1;
+
u16 imod_interval;
+
+ int max_cfg_eps;
+ int last_fifo_depth;
+ int num_ep_resized;
+ struct dentry *debug_root;
};
#define INCRX_BURST_MODE 0
@@ -1175,31 +1341,7 @@ struct dwc3_event_type {
#define DWC3_DEPEVT_EPCMDCMPLT 0x07
/**
- * dwc3_ep_event_string - returns event name
- * @event: then event code
- */
-static inline const char *dwc3_ep_event_string(u8 event)
-{
- switch (event) {
- case DWC3_DEPEVT_XFERCOMPLETE:
- return "Transfer Complete";
- case DWC3_DEPEVT_XFERINPROGRESS:
- return "Transfer In-Progress";
- case DWC3_DEPEVT_XFERNOTREADY:
- return "Transfer Not Ready";
- case DWC3_DEPEVT_RXTXFIFOEVT:
- return "FIFO";
- case DWC3_DEPEVT_STREAMEVT:
- return "Stream";
- case DWC3_DEPEVT_EPCMDCMPLT:
- return "Endpoint Command Complete";
- }
-
- return "UNKNOWN";
-}
-
-/**
- * struct dwc3_event_depvt - Device Endpoint Events
+ * struct dwc3_event_depevt - Device Endpoint Events
* @one_bit: indicates this is an endpoint event (not used)
* @endpoint_number: number of the endpoint
* @endpoint_event: The event we have:
@@ -1238,6 +1380,10 @@ struct dwc3_event_depevt {
#define DEPEVT_STREAMEVT_FOUND 1
#define DEPEVT_STREAMEVT_NOTFOUND 2
+/* Stream event parameter */
+#define DEPEVT_STREAM_PRIME 0xfffe
+#define DEPEVT_STREAM_NOSTREAM 0x0
+
/* Control-only Status */
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
@@ -1264,7 +1410,7 @@ struct dwc3_event_depevt {
* 3 - ULStChng
* 4 - WkUpEvt
* 5 - Reserved
- * 6 - EOPF
+ * 6 - Suspend (EOPF on revisions 2.10a and prior)
* 7 - SOF
* 8 - Reserved
* 9 - ErrticErr
@@ -1336,28 +1482,63 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
+void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
+u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
-/* check whether we are on the DWC_usb3 core */
-static inline bool dwc3_is_usb3(struct dwc3 *dwc)
-{
- return !(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_IP_IS(_ip) \
+ (dwc->ip == _ip##_IP)
+
+#define DWC3_VER_IS(_ip, _ver) \
+ (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_PRIOR(_ip, _ver) \
+ (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver)
-/* check whether we are on the DWC_usb31 core */
-static inline bool dwc3_is_usb31(struct dwc3 *dwc)
+#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \
+ (DWC3_IP_IS(_ip) && \
+ (int)dwc->revision >= _ip##_REVISION_##_from && \
+ (!(_ip##_REVISION_##_to) || \
+ dwc->revision <= _ip##_REVISION_##_to))
+
+#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \
+ (DWC3_VER_IS(_ip, _ver) && \
+ dwc->version_type >= _ip##_VERSIONTYPE_##_from && \
+ (!(_ip##_VERSIONTYPE_##_to) || \
+ dwc->version_type <= _ip##_VERSIONTYPE_##_to))
+
+/**
+ * dwc3_mdwidth - get MDWIDTH value in bits
+ * @dwc: pointer to our context structure
+ *
+ * Return MDWIDTH configuration value in bits.
+ */
+static inline u32 dwc3_mdwidth(struct dwc3 *dwc)
{
- return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
+ u32 mdwidth;
+
+ mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+ return mdwidth;
}
bool dwc3_has_imod(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
+int dwc3_core_soft_reset(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
+void dwc3_host_exit(struct dwc3 *dwc);
#else
static inline int dwc3_host_init(struct dwc3 *dwc)
{ return 0; }
+static inline void dwc3_host_exit(struct dwc3 *dwc)
+{ }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
@@ -1366,9 +1547,12 @@ void dwc3_gadget_exit(struct dwc3 *dwc);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
-int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params);
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
+ u32 param);
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
+void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1382,9 +1566,14 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
enum dwc3_link_state state)
{ return 0; }
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
+ struct dwc3_gadget_ep_cmd_params *params)
+{ return 0; }
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
int cmd, u32 param)
{ return 0; }
+static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{ }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 1494df261d..8bb2c9e3b9 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/**
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)
return "All FIFO Flush";
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
return "Set Endpoint NRDY";
+ case DWC3_DGCMD_SET_ENDPOINT_PRIME:
+ return "Set Endpoint Prime";
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
return "Run SoC Bus Loopback Test";
default:
@@ -112,7 +114,7 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
case DWC3_LINK_STATE_RESUME:
return "Resume";
default:
- return "UNKNOWN link state\n";
+ return "UNKNOWN link state";
}
}
@@ -141,7 +143,7 @@ dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
case DWC3_LINK_STATE_RESUME:
return "Resume";
default:
- return "UNKNOWN link state\n";
+ return "UNKNOWN link state";
}
}
@@ -193,294 +195,54 @@ static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
* dwc3_gadget_event_string - returns event name
* @event: the event code
*/
-static inline const char *
-dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
+static inline const char *dwc3_gadget_event_string(char *str, size_t size,
+ const struct dwc3_event_devt *event)
{
enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
switch (event->type) {
case DWC3_DEVICE_EVENT_DISCONNECT:
- sprintf(str, "Disconnect: [%s]",
+ snprintf(str, size, "Disconnect: [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_RESET:
- sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state));
+ snprintf(str, size, "Reset [%s]",
+ dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_CONNECT_DONE:
- sprintf(str, "Connection Done [%s]",
+ snprintf(str, size, "Connection Done [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
- sprintf(str, "Link Change [%s]",
+ snprintf(str, size, "Link Change [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_WAKEUP:
- sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state));
+ snprintf(str, size, "WakeUp [%s]",
+ dwc3_gadget_link_string(state));
break;
- case DWC3_DEVICE_EVENT_EOPF:
- sprintf(str, "End-Of-Frame [%s]",
+ case DWC3_DEVICE_EVENT_SUSPEND:
+ snprintf(str, size, "Suspend [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_SOF:
- sprintf(str, "Start-Of-Frame [%s]",
+ snprintf(str, size, "Start-Of-Frame [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- sprintf(str, "Erratic Error [%s]",
+ snprintf(str, size, "Erratic Error [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- sprintf(str, "Command Complete [%s]",
+ snprintf(str, size, "Command Complete [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state));
- break;
- default:
- sprintf(str, "UNKNOWN");
- }
-
- return str;
-}
-
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
-{
- switch (t & USB_RECIP_MASK) {
- case USB_RECIP_INTERFACE:
- sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
- i, l);
- break;
- case USB_RECIP_ENDPOINT:
- sprintf(str, "Get Endpoint Status(ep%d%s)",
- i & ~USB_DIR_IN,
- i & USB_DIR_IN ? "in" : "out");
- break;
- }
-}
-
-static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
- __u16 i, char *str)
-{
- switch (t & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- sprintf(str, "%s Device Feature(%s%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- ({char *s;
- switch (v) {
- case USB_DEVICE_SELF_POWERED:
- s = "Self Powered";
- break;
- case USB_DEVICE_REMOTE_WAKEUP:
- s = "Remote Wakeup";
- break;
- case USB_DEVICE_TEST_MODE:
- s = "Test Mode";
- break;
- case USB_DEVICE_U1_ENABLE:
- s = "U1 Enable";
- break;
- case USB_DEVICE_U2_ENABLE:
- s = "U2 Enable";
- break;
- case USB_DEVICE_LTM_ENABLE:
- s = "LTM Enable";
- break;
- default:
- s = "UNKNOWN";
- } s; }),
- v == USB_DEVICE_TEST_MODE ?
- ({ char *s;
- switch (i) {
- case TEST_J:
- s = ": TEST_J";
- break;
- case TEST_K:
- s = ": TEST_K";
- break;
- case TEST_SE0_NAK:
- s = ": TEST_SE0_NAK";
- break;
- case TEST_PACKET:
- s = ": TEST_PACKET";
- break;
- case TEST_FORCE_EN:
- s = ": TEST_FORCE_EN";
- break;
- default:
- s = ": UNKNOWN";
- } s; }) : "");
- break;
- case USB_RECIP_INTERFACE:
- sprintf(str, "%s Interface Feature(%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- v == USB_INTRF_FUNC_SUSPEND ?
- "Function Suspend" : "UNKNOWN");
- break;
- case USB_RECIP_ENDPOINT:
- sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
- i & ~USB_DIR_IN,
- i & USB_DIR_IN ? "in" : "out");
- break;
- }
-}
-
-static inline void dwc3_decode_set_address(__u16 v, char *str)
-{
- sprintf(str, "Set Address(Addr = %02x)", v);
-}
-
-static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
- __u16 i, __u16 l, char *str)
-{
- sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
- b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
- ({ char *s;
- switch (v >> 8) {
- case USB_DT_DEVICE:
- s = "Device";
- break;
- case USB_DT_CONFIG:
- s = "Configuration";
- break;
- case USB_DT_STRING:
- s = "String";
- break;
- case USB_DT_INTERFACE:
- s = "Interface";
- break;
- case USB_DT_ENDPOINT:
- s = "Endpoint";
- break;
- case USB_DT_DEVICE_QUALIFIER:
- s = "Device Qualifier";
- break;
- case USB_DT_OTHER_SPEED_CONFIG:
- s = "Other Speed Config";
- break;
- case USB_DT_INTERFACE_POWER:
- s = "Interface Power";
- break;
- case USB_DT_OTG:
- s = "OTG";
- break;
- case USB_DT_DEBUG:
- s = "Debug";
- break;
- case USB_DT_INTERFACE_ASSOCIATION:
- s = "Interface Association";
- break;
- case USB_DT_BOS:
- s = "BOS";
- break;
- case USB_DT_DEVICE_CAPABILITY:
- s = "Device Capability";
- break;
- case USB_DT_PIPE_USAGE:
- s = "Pipe Usage";
- break;
- case USB_DT_SS_ENDPOINT_COMP:
- s = "SS Endpoint Companion";
- break;
- case USB_DT_SSP_ISOC_ENDPOINT_COMP:
- s = "SSP Isochronous Endpoint Companion";
- break;
- default:
- s = "UNKNOWN";
- break;
- } s; }), v & 0xff, l);
-}
-
-
-static inline void dwc3_decode_get_configuration(__u16 l, char *str)
-{
- sprintf(str, "Get Configuration(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_configuration(__u8 v, char *str)
-{
- sprintf(str, "Set Configuration(Config = %d)", v);
-}
-
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
-{
- sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
-{
- sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
-}
-
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
-{
- sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_sel(__u16 l, char *str)
-{
- sprintf(str, "Set SEL(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
-{
- sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
-}
-
-/**
- * dwc3_decode_ctrl - returns a string represetion of ctrl request
- */
-static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
- __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
-{
- switch (bRequest) {
- case USB_REQ_GET_STATUS:
- dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
- break;
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
- wIndex, str);
- break;
- case USB_REQ_SET_ADDRESS:
- dwc3_decode_set_address(wValue, str);
- break;
- case USB_REQ_GET_DESCRIPTOR:
- case USB_REQ_SET_DESCRIPTOR:
- dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
- wIndex, wLength, str);
- break;
- case USB_REQ_GET_CONFIGURATION:
- dwc3_decode_get_configuration(wLength, str);
- break;
- case USB_REQ_SET_CONFIGURATION:
- dwc3_decode_set_configuration(wValue, str);
- break;
- case USB_REQ_GET_INTERFACE:
- dwc3_decode_get_intf(wIndex, wLength, str);
- break;
- case USB_REQ_SET_INTERFACE:
- dwc3_decode_set_intf(wValue, wIndex, str);
- break;
- case USB_REQ_SYNCH_FRAME:
- dwc3_decode_synch_frame(wIndex, wLength, str);
- break;
- case USB_REQ_SET_SEL:
- dwc3_decode_set_sel(wLength, str);
- break;
- case USB_REQ_SET_ISOCH_DELAY:
- dwc3_decode_set_isoch_delay(wValue, str);
+ snprintf(str, size, "Overflow [%s]",
+ dwc3_gadget_link_string(state));
break;
default:
- sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
- bRequestType, bRequest,
- cpu_to_le16(wValue) & 0xff,
- cpu_to_le16(wValue) >> 8,
- cpu_to_le16(wIndex) & 0xff,
- cpu_to_le16(wIndex) >> 8,
- cpu_to_le16(wLength) & 0xff,
- cpu_to_le16(wLength) >> 8);
+ snprintf(str, size, "UNKNOWN");
}
return str;
@@ -490,48 +252,41 @@ static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
* dwc3_ep_event_string - returns event name
* @event: then event code
*/
-static inline const char *
-dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
- u32 ep0state)
+static inline const char *dwc3_ep_event_string(char *str, size_t size,
+ const struct dwc3_event_depevt *event, u32 ep0state)
{
u8 epnum = event->endpoint_number;
size_t len;
int status;
- int ret;
- ret = sprintf(str, "ep%d%s: ", epnum >> 1,
+ len = scnprintf(str, size, "ep%d%s: ", epnum >> 1,
(epnum & 1) ? "in" : "out");
- if (ret < 0)
- return "UNKNOWN";
status = event->status;
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- len = strlen(str);
- sprintf(str + len, "Transfer Complete (%c%c%c)",
+ len += scnprintf(str + len, size - len,
+ "Transfer Complete (%c%c%c)",
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
status & DEPEVT_STATUS_LST ? 'L' : 'l');
- len = strlen(str);
-
if (epnum <= 1)
- sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state));
+ scnprintf(str + len, size - len, " [%s]",
+ dwc3_ep0_state_string(ep0state));
break;
case DWC3_DEPEVT_XFERINPROGRESS:
- len = strlen(str);
-
- sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)",
+ scnprintf(str + len, size - len,
+ "Transfer In Progress [%08x] (%c%c%c)",
event->parameters,
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
status & DEPEVT_STATUS_LST ? 'M' : 'm');
break;
case DWC3_DEPEVT_XFERNOTREADY:
- len = strlen(str);
-
- sprintf(str + len, "Transfer Not Ready [%d]%s",
+ len += scnprintf(str + len, size - len,
+ "Transfer Not Ready [%08x]%s",
event->parameters,
status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
" (Active)" : " (Not Active)");
@@ -542,36 +297,38 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
switch (phase) {
case DEPEVT_STATUS_CONTROL_DATA:
- strcat(str, " [Data Phase]");
+ scnprintf(str + len, size - len,
+ " [Data Phase]");
break;
case DEPEVT_STATUS_CONTROL_STATUS:
- strcat(str, " [Status Phase]");
+ scnprintf(str + len, size - len,
+ " [Status Phase]");
}
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- strcat(str, "FIFO");
+ scnprintf(str + len, size - len, "FIFO");
break;
case DWC3_DEPEVT_STREAMEVT:
status = event->status;
switch (status) {
case DEPEVT_STREAMEVT_FOUND:
- sprintf(str + ret, " Stream %d Found",
+ scnprintf(str + len, size - len, " Stream %d Found",
event->parameters);
break;
case DEPEVT_STREAMEVT_NOTFOUND:
default:
- strcat(str, " Stream Not Found");
+ scnprintf(str + len, size - len, " Stream Not Found");
break;
}
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- strcat(str, "Endpoint Command Complete");
+ scnprintf(str + len, size - len, "Endpoint Command Complete");
break;
default:
- sprintf(str, "UNKNOWN");
+ scnprintf(str + len, size - len, "UNKNOWN");
}
return str;
@@ -596,8 +353,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
return "Wake-Up";
case DWC3_DEVICE_EVENT_HIBER_REQ:
return "Hibernation";
- case DWC3_DEVICE_EVENT_EOPF:
- return "End of Periodic Frame";
+ case DWC3_DEVICE_EVENT_SUSPEND:
+ return "Suspend";
case DWC3_DEVICE_EVENT_SOF:
return "Start of Frame";
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
@@ -611,14 +368,17 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
}
}
-static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state)
+static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
+ u32 ep0state)
{
- const union dwc3_event evt = (union dwc3_event) event;
+ union dwc3_event evt;
+
+ memcpy(&evt, &event, sizeof(event));
if (evt.type.is_devspec)
- return dwc3_gadget_event_string(str, &evt.devt);
+ return dwc3_gadget_event_string(str, size, &evt.devt);
else
- return dwc3_ep_event_string(str, &evt.depevt, ep0state);
+ return dwc3_ep_event_string(str, size, &evt.depevt, ep0state);
}
static inline const char *dwc3_ep_cmd_status_string(int status)
@@ -653,9 +413,15 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
#ifdef CONFIG_DEBUG_FS
-extern void dwc3_debugfs_init(struct dwc3 *);
-extern void dwc3_debugfs_exit(struct dwc3 *);
+extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep);
+extern void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep);
+extern void dwc3_debugfs_init(struct dwc3 *d);
+extern void dwc3_debugfs_exit(struct dwc3 *d);
#else
+static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
+{ }
+static inline void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep)
+{ }
static inline void dwc3_debugfs_init(struct dwc3 *d)
{ }
static inline void dwc3_debugfs_exit(struct dwc3 *d)
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index 6261122a12..1e62224015 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -20,15 +20,15 @@
#include <of.h>
struct dwc3_of_simple {
- struct device_d *dev;
+ struct device *dev;
struct clk_bulk_data *clks;
int num_clocks;
};
-static int dwc3_of_simple_probe(struct device_d *dev)
+static int dwc3_of_simple_probe(struct device *dev)
{
struct dwc3_of_simple *simple;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret;
@@ -57,7 +57,7 @@ static int dwc3_of_simple_probe(struct device_d *dev)
return 0;
}
-static void dwc3_of_simple_remove(struct device_d *dev)
+static void dwc3_of_simple_remove(struct device *dev)
{
struct dwc3_of_simple *simple = dev->priv;
@@ -67,16 +67,11 @@ static void dwc3_of_simple_remove(struct device_d *dev)
static const struct of_device_id of_dwc3_simple_match[] = {
{.compatible = "rockchip,rk3399-dwc3"},
{.compatible = "xlnx,zynqmp-dwc3"},
- {.compatible = "fsl,ls1046a-dwc3"},
{.compatible = "fsl,imx8mp-dwc3"},
- {.compatible = "cavium,octeon-7130-usb-uctl"},
- {.compatible = "sprd,sc9860-dwc3"},
- {.compatible = "amlogic,meson-axg-dwc3"},
- {.compatible = "amlogic,meson-gxl-dwc3"},
- {.compatible = "allwinner,sun50i-h6-dwc3"},
{/* Sentinel */}};
+MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
-static struct driver_d dwc3_of_simple_driver = {
+static struct driver dwc3_of_simple_driver = {
.probe = dwc3_of_simple_probe,
.remove = dwc3_of_simple_remove,
.name = "dwc3-of-simple",
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index c0e316e14f..6285566b4b 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1,26 +1,26 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/**
+// SPDX-License-Identifier: GPL-2.0
+/*
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported
- * to uboot.
- *
- * commit c00552ebaf : Merge 3.18-rc7 into usb-next
*/
#include <common.h>
#include <dma.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/completion.h>
-#include <usb/gadget.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
#include "core.h"
+#include "debug.h"
#include "gadget.h"
#include "io.h"
@@ -29,11 +29,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
- dma_addr_t buf_dma, u32 len, u32 type,
- bool chain)
+ dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
- struct dwc3_trb *trb;
- struct dwc3 *dwc;
+ struct dwc3_trb *trb;
+ struct dwc3 *dwc;
dwc = dep->dwc;
trb = &dwc->ep0_trb[dep->trb_enqueue];
@@ -47,67 +46,47 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl = type;
trb->ctrl |= (DWC3_TRB_CTRL_HWO
- | DWC3_TRB_CTRL_ISP_IMI);
+ | DWC3_TRB_CTRL_ISP_IMI);
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
else
trb->ctrl |= (DWC3_TRB_CTRL_IOC
- | DWC3_TRB_CTRL_LST);
+ | DWC3_TRB_CTRL_LST);
}
static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3 *dwc;
- int ret;
-
- dwc = dep->dwc;
+ struct dwc3 *dwc;
+ int ret;
- if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
- dev_err(dwc->dev, "%s: transfer already started\n", dep->name);
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
return 0;
- }
+
+ dwc = dep->dwc;
memset(&params, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, &params);
- if (ret < 0) {
- dev_err(dwc->dev, "%s: STARTTRANSFER failed\n", dep->name);
+ if (ret < 0)
return ret;
- }
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
return 0;
}
-static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
-{
- switch (state) {
- case EP0_UNCONNECTED:
- return "Unconnected";
- case EP0_SETUP_PHASE:
- return "Setup Phase";
- case EP0_DATA_PHASE:
- return "Data Phase";
- case EP0_STATUS_PHASE:
- return "Status Phase";
- default:
- return "UNKNOWN";
- }
-}
-
static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
- struct dwc3_request *req)
+ struct dwc3_request *req)
{
- struct dwc3 *dwc = dep->dwc;
+ struct dwc3 *dwc = dep->dwc;
- req->request.actual = 0;
- req->request.status = -EINPROGRESS;
- req->epnum = dep->number;
+ req->request.actual = 0;
+ req->request.status = -EINPROGRESS;
+ req->epnum = dep->number;
list_add_tail(&req->list, &dep->pending_list);
@@ -121,7 +100,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* IRQ we were waiting for is long gone.
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- unsigned direction;
+ unsigned int direction;
direction = !!(dep->flags & DWC3_EP0_DIR_IN);
@@ -143,16 +122,14 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* handle it here.
*/
if (dwc->delayed_status) {
- unsigned direction;
+ unsigned int direction;
direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED);
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
- else
- dev_dbg(dwc->dev, "too early for delayed status\n");
return 0;
}
@@ -190,7 +167,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* XferNotReady(STATUS).
*/
if (dwc->three_stage_setup) {
- unsigned direction;
+ unsigned int direction;
direction = dwc->ep0_expect_in;
dwc->ep0state = EP0_DATA_PHASE;
@@ -205,16 +182,18 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request)
{
- struct dwc3_request *req = to_dwc3_request(request);
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
- unsigned long flags;
- int ret;
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ unsigned long flags;
+
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc) {
- dev_err(dwc->dev, "trying to queue request %p to disabled %s\n",
- request, dep->name);
+ if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
ret = -ESHUTDOWN;
goto out;
}
@@ -225,10 +204,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request)
goto out;
}
- dev_dbg(dwc->dev, "queueing request %p to %s length %d state '%s'\n",
- request, dep->name, request->length,
- dwc3_ep0_state_string(dwc->ep0state));
-
ret = __dwc3_gadget_ep0_queue(dep, req);
out:
@@ -237,9 +212,9 @@ out:
return ret;
}
-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
- struct dwc3_ep *dep;
+ struct dwc3_ep *dep;
/* reinitialize physical ep1 */
dep = dwc->eps[1];
@@ -252,20 +227,22 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
dwc->delayed_status = false;
if (!list_empty(&dep->pending_list)) {
- struct dwc3_request *req;
+ struct dwc3_request *req;
req = next_request(&dep->pending_list);
dwc3_gadget_giveback(dep, req, -ECONNRESET);
}
+ dwc->eps[0]->trb_enqueue = 0;
+ dwc->eps[1]->trb_enqueue = 0;
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
}
int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
dwc3_ep0_stall_and_restart(dwc);
@@ -274,8 +251,8 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
- unsigned long flags;
- int ret;
+ unsigned long flags;
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep0_set_halt(ep, value);
@@ -286,27 +263,49 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
void dwc3_ep0_out_start(struct dwc3 *dwc)
{
- struct dwc3_ep *dep;
- int ret;
+ struct dwc3_ep *dep;
+ int ret;
+ int i;
+
+ complete(&dwc->ep0_in_setup);
dep = dwc->eps[0];
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
- DWC3_TRBCTL_CONTROL_SETUP, false);
+ DWC3_TRBCTL_CONTROL_SETUP, false);
ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0);
+ for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
+ struct dwc3_ep *dwc3_ep;
+
+ dwc3_ep = dwc->eps[i];
+ if (!dwc3_ep)
+ continue;
+
+ if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
+ continue;
+
+ dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
+ if (dwc->connected)
+ dwc3_stop_active_transfer(dwc3_ep, true, true);
+ else
+ dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN);
+ }
}
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
{
- struct dwc3_ep *dep;
- u32 windex = le16_to_cpu(wIndex_le);
- u32 epnum;
+ struct dwc3_ep *dep;
+ u32 windex = le16_to_cpu(wIndex_le);
+ u32 epnum;
epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1;
if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
epnum |= 1;
dep = dwc->eps[epnum];
+ if (dep == NULL)
+ return NULL;
+
if (dep->flags & DWC3_EP_ENABLED)
return dep;
@@ -320,14 +319,14 @@ static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req)
* ch 9.4.5
*/
static int dwc3_ep0_handle_status(struct dwc3 *dwc,
- struct usb_ctrlrequest *ctrl)
+ struct usb_ctrlrequest *ctrl)
{
- struct dwc3_ep *dep;
- u32 recip;
- u32 value;
- u32 reg;
- u16 usb_status = 0;
- __le16 *response_pkt;
+ struct dwc3_ep *dep;
+ u32 recip;
+ u32 value;
+ u32 reg;
+ u16 usb_status = 0;
+ __le16 *response_pkt;
/* We don't support PTM_STATUS */
value = le16_to_cpu(ctrl->wValue);
@@ -340,7 +339,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
/*
* LTM will be set once we know how to set this in HW.
*/
- usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+ usb_status |= dwc->gadget->is_selfpowered;
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
@@ -385,7 +384,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
}
static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
- int set)
+ int set)
{
u32 reg;
@@ -394,6 +393,8 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
return -EINVAL;
+ if (set && dwc->dis_u1_entry_quirk)
+ return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set)
@@ -406,15 +407,18 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
}
static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
- int set)
+ int set)
{
u32 reg;
+
if (state != USB_STATE_CONFIGURED)
return -EINVAL;
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
return -EINVAL;
+ if (set && dwc->dis_u2_entry_quirk)
+ return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set)
@@ -427,7 +431,7 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
}
static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
- u32 wIndex, int set)
+ u32 wIndex, int set)
{
if ((wIndex & 0xff) != 0)
return -EINVAL;
@@ -435,11 +439,11 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
return -EINVAL;
switch (wIndex >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
dwc->test_mode_nr = wIndex >> 8;
dwc->test_mode = true;
break;
@@ -451,22 +455,22 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
}
static int dwc3_ep0_handle_device(struct dwc3 *dwc,
- struct usb_ctrlrequest *ctrl, int set)
+ struct usb_ctrlrequest *ctrl, int set)
{
- enum usb_device_state state;
- u32 wValue;
- u32 wIndex;
- int ret = 0;
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
- state = dwc->gadget.state;
+ state = dwc->gadget->state;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
break;
/*
- * 9.4.1 says only only for SS, in AddressState only for
+ * 9.4.1 says only for SS, in AddressState only for
* default control pipe
*/
case USB_DEVICE_U1_ENABLE:
@@ -489,10 +493,10 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
}
static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
- struct usb_ctrlrequest *ctrl, int set)
+ struct usb_ctrlrequest *ctrl, int set)
{
- u32 wValue;
- int ret = 0;
+ u32 wValue;
+ int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
@@ -514,11 +518,11 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
}
static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
- struct usb_ctrlrequest *ctrl, int set)
+ struct usb_ctrlrequest *ctrl, int set)
{
- struct dwc3_ep *dep;
- u32 wValue;
- int ret;
+ struct dwc3_ep *dep;
+ u32 wValue;
+ int ret;
wValue = le16_to_cpu(ctrl->wValue);
@@ -534,6 +538,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
ret = __dwc3_gadget_ep_set_halt(dep, set, true);
if (ret)
return -EINVAL;
+
+ /* ClearFeature(Halt) may need delayed status */
+ if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return USB_GADGET_DELAYED_STATUS;
+
break;
default:
return -EINVAL;
@@ -542,12 +551,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
return 0;
}
-
static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
- struct usb_ctrlrequest *ctrl, int set)
+ struct usb_ctrlrequest *ctrl, int set)
{
- u32 recip;
- int ret;
+ u32 recip;
+ int ret;
recip = ctrl->bRequestType & USB_RECIP_MASK;
@@ -570,7 +578,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u32 addr;
u32 reg;
@@ -581,7 +589,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
}
if (state == USB_STATE_CONFIGURED) {
- dev_err(dwc->dev, "trying to set address when configured\n");
+ dev_err(dwc->dev, "can't SetAddress() from Configured State\n");
return -EINVAL;
}
@@ -591,27 +599,28 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
- usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
else
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT);
return 0;
}
static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- int ret;
+ int ret = -EINVAL;
- spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
- spin_lock(&dwc->lock);
+ if (dwc->async_callbacks) {
+ spin_unlock(&dwc->lock);
+ ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
+ spin_lock(&dwc->lock);
+ }
return ret;
}
-#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u32 cfg;
int ret;
u32 reg;
@@ -623,6 +632,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
case USB_STATE_ADDRESS:
+ dwc3_gadget_clear_tx_fifos(dwc);
+
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
@@ -634,7 +645,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* to change the state on the next usb_ep_queue()
*/
if (ret == 0)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED);
/*
@@ -642,7 +653,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* nothing is pending from application.
*/
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+ if (!dwc->dis_u1_entry_quirk)
+ reg |= DWC3_DCTL_ACCEPTU1ENA;
+ if (!dwc->dis_u2_entry_quirk)
+ reg |= DWC3_DCTL_ACCEPTU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
break;
@@ -650,7 +664,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_ADDRESS);
break;
default:
@@ -661,11 +675,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
{
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
- u32 param = 0;
- u32 reg;
+ u32 param = 0;
+ u32 reg;
struct timing {
u8 u1sel;
@@ -705,9 +719,9 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- struct dwc3_ep *dep;
- enum usb_device_state state = dwc->gadget.state;
- u16 wLength;
+ struct dwc3_ep *dep;
+ enum usb_device_state state = dwc->gadget->state;
+ u16 wLength;
if (state == USB_STATE_DEFAULT)
return -EINVAL;
@@ -737,12 +751,11 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
}
-static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc,
- struct usb_ctrlrequest *ctrl)
+static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- u16 wLength;
- u16 wValue;
- u16 wIndex;
+ u16 wLength;
+ u16 wValue;
+ u16 wIndex;
wValue = le16_to_cpu(ctrl->wValue);
wLength = le16_to_cpu(ctrl->wLength);
@@ -751,11 +764,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc,
if (wIndex || wLength)
return -EINVAL;
- /*
- * REVISIT It's unclear from Databook what to do with this
- * value. For now, just cache it.
- */
- dwc->isoch_delay = wValue;
+ dwc->gadget->isoch_delay = wValue;
return 0;
}
@@ -766,35 +775,27 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
- dev_dbg(dwc->dev, "USB_REQ_GET_STATUS\n");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
- dev_dbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
- dev_dbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
- dev_dbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
- dev_dbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
case USB_REQ_SET_SEL:
- dev_dbg(dwc->dev, "USB_REQ_SET_SEL\n");
ret = dwc3_ep0_set_sel(dwc, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
- dev_dbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
default:
- dev_dbg(dwc->dev, "Forwarding to gadget driver\n");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
}
@@ -803,13 +804,13 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
}
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb;
int ret = -EINVAL;
u32 len;
- if (!dwc->gadget_driver)
+ if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out;
len = le16_to_cpu(ctrl->wLength);
@@ -837,16 +838,16 @@ out:
}
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
- struct dwc3_request *r = NULL;
- struct usb_request *ur;
- struct dwc3_trb *trb;
- struct dwc3_ep *ep0;
- u32 transferred = 0;
- u32 status;
- u32 length;
- u8 epnum;
+ struct dwc3_request *r;
+ struct usb_request *ur;
+ struct dwc3_trb *trb;
+ struct dwc3_ep *ep0;
+ u32 transferred = 0;
+ u32 status;
+ u32 length;
+ u8 epnum;
epnum = event->endpoint_number;
ep0 = dwc->eps[0];
@@ -860,9 +861,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
- dev_dbg(dwc->dev, "Setup Pending received\n");
dwc->setup_packet_pending = true;
-
if (r)
dwc3_gadget_giveback(ep0, r, -ECONNRESET);
@@ -876,7 +875,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ur->actual += transferred;
if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
- ur->length && ur->zero) || dwc->ep0_bounced) {
+ ur->length && ur->zero) || dwc->ep0_bounced) {
trb++;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
@@ -895,12 +894,12 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
}
static void dwc3_ep0_complete_status(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
- struct dwc3_request *r;
- struct dwc3_ep *dep;
- struct dwc3_trb *trb;
- u32 status;
+ struct dwc3_request *r;
+ struct dwc3_ep *dep;
+ struct dwc3_trb *trb;
+ u32 status;
dep = dwc->eps[0];
trb = dwc->ep0_trb;
@@ -916,27 +915,25 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
if (ret < 0) {
- dev_dbg(dwc->dev, "Invalid Test #%d\n",
- dwc->test_mode_nr);
+ dev_err(dwc->dev, "invalid test #%d\n",
+ dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
return;
}
}
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (status == DWC3_TRBSTS_SETUP_PENDING) {
- dev_dbg(dwc->dev, "Setup Pending received\n");
+ if (status == DWC3_TRBSTS_SETUP_PENDING)
dwc->setup_packet_pending = true;
- }
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
}
static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
- struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dep->resource_index = 0;
@@ -944,17 +941,14 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
switch (dwc->ep0state) {
case EP0_SETUP_PHASE:
- dev_dbg(dwc->dev, "Setup Phase\n");
dwc3_ep0_inspect_setup(dwc, event);
break;
case EP0_DATA_PHASE:
- dev_dbg(dwc->dev, "Data Phase\n");
dwc3_ep0_complete_data(dwc, event);
break;
case EP0_STATUS_PHASE:
- dev_dbg(dwc->dev, "Status Phase\n");
dwc3_ep0_complete_status(dwc, event);
break;
default:
@@ -963,31 +957,29 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
}
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
- struct dwc3_ep *dep,
- struct dwc3_request *req)
+ struct dwc3_ep *dep, struct dwc3_request *req)
{
- dma_addr_t dma_addr;
- int ret;
+ unsigned int trb_length = 0;
+ int ret;
req->direction = !!dep->number;
if (req->request.length == 0) {
- dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, false);
+ if (!req->direction)
+ trb_length = dep->endpoint.maxpacket;
+
+ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length,
+ DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dep);
- } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
- (dep->number == 0)) {
- u32 maxpacket;
- u32 rem;
-
- dma_addr = dma_map_single(dwc->dev, req->request.buf,
- req->request.length,
- dep->number ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (dma_mapping_error(dwc->dev, dma_addr))
- return;
+ } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
+ && (dep->number == 0)) {
+ u32 maxpacket;
+ u32 rem;
- req->request.dma = dma_addr;
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
+ return;
maxpacket = dep->endpoint.maxpacket;
rem = req->request.length % maxpacket;
@@ -1009,14 +1001,11 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dep);
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
- dma_addr = dma_map_single(dwc->dev, req->request.buf,
- req->request.length,
- dep->number ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (dma_mapping_error(dwc->dev, dma_addr))
- return;
- req->request.dma = dma_addr;
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
+ return;
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
@@ -1026,21 +1015,20 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
+ if (!req->direction)
+ trb_length = dep->endpoint.maxpacket;
+
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
- 0, DWC3_TRBCTL_CONTROL_DATA,
+ trb_length, DWC3_TRBCTL_CONTROL_DATA,
false);
ret = dwc3_ep0_start_trans(dep);
} else {
- dma_addr = dma_map_single(dwc->dev, req->request.buf,
- req->request.length,
- dep->number ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (dma_mapping_error(dwc->dev, dma_addr))
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
return;
- req->request.dma = dma_addr;
-
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
@@ -1055,8 +1043,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
{
- struct dwc3 *dwc = dep->dwc;
- u32 type;
+ struct dwc3 *dwc = dep->dwc;
+ u32 type;
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
@@ -1071,20 +1059,38 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
}
static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
- struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
__dwc3_ep0_do_control_status(dwc, dep);
}
-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
+{
+ unsigned int direction = !dwc->ep0_expect_in;
+
+ dwc->delayed_status = false;
+ dwc->clear_stall_protocol = 0;
+
+ if (dwc->ep0state != EP0_STATUS_PHASE)
+ return;
+
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
+}
+
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
- u32 cmd;
- int ret;
+ u32 cmd;
+ int ret;
- if (!dep->resource_index)
+ /*
+ * For status/DATA OUT stage, TRB will be queued on ep0 out
+ * endpoint for which resource index is zero. Hence allow
+ * queuing ENDXFER command for ep0 out endpoint.
+ */
+ if (!dep->resource_index && dep->number)
return;
cmd = DWC3_DEPCMD_ENDTRANSFER;
@@ -1092,17 +1098,17 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
- WARN_ON(ret);
+ WARN_ON_ONCE(ret);
dep->resource_index = 0;
}
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
- dev_dbg(dwc->dev, "Control Data\n");
-
+ if (!dwc->softconnect || !dwc->connected)
+ return;
/*
* We already have a DATA transfer in the controller's cache,
* if we receive a XferNotReady(DATA) we will ignore it, unless
@@ -1113,9 +1119,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
* control endpoint.
*/
if (dwc->ep0_expect_in != event->endpoint_number) {
- struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
+ struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
- dev_dbg(dwc->dev, "Wrong direction for Data phase\n");
+ dev_err(dwc->dev, "unexpected direction for Data Phase\n");
dwc3_ep0_end_control_data(dwc, dep);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -1127,15 +1133,17 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
- dev_dbg(dwc->dev, "Control Status\n");
+ if (dwc->setup_packet_pending) {
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
struct dwc3_ep *dep = dwc->eps[0];
- WARN_ON(event->endpoint_number != 1);
- dev_dbg(dwc->dev, "Delayed Status\n");
+ WARN_ON_ONCE(event->endpoint_number != 1);
/*
* We should handle the delay STATUS phase here if the
* request for handling delay STATUS has been queued
@@ -1143,7 +1151,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
*/
if (!list_empty(&dep->pending_list)) {
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED);
dwc3_ep0_do_control_status(dwc, event);
}
@@ -1156,14 +1164,10 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
}
void dwc3_ep0_interrupt(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
- u8 epnum = event->endpoint_number;
-
- dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
- dwc3_ep_event_string(event->endpoint_event),
- epnum >> 1, (epnum & 1) ? "in" : "out",
- dwc3_ep0_state_string(dwc->ep0state));
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+ u8 cmd;
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
@@ -1177,7 +1181,14 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
case DWC3_DEPEVT_XFERINPROGRESS:
case DWC3_DEPEVT_RXTXFIFOEVT:
case DWC3_DEPEVT_STREAMEVT:
+ break;
case DWC3_DEPEVT_EPCMDCMPLT:
+ cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ }
break;
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index e911bba85e..48be74f7e9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1,40 +1,37 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/**
+// SPDX-License-Identifier: GPL-2.0
+/*
* gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported
- * to uboot.
- *
- * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier
*/
#include <common.h>
#include <dma.h>
#include <io.h>
#include <linux/list.h>
-#include <usb/gadget.h>
-#include <usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include "debug.h"
#include "core.h"
#include "gadget.h"
+#include "io.h"
#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
& ~((d)->interval - 1))
-
/**
- * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
+ * dwc3_gadget_set_test_mode - enables usb2 test modes
* @dwc: pointer to our context structure
* @mode: the mode to set (J, K SE0 NAK, Force Enable)
*
- * Caller should take care of locking. This function will
- * return 0 on success or -EINVAL if wrong Test Selector
- * is passed
+ * Caller should take care of locking. This function will return 0 on
+ * success or -EINVAL if wrong Test Selector is passed.
*/
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{
@@ -44,24 +41,24 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
reg |= mode << 1;
break;
default:
return -EINVAL;
}
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
return 0;
}
/**
- * dwc3_gadget_get_link_state - Gets current state of USB Link
+ * dwc3_gadget_get_link_state - gets current state of usb link
* @dwc: pointer to our context structure
*
* Caller should take care of locking. This function will
@@ -69,7 +66,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
*/
int dwc3_gadget_get_link_state(struct dwc3 *dwc)
{
- u32 reg;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -77,7 +74,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
}
/**
- * dwc3_gadget_set_link_state - Sets USB Link to a particular State
+ * dwc3_gadget_set_link_state - sets usb link to a particular state
* @dwc: pointer to our context structure
* @state: the state to put link into
*
@@ -86,14 +83,14 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
*/
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
{
- int retries = 10000;
- u32 reg;
+ int retries = 10000;
+ u32 reg;
/*
* Wait until device controller is ready. Only applies to 1.94a and
* later RTL.
*/
- if (dwc->revision >= DWC3_REVISION_194A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD)
@@ -109,6 +106,9 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ /* set no action before sending new link state change */
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
/* set requested state */
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -117,7 +117,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
* The following code is racy when called from dwc3_gadget_wakeup,
* and is not needed, at least on newer versions
*/
- if (dwc->revision >= DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
return 0;
/* wait for a change in DSTS */
@@ -131,8 +131,6 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
udelay(5);
}
- dev_dbg(dwc->dev, "link state change request timed out\n");
-
return -ETIMEDOUT;
}
@@ -170,10 +168,9 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
}
static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
- struct dwc3_request *req,
- int status)
+ struct dwc3_request *req, int status)
{
- struct dwc3 *dwc = dep->dwc;
+ struct dwc3 *dwc = dep->dwc;
list_del(&req->list);
req->remaining = 0;
@@ -182,13 +179,10 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (req->request.length == 0)
- return;
-
if (req->trb)
dma_unmap_single(dwc->dev, req->request.dma,
- req->request.length,
- req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->request.length,
+ req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
req->trb = NULL;
}
@@ -204,18 +198,13 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
* layers that it has completed.
*/
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
- int status)
+ int status)
{
- struct dwc3 *dwc = dep->dwc;
-
dwc3_gadget_del_and_unmap_request(dep, req, status);
- dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
- req, dep->name, req->request.actual,
- req->request.length, status);
req->status = DWC3_REQUEST_STATUS_COMPLETED;
spin_unlock(&dwc->lock);
- req->request.complete(&dep->endpoint, &req->request);
+ usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
}
@@ -228,12 +217,13 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
* Caller should take care of locking. Issue @cmd with a given @param to @dwc
* and wait for its completion.
*/
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
+ u32 param)
{
- u32 timeout = 500;
- int status = 0;
- int ret = 0;
- u32 reg;
+ u32 timeout = 500;
+ int status = 0;
+ int ret = 0;
+ u32 reg;
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
@@ -241,16 +231,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
do {
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) {
- dev_dbg(dwc->dev, "%s: Command Complete --> %d\n",
- __func__,
- DWC3_DGCMD_STATUS(reg));
status = DWC3_DGCMD_STATUS(reg);
if (status)
ret = -EINVAL;
break;
}
-
- udelay(1);
} while (--timeout);
if (!timeout) {
@@ -262,6 +247,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
}
static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
+
/**
* dwc3_send_gadget_ep_cmd - issue an endpoint command
* @dep: the endpoint to which the command is going to be issued
@@ -271,17 +257,17 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
* Caller should handle locking. This function will issue @cmd with given
* @params to @dep and wait for its completion.
*/
-int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
- struct dwc3_gadget_ep_cmd_params *params)
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
+ struct dwc3_gadget_ep_cmd_params *params)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
- struct dwc3 *dwc = dep->dwc;
- u32 timeout = 1000;
- u32 saved_config = 0;
- u32 reg;
+ struct dwc3 *dwc = dep->dwc;
+ u32 timeout = 5000;
+ u32 saved_config = 0;
+ u32 reg;
- int cmd_status = 0;
- int ret = -EINVAL;
+ int cmd_status = 0;
+ int ret = -EINVAL;
/*
* When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
@@ -293,7 +279,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
*
* DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
- if (dwc->gadget.speed <= USB_SPEED_HIGH) {
+ if (dwc->gadget->speed <= USB_SPEED_HIGH ||
+ DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -310,21 +297,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
}
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- int needs_wakeup;
+ int link_state;
- needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
- dwc->link_state == DWC3_LINK_STATE_U2 ||
- dwc->link_state == DWC3_LINK_STATE_U3);
+ /*
+ * Initiate remote wakeup if the link state is in U3 when
+ * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
+ * link state is in U1/U2, no remote wakeup is needed. The Start
+ * Transfer command will initiate the link recovery.
+ */
+ link_state = dwc3_gadget_get_link_state(dwc);
+ switch (link_state) {
+ case DWC3_LINK_STATE_U2:
+ if (dwc->gadget->speed >= USB_SPEED_SUPER)
+ break;
- if (unlikely(needs_wakeup)) {
+ fallthrough;
+ case DWC3_LINK_STATE_U3:
ret = __dwc3_gadget_wakeup(dwc);
- dev_warn(dwc->dev, "wakeup failed --> %d\n", ret);
+ dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
+ ret);
+ break;
}
}
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+ /*
+ * For some commands such as Update Transfer command, DEPCMDPARn
+ * registers are reserved. Since the driver often sends Update Transfer
+ * command, don't write to DEPCMDPARn to avoid register write delays and
+ * improve performance.
+ */
+ if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+ }
/*
* Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're
@@ -348,6 +354,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
cmd |= DWC3_DEPCMD_CMDACT;
dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
+
+ if (!(cmd & DWC3_DEPCMD_CMDACT) ||
+ (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
+ !(cmd & DWC3_DEPCMD_CMDIOC))) {
+ ret = 0;
+ goto skip_status;
+ }
+
do {
reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
if (!(reg & DWC3_DEPCMD_CMDACT)) {
@@ -358,6 +372,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
ret = 0;
break;
case DEPEVT_TRANSFER_NO_RESOURCE:
+ dev_warn(dwc->dev, "No resource for %s\n",
+ dep->name);
ret = -EINVAL;
break;
case DEPEVT_TRANSFER_BUS_EXPIRY:
@@ -387,9 +403,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
cmd_status = -ETIMEDOUT;
}
- if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- dep->flags |= DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_get_transfer_index(dep);
+skip_status:
+
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ if (ret == 0)
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+
+ if (ret != -ETIMEDOUT)
+ dwc3_gadget_ep_get_transfer_index(dep);
}
if (saved_config) {
@@ -399,7 +420,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
}
return ret;
-
}
static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
@@ -416,8 +436,9 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
* IN transfers due to a mishandled error condition. Synopsys
* STAR 9000614252.
*/
- if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
- (dwc->gadget.speed >= USB_SPEED_SUPER))
+ if (dep->direction &&
+ !DWC3_VER_IS_PRIOR(DWC3, 260A) &&
+ (dwc->gadget->speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(&params, 0, sizeof(params));
@@ -426,9 +447,9 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
}
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
- struct dwc3_trb *trb)
+ struct dwc3_trb *trb)
{
- u32 offset = (char *) trb - (char *) dep->trb_pool;
+ u32 offset = (char *) trb - (char *) dep->trb_pool;
return dep->trb_pool_dma + offset;
}
@@ -438,12 +459,11 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
if (dep->trb_pool)
return 0;
- dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) *
- DWC3_TRB_NUM,
- &dep->trb_pool_dma);
+ dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ &dep->trb_pool_dma);
if (!dep->trb_pool) {
dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
- dep->name);
+ dep->name);
return -ENOMEM;
}
@@ -452,7 +472,8 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
- dma_free_coherent(dep->trb_pool, 0, sizeof(dma_addr_t));
+ dma_free_coherent(dep->trb_pool, dep->trb_pool_dma,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
dep->trb_pool = NULL;
dep->trb_pool_dma = 0;
@@ -467,7 +488,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
- &params);
+ &params);
}
/**
@@ -506,10 +527,10 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
static int dwc3_gadget_start_config(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3 *dwc;
- u32 cmd;
- int i;
- int ret;
+ struct dwc3 *dwc;
+ u32 cmd;
+ int i;
+ int ret;
if (dep->number)
return 0;
@@ -552,9 +573,10 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget.speed == USB_SPEED_SUPER) {
- u32 burst = dep->endpoint.maxburst - 1;
- params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+ if (dwc->gadget->speed >= USB_SPEED_SUPER) {
+ u32 burst = dep->endpoint.maxburst;
+
+ params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
params.param0 |= action;
@@ -569,6 +591,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+ | DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_STREAM_EVENT_EN;
dep->stream_capable = true;
}
@@ -592,31 +615,239 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1);
if (desc->bInterval) {
- params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1);
- dep->interval = 1 << (desc->bInterval - 1);
+ u8 bInterval_m1;
+
+ /*
+ * Valid range for DEPCFG.bInterval_m1 is from 0 to 13.
+ *
+ * NOTE: The programming guide incorrectly stated bInterval_m1
+ * must be set to 0 when operating in fullspeed. Internally the
+ * controller does not have this limitation. See DWC_usb3x
+ * programming guide section 3.2.2.1.
+ */
+ bInterval_m1 = min_t(u8, desc->bInterval - 1, 13);
+
+ if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+ dwc->gadget->speed == USB_SPEED_FULL)
+ dep->interval = desc->bInterval;
+ else
+ dep->interval = 1 << (desc->bInterval - 1);
+
+ params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1);
}
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}
/**
- * __dwc3_gadget_ep_enable - Initializes a HW endpoint
+ * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
+ * @dwc: pointer to the DWC3 context
+ * @mult: multiplier to be used when calculating the fifo_size
+ *
+ * Calculates the size value based on the equation below:
+ *
+ * DWC3 revision 280A and prior:
+ * fifo_size = mult * (max_packet / mdwidth) + 1;
+ *
+ * DWC3 revision 290A and onwards:
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * The max packet size is set to 1024, as the txfifo requirements mainly apply
+ * to super speed USB use cases. However, it is safe to overestimate the fifo
+ * allocations for other scenarios, i.e. high speed USB.
+ */
+static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
+{
+ int max_packet = 1024;
+ int fifo_size;
+ int mdwidth;
+
+ mdwidth = dwc3_mdwidth(dwc);
+
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth >>= 3;
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 290A))
+ fifo_size = mult * (max_packet / mdwidth) + 1;
+ else
+ fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
+ return fifo_size;
+}
+
+/**
+ * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation
+ * @dwc: pointer to the DWC3 context
+ *
+ * Iterates through all the endpoint registers and clears the previous txfifo
+ * allocations.
+ */
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int fifo_depth;
+ int size;
+ int num;
+
+ if (!dwc->do_fifo_resize)
+ return;
+
+ /* Read ep0IN related TXFIFO size */
+ dep = dwc->eps[1];
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ if (DWC3_IP_IS(DWC3))
+ fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
+ else
+ fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size);
+
+ dwc->last_fifo_depth = fifo_depth;
+ /* Clear existing TXFIFO for all IN eps except ep0 */
+ for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
+ num += 2) {
+ dep = dwc->eps[num];
+ /* Don't change TXFRAMNUM on usb31 version */
+ size = DWC3_IP_IS(DWC3) ? 0 :
+ dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
+ DWC31_GTXFIFOSIZ_TXFRAMNUM;
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+ dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
+ }
+ dwc->num_ep_resized = 0;
+}
+
+/*
+ * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
+ * @dwc: pointer to our context structure
+ *
+ * This function will a best effort FIFO allocation in order
+ * to improve FIFO usage and throughput, while still allowing
+ * us to enable as many endpoints as possible.
+ *
+ * Keep in mind that this operation will be highly dependent
+ * on the configured size for RAM1 - which contains TxFifo -,
+ * the amount of endpoints enabled on coreConsultant tool, and
+ * the width of the Master Bus.
+ *
+ * In general, FIFO depths are represented with the following equation:
+ *
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * In conjunction with dwc3_gadget_check_config(), this resizing logic will
+ * ensure that all endpoints will have enough internal memory for one max
+ * packet per endpoint.
+ */
+static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int fifo_0_start;
+ int ram1_depth;
+ int fifo_size;
+ int min_depth;
+ int num_in_ep;
+ int remaining;
+ int num_fifos = 1;
+ int fifo;
+ int tmp;
+
+ if (!dwc->do_fifo_resize)
+ return 0;
+
+ /* resize IN endpoints except ep0 */
+ if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
+ return 0;
+
+ /* bail if already resized */
+ if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
+ return 0;
+
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+
+ if ((dep->endpoint.maxburst > 1 &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ num_fifos = 3;
+
+ if (dep->endpoint.maxburst > 6 &&
+ (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31))
+ num_fifos = dwc->tx_fifo_resize_max_num;
+
+ /* FIFO size for a single buffer */
+ fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
+
+ /* Calculate the number of remaining EPs w/o any FIFO */
+ num_in_ep = dwc->max_cfg_eps;
+ num_in_ep -= dwc->num_ep_resized;
+
+ /* Reserve at least one FIFO for the number of IN EPs */
+ min_depth = num_in_ep * (fifo + 1);
+ remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
+ remaining = max_t(int, 0, remaining);
+ /*
+ * We've already reserved 1 FIFO per EP, so check what we can fit in
+ * addition to it. If there is not enough remaining space, allocate
+ * all the remaining space to the EP.
+ */
+ fifo_size = (num_fifos - 1) * fifo;
+ if (remaining < fifo_size)
+ fifo_size = remaining;
+
+ fifo_size += fifo;
+ /* Last increment according to the TX FIFO size equation */
+ fifo_size++;
+
+ /* Check if TXFIFOs start at non-zero addr */
+ tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
+
+ fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
+ if (DWC3_IP_IS(DWC3))
+ dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+ else
+ dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+ /* Check fifo size allocation doesn't exceed available RAM size. */
+ if (dwc->last_fifo_depth >= ram1_depth) {
+ dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
+ dwc->last_fifo_depth, ram1_depth,
+ dep->endpoint.name, fifo_size);
+ if (DWC3_IP_IS(DWC3))
+ fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+ else
+ fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+ dwc->last_fifo_depth -= fifo_size;
+ return -ENOMEM;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+ dep->flags |= DWC3_EP_TXFIFO_RESIZED;
+ dwc->num_ep_resized++;
+
+ return 0;
+}
+
+/**
+ * __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
- * @desc: USB Endpoint Descriptor
+ * @action: one of INIT, MODIFY or RESTORE
*
- * Caller should take care of locking
+ * Caller should take care of locking. Execute all necessary commands to
+ * initialize a HW endpoint so it can be used by a gadget driver.
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
- struct dwc3 *dwc = dep->dwc;
+ struct dwc3 *dwc = dep->dwc;
- u32 reg;
- int ret;
-
- dev_dbg(dwc->dev, "Enabling %s\n", dep->name);
+ u32 reg;
+ int ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
+ ret = dwc3_gadget_resize_tx_fifos(dep);
+ if (ret)
+ return ret;
+
ret = dwc3_gadget_start_config(dep);
if (ret)
return ret;
@@ -627,8 +858,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
return ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
- struct dwc3_trb *trb_st_hw;
- struct dwc3_trb *trb_link;
+ struct dwc3_trb *trb_st_hw;
+ struct dwc3_trb *trb_link;
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
@@ -637,12 +868,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dep->trb_dequeue = 0;
+ dep->trb_enqueue = 0;
+
if (usb_endpoint_xfer_control(desc))
- return 0;
+ goto out;
/* Initialize the TRB ring */
- dep->trb_dequeue = 0;
- dep->trb_enqueue = 0;
memset_io(dep->trb_pool, 0,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
@@ -650,12 +882,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
trb_st_hw = &dep->trb_pool[0];
trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
- memset_io(trb_link, 0, sizeof(*trb_link));
-
- trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep,
- trb_st_hw));
- trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep,
- trb_st_hw));
+ trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
+ trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
}
@@ -664,10 +892,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
+ if (usb_endpoint_xfer_bulk(desc) ||
usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3_trb *trb;
+ struct dwc3_trb *trb;
dma_addr_t trb_dma;
u32 cmd;
@@ -683,47 +911,88 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (ret < 0)
return ret;
+
+ if (dep->stream_capable) {
+ /*
+ * For streams, at start, there maybe a race where the
+ * host primes the endpoint before the function driver
+ * queues a request to initiate a stream. In that case,
+ * the controller will not see the prime to generate the
+ * ERDY and start stream. To workaround this, issue a
+ * no-op TRB as normal, but end it immediately. As a
+ * result, when the function driver queues the request,
+ * the next START_TRANSFER command will cause the
+ * controller to generate an ERDY to initiate the
+ * stream.
+ */
+ dwc3_stop_active_transfer(dep, true, true);
+
+ /*
+ * All stream eps will reinitiate stream on NoStream
+ * rejection until we can determine that the host can
+ * prime after the first transfer.
+ *
+ * However, if the controller is capable of
+ * TXF_FLUSH_BYPASS, then IN direction endpoints will
+ * automatically restart the stream without the driver
+ * initiation.
+ */
+ if (!dep->direction ||
+ !(dwc->hwparams.hwparams9 &
+ DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS))
+ dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+ }
}
+out:
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
- bool interrupt);
-static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
+void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status)
{
- struct dwc3_request *req;
+ struct dwc3_request *req;
dwc3_stop_active_transfer(dep, true, false);
+ /* If endxfer is delayed, avoid unmapping requests */
+ if (dep->flags & DWC3_EP_DELAY_STOP)
+ return;
+
/* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) {
req = next_request(&dep->started_list);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ dwc3_gadget_giveback(dep, req, status);
}
while (!list_empty(&dep->pending_list)) {
req = next_request(&dep->pending_list);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ dwc3_gadget_giveback(dep, req, status);
+ }
+
+ while (!list_empty(&dep->cancelled_list)) {
+ req = next_request(&dep->cancelled_list);
+
+ dwc3_gadget_giveback(dep, req, status);
}
}
/**
- * __dwc3_gadget_ep_disable - Disables a HW endpoint
+ * __dwc3_gadget_ep_disable - disables a hw endpoint
* @dep: the endpoint to disable
*
- * This function also removes requests which are currently processed ny the
- * hardware and those which are not yet scheduled.
+ * This function undoes what __dwc3_gadget_ep_enable did and also removes
+ * requests which are currently being processed by the hardware and those which
+ * are not yet scheduled.
+ *
* Caller should take care of locking.
*/
static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
{
- struct dwc3 *dwc = dep->dwc;
- u32 reg;
-
- dwc3_remove_requests(dwc, dep);
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+ u32 mask;
/* make sure HW endpoint isn't stalled */
if (dep->flags & DWC3_EP_STALL)
@@ -733,9 +1002,19 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
reg &= ~DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
+
dep->stream_capable = false;
dep->type = 0;
- dep->flags = 0;
+ mask = DWC3_EP_TXFIFO_RESIZED;
+ /*
+ * dwc3_remove_requests() can exit early if DWC3 EP delayed stop is
+ * set. Do not clear DEP flags, so that the end transfer command will
+ * be reattempted during the next SETUP stage.
+ */
+ if (dep->flags & DWC3_EP_DELAY_STOP)
+ mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
+ dep->flags &= mask;
/* Clear out the ep descriptors for non-ep0 */
if (dep->number > 1) {
@@ -749,7 +1028,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
/* -------------------------------------------------------------------------- */
static int dwc3_gadget_ep0_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc)
+ const struct usb_endpoint_descriptor *desc)
{
return -EINVAL;
}
@@ -762,12 +1041,12 @@ static int dwc3_gadget_ep0_disable(struct usb_ep *ep)
/* -------------------------------------------------------------------------- */
static int dwc3_gadget_ep_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc)
+ const struct usb_endpoint_descriptor *desc)
{
- struct dwc3_ep *dep;
- struct dwc3 *dwc;
- unsigned long flags;
- int ret;
+ struct dwc3_ep *dep;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ int ret;
if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
pr_debug("dwc3: invalid parameters\n");
@@ -782,11 +1061,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (dep->flags & DWC3_EP_ENABLED) {
- WARN(true, "%s is already enabled\n",
- dep->name);
+ if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
+ "%s is already enabled\n",
+ dep->name))
return 0;
- }
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
@@ -797,10 +1075,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
static int dwc3_gadget_ep_disable(struct usb_ep *ep)
{
- struct dwc3_ep *dep;
- struct dwc3 *dwc;
- unsigned long flags;
- int ret;
+ struct dwc3_ep *dep;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ int ret;
if (!ep) {
pr_debug("dwc3: invalid parameters\n");
@@ -810,11 +1088,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (!(dep->flags & DWC3_EP_ENABLED)) {
- WARN(true, "%s is already disabled\n",
- dep->name);
+ if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
+ "%s is already disabled\n",
+ dep->name))
return 0;
- }
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_disable(dep);
@@ -825,10 +1102,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep)
{
- struct dwc3_request *req;
- struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3_request *req;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
- req = xzalloc(sizeof(*req));
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return NULL;
@@ -841,9 +1118,9 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep)
}
static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
- struct usb_request *request)
+ struct usb_request *request)
{
- struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_request *req = to_dwc3_request(request);
kfree(req);
}
@@ -869,19 +1146,19 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{
- struct dwc3_trb *tmp;
- u8 trbs_left;
+ u8 trbs_left;
/*
- * If enqueue & dequeue are equal than it is either full or empty.
- *
- * One way to know for sure is if the TRB right before us has HWO bit
- * set or not. If it has, then we're definitely full and can't fit any
- * more transfers in our ring.
+ * If the enqueue & dequeue are equal then the TRB ring is either full
+ * or empty. It's considered full when there are DWC3_TRB_NUM-1 of TRBs
+ * pending to be processed by the driver.
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
- tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ /*
+ * If there is any request remained in the started_list at
+ * this point, that means there is no TRB available.
+ */
+ if (!list_empty(&dep->started_list))
return 0;
return DWC3_TRB_NUM - 1;
@@ -896,17 +1173,47 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
return trbs_left;
}
-static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
- dma_addr_t dma, unsigned length,
- unsigned chain, unsigned node,
- unsigned stream_id, unsigned short_not_ok,
- unsigned no_interrupt)
+/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @trb_length: buffer size of the TRB
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ * @use_bounce_buffer: set to use bounce buffer
+ * @must_interrupt: set to interrupt on TRB completion
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int trb_length,
+ unsigned int chain, unsigned int node, bool use_bounce_buffer,
+ bool must_interrupt)
{
- struct dwc3 *dwc = dep->dwc;
- struct usb_gadget *gadget = &dwc->gadget;
- enum usb_device_speed speed = gadget->speed;
+ struct dwc3_trb *trb;
+ dma_addr_t dma;
+ unsigned int stream_id = req->request.stream_id;
+ unsigned int short_not_ok = req->request.short_not_ok;
+ unsigned int no_interrupt = req->request.no_interrupt;
+ unsigned int is_last = req->request.is_last;
+ struct dwc3 *dwc = dep->dwc;
+ struct usb_gadget *gadget = dwc->gadget;
+ enum usb_device_speed speed = gadget->speed;
+
+ if (use_bounce_buffer)
+ dma = dep->dwc->bounce_addr;
+ else
+ dma = req->request.dma;
+
+ trb = &dep->trb_pool[dep->trb_enqueue];
- trb->size = DWC3_TRB_SIZE_LENGTH(length);
+ if (!req->trb) {
+ dwc3_gadget_move_started_request(req);
+ req->trb = trb;
+ req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+ }
+
+ req->num_trbs++;
+
+ trb->size = DWC3_TRB_SIZE_LENGTH(trb_length);
trb->bpl = lower_32_bits(dma);
trb->bph = upper_32_bits(dma);
@@ -946,10 +1253,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
unsigned int mult = 2;
unsigned int maxp = usb_endpoint_maxp(ep->desc);
- if (length <= (2 * maxp))
+ if (req->request.length <= (2 * maxp))
mult--;
- if (length <= maxp)
+ if (req->request.length <= maxp)
mult--;
trb->size |= DWC3_TRB_SIZE_PCM1(mult);
@@ -958,8 +1265,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
}
- /* always enable Interrupt on Missed ISOC */
- trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ if (!no_interrupt && !chain)
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
break;
case USB_ENDPOINT_XFER_BULK:
@@ -972,7 +1279,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
* checked it already :)
*/
dev_warn(dwc->dev, "Unknown endpoint type %d\n",
- usb_endpoint_type(dep->endpoint.desc));
+ usb_endpoint_type(dep->endpoint.desc));
}
/*
@@ -987,149 +1294,197 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
- if ((!no_interrupt && !chain) ||
- (dwc3_calc_trbs_left(dep) == 1))
+ /* All TRBs setup for MST must set CSP=1 when LST=0 */
+ if (dep->stream_capable && DWC3_MST_CAPABLE(&dwc->hwparams))
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
+
+ if ((!no_interrupt && !chain) || must_interrupt)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
+ else if (dep->stream_capable && is_last &&
+ !DWC3_MST_CAPABLE(&dwc->hwparams))
+ trb->ctrl |= DWC3_TRB_CTRL_LST;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
+ /*
+ * As per data book 4.2.3.2TRB Control Bit Rules section
+ *
+ * The controller autonomously checks the HWO field of a TRB to determine if the
+ * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
+ * is valid before setting the HWO field to '1'. In most systems, this means that
+ * software must update the fourth DWORD of a TRB last.
+ *
+ * However there is a possibility of CPU re-ordering here which can cause
+ * controller to observe the HWO bit set prematurely.
+ * Add a write memory barrier to prevent CPU re-ordering.
+ */
+ /* wmb() FIXME */
trb->ctrl |= DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_enq(dep);
}
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- * @chain: should this TRB be chained to the next?
- * @node: only for isochronous endpoints. First TRB needs different type.
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
- struct dwc3_request *req,
- unsigned chain, unsigned node)
+static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
{
- struct dwc3_trb *trb;
- unsigned int length;
- dma_addr_t dma;
- unsigned stream_id = req->request.stream_id;
- unsigned short_not_ok = req->request.short_not_ok;
- unsigned no_interrupt = req->request.no_interrupt;
-
- length = req->request.length;
- dma = req->request.dma;
-
- trb = &dep->trb_pool[dep->trb_enqueue];
-
- if (!req->trb) {
- dwc3_gadget_move_started_request(req);
- req->trb = trb;
- req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- }
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = req->request.length % maxp;
- req->num_trbs++;
+ if ((req->request.length && req->request.zero && !rem &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc)) ||
+ (!req->direction && rem))
+ return true;
- __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
- stream_id, short_not_ok, no_interrupt);
+ return false;
}
-static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
- struct dwc3_request *req)
+/**
+ * dwc3_prepare_last_sg - prepare TRBs for the last SG entry
+ * @dep: The endpoint that the request belongs to
+ * @req: The request to prepare
+ * @entry_length: The last SG entry size
+ * @node: Indicates whether this is not the first entry (for isoc only)
+ *
+ * Return the number of TRBs prepared.
+ */
+static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int entry_length,
+ unsigned int node)
{
- unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
- unsigned int rem = length % maxp;
+ unsigned int rem = req->request.length % maxp;
+ unsigned int num_trbs = 1;
- if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
+ if (dwc3_needs_extra_trb(dep, req))
+ num_trbs++;
- req->needs_extra_trb = true;
+ if (dwc3_calc_trbs_left(dep) < num_trbs)
+ return 0;
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, true, 0);
+ req->needs_extra_trb = num_trbs > 1;
- /* Now prepare one extra TRB to align transfer size */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
- } else if (req->request.zero && req->request.length &&
- (IS_ALIGNED(req->request.length, maxp))) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
+ /* Prepare a normal TRB */
+ if (req->direction || req->request.length)
+ dwc3_prepare_one_trb(dep, req, entry_length,
+ req->needs_extra_trb, node, false, false);
- req->needs_extra_trb = true;
+ /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */
+ if ((!req->direction && !req->request.length) || req->needs_extra_trb)
+ dwc3_prepare_one_trb(dep, req,
+ req->direction ? 0 : maxp - rem,
+ false, 1, true, false);
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, true, 0);
+ return num_trbs;
+}
- /* Now prepare one extra TRB to handle ZLP */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
- } else {
- dwc3_prepare_one_trb(dep, req, false, 0);
- }
+static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep,
+ struct dwc3_request *req)
+{
+ return dwc3_prepare_last_sg(dep, req, req->request.length, 0);
}
/*
* dwc3_prepare_trbs - setup TRBs from requests
* @dep: endpoint for which requests are being prepared
- * @starting: true if the endpoint is idle and no requests are queued.
*
* The function goes through the requests list and sets up TRBs for the
* transfers. The function returns once there are no more TRBs available or
* it runs out of requests.
+ *
+ * Returns the number of TRBs prepared or negative errno.
*/
-static void dwc3_prepare_trbs(struct dwc3_ep *dep)
+static int dwc3_prepare_trbs(struct dwc3_ep *dep)
{
- struct dwc3_request *req, *n;
- struct dwc3 *dwc = dep->dwc;
- dma_addr_t dma_addr;
+ struct dwc3_request *req, *n;
+ int ret = 0;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
+ /*
+ * We can get in a situation where there's a request in the started list
+ * but there weren't enough TRBs to fully kick it in the first time
+ * around, so it has been waiting for more TRBs to be freed up.
+ *
+ * In that case, we should check if we have a request with pending_sgs
+ * in the started list and prepare TRBs for that request first,
+ * otherwise we will prepare TRBs completely out of order and that will
+ * break things.
+ */
+ list_for_each_entry(req, &dep->started_list, list) {
+ if (!dwc3_calc_trbs_left(dep))
+ return ret;
+
+ /*
+ * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+ * burst capability may try to read and use TRBs beyond the
+ * active transfer instead of stopping.
+ */
+ if (dep->stream_capable && req->request.is_last &&
+ !DWC3_MST_CAPABLE(&dep->dwc->hwparams))
+ return ret;
+ }
+
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
- dma_addr = dma_map_single(dwc->dev, req->request.buf,
- req->request.length,
- dep->number ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (dma_mapping_error(dwc->dev, dma_addr))
- return;
+ struct dwc3 *dwc = dep->dwc;
- req->request.dma = dma_addr;
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
+ dep->direction);
+ if (ret)
+ return ret;
- dwc3_prepare_one_trb_linear(dep, req);
+ req->sg = req->request.sg;
+ req->start_sg = req->sg;
+ req->num_queued_sgs = 0;
+ req->num_pending_sgs = req->request.num_mapped_sgs;
- if (!dwc3_calc_trbs_left(dep))
- return;
+ ret = dwc3_prepare_trbs_linear(dep, req);
+
+ if (!ret || !dwc3_calc_trbs_left(dep))
+ return ret;
+
+ /*
+ * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+ * burst capability may try to read and use TRBs beyond the
+ * active transfer instead of stopping.
+ */
+ if (dep->stream_capable && req->request.is_last &&
+ !DWC3_MST_CAPABLE(&dwc->hwparams))
+ return ret;
}
+
+ return ret;
}
+static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
+
static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3_request *req;
- int starting;
- int ret;
- u32 cmd;
+ struct dwc3_request *req;
+ int starting;
+ int ret;
+ u32 cmd;
- if (!dwc3_calc_trbs_left(dep))
- return 0;
+ /*
+ * Note that it's normal to have no new TRBs prepared (i.e. ret == 0).
+ * This happens when we need to stop and restart a transfer such as in
+ * the case of reinitiating a stream or retrying an isoc transfer.
+ */
+ ret = dwc3_prepare_trbs(dep);
+ if (ret < 0)
+ return ret;
starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
- dwc3_prepare_trbs(dep);
+ /*
+ * If there's no new TRB prepared and we don't need to restart a
+ * transfer, there's no need to update the transfer.
+ */
+ if (!ret && !starting)
+ return ret;
req = next_request(&dep->started_list);
if (!req) {
@@ -1156,29 +1511,84 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (ret < 0) {
- /*
- * FIXME we need to iterate over the list of requests
- * here and stop, unmap, free and del each of the linked
- * requests instead of what we do now.
- */
- if (req->trb)
- memset(req->trb, 0, sizeof(struct dwc3_trb));
- dwc3_gadget_del_and_unmap_request(dep, req, ret);
+ struct dwc3_request *tmp;
+
+ if (ret == -EAGAIN)
+ return ret;
+
+ dwc3_stop_active_transfer(dep, true, true);
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
+
+ /* If ep isn't started, then there's no end transfer pending */
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
return ret;
}
+ if (dep->stream_capable && req->request.is_last &&
+ !DWC3_MST_CAPABLE(&dep->dwc->hwparams))
+ dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
return 0;
}
static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
{
- u32 reg;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
return DWC3_DSTS_SOFFN(reg);
}
/**
+ * __dwc3_stop_active_transfer - stop the current active transfer
+ * @dep: isoc endpoint
+ * @force: set forcerm bit in the command
+ * @interrupt: command complete interrupt after End Transfer command
+ *
+ * When setting force, the ForceRM bit will be set. In that case
+ * the controller won't update the TRB progress on command
+ * completion. It also won't clear the HWO bit in the TRB.
+ * The command will also not complete immediately in that case.
+ */
+static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
+ cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ /*
+ * If the End Transfer command was timed out while the device is
+ * not in SETUP phase, it's possible that an incoming Setup packet
+ * may prevent the command's completion. Let's retry when the
+ * ep0state returns to EP0_SETUP_PHASE.
+ */
+ if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) {
+ dep->flags |= DWC3_EP_DELAY_STOP;
+ return 0;
+ }
+ WARN_ON_ONCE(ret);
+ dep->resource_index = 0;
+
+ if (!interrupt)
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ else if (!ret)
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+
+ dep->flags &= ~DWC3_EP_DELAY_STOP;
+ return ret;
+}
+
+/**
* dwc3_gadget_start_isoc_quirk - workaround invalid frame number
* @dep: isoc endpoint
*
@@ -1235,7 +1645,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
* Check if we can start isoc transfer on the next interval or
* 4 uframes in the future with BIT[15:14] as dep->combo_num
*/
- test_frame_number = dep->frame_number & 0x3fff;
+ test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK;
test_frame_number |= dep->combo_num << 14;
test_frame_number += max_t(u32, 4, dep->interval);
@@ -1282,7 +1692,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
else if (test0 && test1)
dep->combo_num = 0;
- dep->frame_number &= 0x3fff;
+ dep->frame_number &= DWC3_FRNUMBER_MASK;
dep->frame_number |= dep->combo_num << 14;
dep->frame_number += max_t(u32, 4, dep->interval);
@@ -1295,63 +1705,110 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
int ret;
int i;
- if (list_empty(&dep->pending_list)) {
+ if (list_empty(&dep->pending_list) &&
+ list_empty(&dep->started_list)) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
return -EAGAIN;
}
- if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
- (dwc->revision <= DWC3_USB31_REVISION_160A ||
- (dwc->revision == DWC3_USB31_REVISION_170A &&
- dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
- dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
-
- if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
+ if (!dwc->dis_start_transfer_quirk &&
+ (DWC3_VER_IS_PRIOR(DWC31, 170A) ||
+ DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
+ if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction)
return dwc3_gadget_start_isoc_quirk(dep);
}
+ if (desc->bInterval <= 14 &&
+ dwc->gadget->speed >= USB_SPEED_HIGH) {
+ u32 frame = __dwc3_gadget_get_frame(dwc);
+ bool rollover = frame <
+ (dep->frame_number & DWC3_FRNUMBER_MASK);
+
+ /*
+ * frame_number is set from XferNotReady and may be already
+ * out of date. DSTS only provides the lower 14 bit of the
+ * current frame number. So add the upper two bits of
+ * frame_number and handle a possible rollover.
+ * This will provide the correct frame_number unless more than
+ * rollover has happened since XferNotReady.
+ */
+
+ dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) |
+ frame;
+ if (rollover)
+ dep->frame_number += BIT(14);
+ }
+
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
- dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
+ int future_interval = i + 1;
+
+ /* Give the controller at least 500us to schedule transfers */
+ if (desc->bInterval < 3)
+ future_interval += 3 - desc->bInterval;
+
+ dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval);
ret = __dwc3_gadget_kick_transfer(dep);
if (ret != -EAGAIN)
break;
}
+ /*
+ * After a number of unsuccessful start attempts due to bus-expiry
+ * status, issue END_TRANSFER command and retry on the next XferNotReady
+ * event.
+ */
+ if (ret == -EAGAIN)
+ ret = __dwc3_stop_active_transfer(dep, false, true);
+
return ret;
}
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
- struct dwc3 *dwc = dep->dwc;
+ struct dwc3 *dwc = dep->dwc;
- if (!dep->endpoint.desc) {
- dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
+ dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
return -ESHUTDOWN;
}
- if (req->dep != dep) {
- WARN(true, "request %p belongs to '%s'\n",
- &req->request, req->dep->name);
+ if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
+ &req->request, req->dep->name))
return -EINVAL;
- }
- if (req->status < DWC3_REQUEST_STATUS_COMPLETED) {
- WARN(true, "request %p already in flight\n", &req->request);
+ if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
+ "%s: request %pK already in flight\n",
+ dep->name, &req->request))
return -EINVAL;
- }
- req->request.actual = 0;
- req->request.status = -EINPROGRESS;
+ req->request.actual = 0;
+ req->request.status = -EINPROGRESS;
list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
+ if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
+ return 0;
+
+ /*
+ * Start the transfer only after the END_TRANSFER is completed
+ * and endpoint STALL is cleared.
+ */
+ if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
+ (dep->flags & DWC3_EP_WEDGE) ||
+ (dep->flags & DWC3_EP_DELAY_STOP) ||
+ (dep->flags & DWC3_EP_STALL)) {
+ dep->flags |= DWC3_EP_DELAY_START;
+ return 0;
+ }
+
/*
* NOTICE: Isochronous endpoints should NEVER be prestarted. We must
* wait for a XferNotReady event so we will know what's the current
@@ -1361,28 +1818,27 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* errors which will force us issue EndTransfer command.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
- !(dep->flags & DWC3_EP_TRANSFER_STARTED))
- return 0;
-
- if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST))
return __dwc3_gadget_start_isoc(dep);
- }
+
+ return 0;
}
}
- return __dwc3_gadget_kick_transfer(dep);
+ __dwc3_gadget_kick_transfer(dep);
+
+ return 0;
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request)
{
- struct dwc3_request *req = to_dwc3_request(request);
- struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
- unsigned long flags;
+ unsigned long flags;
- int ret;
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req);
@@ -1391,11 +1847,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request)
return ret;
}
-static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep,
- struct dwc3_request *req)
+static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
{
int i;
+ /* If req->trb is not set, then the request has not started */
+ if (!req->trb)
+ return;
+
/*
* If request was already started, this means we had to
* stop the transfer. With that we also need to ignore
@@ -1409,7 +1868,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep,
for (i = 0; i < req->num_trbs; i++) {
struct dwc3_trb *trb;
- trb = req->trb + i;
+ trb = &dep->trb_pool[dep->trb_dequeue];
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
}
@@ -1419,61 +1878,87 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep,
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
{
- struct dwc3_request *req;
- struct dwc3_request *tmp;
+ struct dwc3_request *req;
+ struct dwc3 *dwc = dep->dwc;
- list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+ while (!list_empty(&dep->cancelled_list)) {
+ req = next_request(&dep->cancelled_list);
dwc3_gadget_ep_skip_trbs(dep, req);
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ switch (req->status) {
+ case DWC3_REQUEST_STATUS_DISCONNECTED:
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ break;
+ case DWC3_REQUEST_STATUS_DEQUEUED:
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ break;
+ case DWC3_REQUEST_STATUS_STALLED:
+ dwc3_gadget_giveback(dep, req, -EPIPE);
+ break;
+ default:
+ dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ break;
+ }
+ /*
+ * The endpoint is disabled, let the dwc3_remove_requests()
+ * handle the cleanup.
+ */
+ if (!dep->endpoint.desc)
+ break;
}
}
static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
- struct usb_request *request)
+ struct usb_request *request)
{
- struct dwc3_request *req = to_dwc3_request(request);
- struct dwc3_request *r = NULL;
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
- unsigned long flags;
- int ret = 0;
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_request *r = NULL;
+
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&dwc->lock, flags);
- list_for_each_entry(r, &dep->pending_list, list) {
+ list_for_each_entry(r, &dep->cancelled_list, list) {
if (r == req)
- break;
+ goto out;
}
- if (r != req) {
- list_for_each_entry(r, &dep->started_list, list) {
- if (r == req)
- break;
+ list_for_each_entry(r, &dep->pending_list, list) {
+ if (r == req) {
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ goto out;
}
+ }
+
+ list_for_each_entry(r, &dep->started_list, list) {
if (r == req) {
+ struct dwc3_request *t;
+
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
- if (!r->trb)
- goto out0;
+ /*
+ * Remove any started request if the transfer is
+ * cancelled.
+ */
+ list_for_each_entry_safe(r, t, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(r,
+ DWC3_REQUEST_STATUS_DEQUEUED);
- dwc3_gadget_move_cancelled_request(req);
- if (dep->flags & DWC3_EP_TRANSFER_STARTED)
- goto out0;
- else
- goto out1;
+ dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
+ goto out;
}
- dev_err(dwc->dev, "request %p was not queued to %s\n",
- request, ep->name);
- ret = -EINVAL;
- goto out0;
}
-out1:
- /* giveback the request */
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
-
-out0:
+ dev_err(dwc->dev, "request %pK was not queued to %s\n",
+ request, ep->name);
+ ret = -EINVAL;
+out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1481,9 +1966,11 @@ out0:
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
{
- struct dwc3_gadget_ep_cmd_params params;
- struct dwc3 *dwc = dep->dwc;
- int ret;
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+ int ret;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
@@ -1494,8 +1981,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (value) {
struct dwc3_trb *trb;
- unsigned transfer_in_flight;
- unsigned started;
+
+ unsigned int transfer_in_flight;
+ unsigned int started;
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
@@ -1511,19 +1999,53 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
}
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL,
- &params);
+ &params);
if (ret)
dev_err(dwc->dev, "failed to set STALL on %s\n",
dep->name);
else
dep->flags |= DWC3_EP_STALL;
} else {
+ /*
+ * Don't issue CLEAR_STALL command to control endpoints. The
+ * controller automatically clears the STALL when it receives
+ * the SETUP token.
+ */
+ if (dep->number <= 1) {
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ return 0;
+ }
+
+ dwc3_stop_active_transfer(dep, true, true);
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
+
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
+ (dep->flags & DWC3_EP_DELAY_STOP)) {
+ dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+ if (protocol)
+ dwc->clear_stall_protocol = dep->number;
+
+ return 0;
+ }
+
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
ret = dwc3_send_clear_stall_ep_cmd(dep);
- if (ret)
+ if (ret) {
dev_err(dwc->dev, "failed to clear STALL on %s\n",
dep->name);
- else
- dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ return ret;
+ }
+
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+
+ if ((dep->flags & DWC3_EP_DELAY_START) &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
+
+ dep->flags &= ~DWC3_EP_DELAY_START;
}
return ret;
@@ -1531,10 +2053,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
{
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- unsigned long flags;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
- int ret;
+ unsigned long flags;
+
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
@@ -1545,9 +2068,9 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
{
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- unsigned long flags;
- int ret;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ unsigned long flags;
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
dep->flags |= DWC3_EP_WEDGE;
@@ -1564,13 +2087,13 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
/* -------------------------------------------------------------------------- */
static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
- .enable = dwc3_gadget_ep0_enable,
+ .enable = dwc3_gadget_ep0_enable,
.disable = dwc3_gadget_ep0_disable,
.alloc_request = dwc3_gadget_ep_alloc_request,
.free_request = dwc3_gadget_ep_free_request,
@@ -1581,7 +2104,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
};
static const struct usb_ep_ops dwc3_gadget_ep_ops = {
- .enable = dwc3_gadget_ep_enable,
+ .enable = dwc3_gadget_ep_enable,
.disable = dwc3_gadget_ep_disable,
.alloc_request = dwc3_gadget_ep_alloc_request,
.free_request = dwc3_gadget_ep_free_request,
@@ -1595,20 +2118,19 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3 *dwc = gadget_to_dwc(g);
return __dwc3_gadget_get_frame(dwc);
}
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
- int retries;
+ int retries;
- int ret;
- u32 reg;
+ int ret;
+ u32 reg;
- u8 link_state;
- u8 speed;
+ u8 link_state;
/*
* According to the Databook Remote wakeup request should
@@ -1618,16 +2140,15 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
*/
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
- speed = reg & DWC3_DSTS_CONNECTSPD;
- if ((speed == DWC3_DSTS_SUPERSPEED) ||
- (speed == DWC3_DSTS_SUPERSPEED_PLUS))
- return 0;
-
link_state = DWC3_DSTS_USBLNKST(reg);
switch (link_state) {
+ case DWC3_LINK_STATE_RESET:
case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
+ case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */
+ case DWC3_LINK_STATE_U1:
+ case DWC3_LINK_STATE_RESUME:
break;
default:
return -EINVAL;
@@ -1640,7 +2161,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
}
/* Recent versions do this automatically */
- if (dwc->revision < DWC3_REVISION_194A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
@@ -1668,9 +2189,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
static int dwc3_gadget_wakeup(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
- int ret;
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_wakeup(dwc);
@@ -1680,37 +2201,145 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
}
static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
- int is_selfpowered)
+ int is_selfpowered)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
+ unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
- dwc->is_selfpowered = !!is_selfpowered;
+ g->is_selfpowered = !!is_selfpowered;
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
+static void dwc3_stop_active_transfers(struct dwc3 *dwc)
+{
+ u32 epnum;
+
+ for (epnum = 2; epnum < dwc->num_eps; epnum++) {
+ struct dwc3_ep *dep;
+
+ dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
+
+ dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
+ }
+}
+
+static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
+{
+ enum usb_ssp_rate ssp_rate = dwc->gadget_ssp_rate;
+ u32 reg;
+
+ if (ssp_rate == USB_SSP_GEN_UNKNOWN)
+ ssp_rate = dwc->max_ssp_rate;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_SPEED_MASK;
+ reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+ if (ssp_rate == USB_SSP_GEN_1x2)
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else if (dwc->max_ssp_rate != USB_SSP_GEN_1x2)
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+
+ if (ssp_rate != USB_SSP_GEN_2x1 &&
+ dwc->max_ssp_rate != USB_SSP_GEN_2x1)
+ reg |= DWC3_DCFG_NUMLANES(1);
+
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
+{
+ enum usb_device_speed speed;
+ u32 reg;
+
+ speed = dwc->gadget_max_speed;
+ if (speed == USB_SPEED_UNKNOWN || speed > dwc->maximum_speed)
+ speed = dwc->maximum_speed;
+
+ if (speed == USB_SPEED_SUPER_PLUS &&
+ DWC3_IP_IS(DWC32)) {
+ __dwc3_gadget_set_ssp_rate(dwc);
+ return;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+ /*
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
+ !dwc->dis_metastability_quirk) {
+ reg |= DWC3_DCFG_SUPERSPEED;
+ } else {
+ switch (speed) {
+ case USB_SPEED_FULL:
+ reg |= DWC3_DCFG_FULLSPEED;
+ break;
+ case USB_SPEED_HIGH:
+ reg |= DWC3_DCFG_HIGHSPEED;
+ break;
+ case USB_SPEED_SUPER:
+ reg |= DWC3_DCFG_SUPERSPEED;
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ if (DWC3_IP_IS(DWC3))
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ break;
+ default:
+ dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+
+ if (DWC3_IP_IS(DWC3))
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ }
+ }
+
+ if (DWC3_IP_IS(DWC32) &&
+ speed > USB_SPEED_UNKNOWN &&
+ speed < USB_SPEED_SUPER_PLUS)
+ reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
- u32 reg;
- u32 timeout = 500;
+ u32 reg;
+ u32 timeout = 2000;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
- if (dwc->revision <= DWC3_REVISION_187A) {
+ if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= DWC3_DCTL_TRGTULST_RX_DET;
}
- if (dwc->revision >= DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
if (dwc->has_hibernation)
reg |= DWC3_DCTL_KEEP_CONNECT;
+ __dwc3_gadget_set_speed(dwc);
dwc->pullups_connected = true;
} else {
reg &= ~DWC3_DCTL_RUN_STOP;
@@ -1721,9 +2350,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
dwc->pullups_connected = false;
}
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
do {
+ udelay(1000);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg));
@@ -1731,54 +2361,104 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
if (!timeout)
return -ETIMEDOUT;
- dev_dbg(dwc->dev, "gadget %s data soft-%s\n",
- dwc->gadget_driver
- ? dwc->gadget_driver->function : "no-function",
- is_on ? "connect" : "disconnect");
-
return 0;
}
-static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
+static void __dwc3_gadget_stop(struct dwc3 *dwc);
+static int __dwc3_gadget_start(struct dwc3 *dwc);
+
+static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- int ret;
- is_on = !!is_on;
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->connected = false;
/*
* Per databook, when we want to stop the gadget, if a control transfer
* is still in process, complete it and get the core into setup phase.
*/
- if (!is_on && dwc->ep0state != EP0_SETUP_PHASE)
- dev_warn(dwc->dev, "not in SETUP phase\n");
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ /*
+ * Original Linux code waits for ep0state bein in setup
+ * phase here using completions. Completions are not properly
+ * implemented in barebox, hence the code is skipped here. I wasn't
+ * able to trigger this code in barebox, but if you hit this compare
+ * it with Linux code and implement it here.
+ */
+ dev_warn(dwc->dev, "%s: unexpected state\n", __func__);
+ }
- spin_lock_irqsave(&dwc->lock, flags);
- ret = dwc3_gadget_run_stop(dwc, is_on, false);
+ /*
+ * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.8 Table 4-7, it states that for a device-initiated
+ * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
+ * command for any active transfers" before clearing the RunStop
+ * bit.
+ */
+ dwc3_stop_active_transfers(dwc);
+ __dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ /*
+ * Note: if the GEVNTCOUNT indicates events in the event buffer, the
+ * driver needs to acknowledge them before the controller can halt.
+ * Simply let the interrupt handler acknowledges and handle the
+ * remaining event generated by the controller while polling for
+ * DSTS.DEVCTLHLT.
+ */
+ return dwc3_gadget_run_stop(dwc, false, false);
+}
+
+static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ int ret;
+
+ is_on = !!is_on;
+
+ dwc->softconnect = is_on;
+
+ if (!is_on) {
+ ret = dwc3_gadget_soft_disconnect(dwc);
+ } else {
+ /*
+ * In the Synopsys DWC_usb31 1.90a programming guide section
+ * 4.1.9, it specifies that for a reconnect after a
+ * device-initiated disconnect requires a core soft reset
+ * (DCTL.CSftRst) before enabling the run/stop bit.
+ */
+ dwc3_core_soft_reset(dwc);
+
+ dwc3_event_buffers_setup(dwc);
+ __dwc3_gadget_start(dwc);
+ ret = dwc3_gadget_run_stop(dwc, true, false);
+ }
+
return ret;
}
static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
{
- u32 reg;
+ u32 reg;
/* Enable all but Start and End of Frame IRQs */
- reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
- DWC3_DEVTEN_EVNTOVERFLOWEN |
- DWC3_DEVTEN_CMDCMPLTEN |
- DWC3_DEVTEN_ERRTICERREN |
- DWC3_DEVTEN_WKUPEVTEN |
- DWC3_DEVTEN_ULSTCNGEN |
- DWC3_DEVTEN_CONNECTDONEEN |
- DWC3_DEVTEN_USBRSTEN |
- DWC3_DEVTEN_DISCONNEVTEN);
-
- if (dwc->revision < DWC3_REVISION_250A)
+ reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
+ DWC3_DEVTEN_CMDCMPLTEN |
+ DWC3_DEVTEN_ERRTICERREN |
+ DWC3_DEVTEN_WKUPEVTEN |
+ DWC3_DEVTEN_CONNECTDONEEN |
+ DWC3_DEVTEN_USBRSTEN |
+ DWC3_DEVTEN_DISCONNEVTEN);
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
reg |= DWC3_DEVTEN_ULSTCNGEN;
+ /* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
+ reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
+
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}
@@ -1817,7 +2497,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
u32 reg;
ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
- mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+ mdwidth = dwc3_mdwidth(dwc);
nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
nump = min_t(u32, nump, 16);
@@ -1831,9 +2511,20 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
static int __dwc3_gadget_start(struct dwc3 *dwc)
{
- struct dwc3_ep *dep;
- int ret = 0;
- u32 reg;
+ struct dwc3_ep *dep;
+ int ret = 0;
+ u32 reg;
+
+ /*
+ * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
+ * the core supports IMOD, disable it.
+ */
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ } else if (dwc3_has_imod(dwc)) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ }
/*
* We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP
@@ -1843,19 +2534,38 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- if (dwc3_is_usb31(dwc))
- reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
- else
+ if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc);
+ /*
+ * Currently the controller handles single stream only. So, Ignore
+ * Packet Pending bit for stream selection and don't search for another
+ * stream if the host sends Data Packet with PP=0 (for OUT direction) or
+ * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
+ * the stream performance.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_IGNSTRMPP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ /* Enable MST by default if the device is capable of MST */
+ if (DWC3_MST_CAPABLE(&dwc->hwparams)) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG1);
+ reg &= ~DWC3_DCFG1_DIS_MST_ENH;
+ dwc3_writel(dwc->regs, DWC3_DCFG1, reg);
+ }
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
+ dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
@@ -1863,6 +2573,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
}
dep = dwc->eps[1];
+ dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
@@ -1871,7 +2582,9 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
+ dwc->ep0_bounced = false;
dwc->link_state = DWC3_LINK_STATE_SS_DIS;
+ dwc->delayed_status = false;
dwc3_ep0_out_start(dwc);
dwc3_gadget_enable_irq(dwc);
@@ -1888,109 +2601,163 @@ err0:
static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
- int ret = 0;
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
- //dwc3_gadget_wakeup(g);
spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver) {
- dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
- dwc->gadget_driver->function);
- ret = -EBUSY;
- goto err1;
- }
+ dwc->gadget_driver = driver;
+ spin_unlock_irqrestore(&dwc->lock, flags);
- dwc->gadget_driver = driver;
+ return 0;
+}
- __dwc3_gadget_start(dwc);
+static void __dwc3_gadget_stop(struct dwc3 *dwc)
+{
+ dwc3_gadget_disable_irq(dwc);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+}
+static int dwc3_gadget_stop(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->gadget_driver = NULL;
+ dwc->max_cfg_eps = 0;
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
+}
-err1:
- spin_unlock_irqrestore(&dwc->lock, flags);
+static void dwc3_gadget_config_params(struct usb_gadget *g,
+ struct usb_dcd_config_params *params)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
- return ret;
+ params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED;
+ params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED;
+
+ /* Recommended BESL */
+ if (!dwc->dis_enblslpm_quirk) {
+ /*
+ * If the recommended BESL baseline is 0 or if the BESL deep is
+ * less than 2, Microsoft's Windows 10 host usb stack will issue
+ * a usb reset immediately after it receives the extended BOS
+ * descriptor and the enumeration will fail. To maintain
+ * compatibility with the Windows' usb stack, let's set the
+ * recommended BESL baseline to 1 and clamp the BESL deep to be
+ * within 2 to 15.
+ */
+ params->besl_baseline = 1;
+ if (dwc->is_utmi_l1_suspend)
+ params->besl_deep =
+ clamp_t(u8, dwc->hird_threshold, 2, 15);
+ }
+
+ /* U1 Device exit Latency */
+ if (dwc->dis_u1_entry_quirk)
+ params->bU1devExitLat = 0;
+ else
+ params->bU1devExitLat = DWC3_DEFAULT_U1_DEV_EXIT_LAT;
+
+ /* U2 Device exit Latency */
+ if (dwc->dis_u2_entry_quirk)
+ params->bU2DevExitLat = 0;
+ else
+ params->bU2DevExitLat =
+ cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT);
}
-static int dwc3_gadget_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static void dwc3_gadget_set_speed(struct usb_gadget *g,
+ enum usb_device_speed speed)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
+ dwc->gadget_max_speed = speed;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
- dwc3_gadget_disable_irq(dwc);
- __dwc3_gadget_ep_disable(dwc->eps[0]);
- __dwc3_gadget_ep_disable(dwc->eps[1]);
+static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
+ enum usb_ssp_rate rate)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
- dwc->gadget_driver = NULL;
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->gadget_max_speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget_ssp_rate = rate;
spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
+static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ if (dwc->usb2_phy)
+ return usb_phy_set_power(dwc->usb2_phy, mA);
+
+ if (!dwc->usb_psy)
+ return -EOPNOTSUPP;
return 0;
}
-static void dwc3_gadget_set_speed(struct dwc3 *dwc,
- enum usb_device_speed speed)
+/**
+ * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration
+ * @g: pointer to the USB gadget
+ *
+ * Used to record the maximum number of endpoints being used in a USB composite
+ * device. (across all configurations) This is to be used in the calculation
+ * of the TXFIFO sizes when resizing internal memory for individual endpoints.
+ * It will help ensured that the resizing logic reserves enough space for at
+ * least one max packet.
+ */
+static int dwc3_gadget_check_config(struct usb_gadget *g)
{
- unsigned long flags;
- u32 reg;
-
- spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
- reg &= ~(DWC3_DCFG_SPEED_MASK);
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct usb_ep *ep;
+ int fifo_size = 0;
+ int ram1_depth;
+ int ep_num = 0;
- /*
- * WORKAROUND: DWC3 revision < 2.20a have an issue
- * which would cause metastability state on Run/Stop
- * bit if we try to force the IP to USB2-only mode.
- *
- * Because of that, we cannot configure the IP to any
- * speed other than the SuperSpeed
- *
- * Refers to:
- *
- * STAR#9000525659: Clock Domain Crossing on DCTL in
- * USB 2.0 Mode
- */
- if (dwc->revision < DWC3_REVISION_220A &&
- !dwc->dis_metastability_quirk) {
- reg |= DWC3_DCFG_SUPERSPEED;
- } else {
- switch (speed) {
- case USB_SPEED_LOW:
- reg |= DWC3_DCFG_LOWSPEED;
- break;
- case USB_SPEED_FULL:
- reg |= DWC3_DCFG_FULLSPEED;
- break;
- case USB_SPEED_HIGH:
- reg |= DWC3_DCFG_HIGHSPEED;
- break;
- case USB_SPEED_SUPER:
- reg |= DWC3_DCFG_SUPERSPEED;
- break;
- case USB_SPEED_SUPER_PLUS:
- if (dwc3_is_usb31(dwc))
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- else
- reg |= DWC3_DCFG_SUPERSPEED;
- break;
- default:
- dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+ if (!dwc->do_fifo_resize)
+ return 0;
- if (dwc->revision & DWC3_REVISION_IS_DWC31)
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- else
- reg |= DWC3_DCFG_SUPERSPEED;
- }
+ list_for_each_entry(ep, &g->ep_list, ep_list) {
+ /* Only interested in the IN endpoints */
+ if (ep->claimed && (ep->address & USB_DIR_IN))
+ ep_num++;
}
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ if (ep_num <= dwc->max_cfg_eps)
+ return 0;
+
+ /* Update the max number of eps in the composition */
+ dwc->max_cfg_eps = ep_num;
+
+ fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps);
+ /* Based on the equation, increment by one for every ep */
+ fifo_size += dwc->max_cfg_eps;
+
+ /* Check if we can fit a single fifo per endpoint */
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+ if (fifo_size > ram1_depth)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->async_callbacks = enable;
spin_unlock_irqrestore(&dwc->lock, flags);
}
@@ -1998,12 +2765,18 @@ static void dwc3_gadget_poll(struct usb_gadget *g);
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
- .wakeup = dwc3_gadget_wakeup,
+ .wakeup = dwc3_gadget_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
- .pullup = dwc3_gadget_pullup,
+ .pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
.udc_poll = dwc3_gadget_poll,
+ .udc_set_speed = dwc3_gadget_set_speed,
+ .udc_set_ssp_rate = dwc3_gadget_set_ssp_rate,
+ .get_config_params = dwc3_gadget_config_params,
+ .vbus_draw = dwc3_gadget_vbus_draw,
+ .check_config = dwc3_gadget_check_config,
+ .udc_async_callbacks = dwc3_gadget_async_callbacks,
};
/* -------------------------------------------------------------------------- */
@@ -2016,7 +2789,9 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!dep->direction)
- dwc->gadget.ep0 = &dep->endpoint;
+ dwc->gadget->ep0 = &dep->endpoint;
+
+ dep->endpoint.caps.type_control = true;
return 0;
}
@@ -2024,41 +2799,48 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
- int mdwidth;
- int kbytes;
+ u32 mdwidth;
int size;
+ int maxpacket;
+
+ mdwidth = dwc3_mdwidth(dwc);
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ if (DWC3_IP_IS(DWC3))
+ size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
-
- /* FIFO Depth is in MDWDITH bytes. Multiply */
- size *= mdwidth;
-
- kbytes = size / 1024;
- if (kbytes == 0)
- kbytes = 1;
+ size = DWC31_GTXFIFOSIZ_TXFDEP(size);
/*
- * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
- * internal overhead. We don't really know how these are used,
- * but documentation say it exists.
+ * maxpacket size is determined as part of the following, after assuming
+ * a mult value of one maxpacket:
+ * DWC3 revision 280A and prior:
+ * fifo_size = mult * (max_packet / mdwidth) + 1;
+ * maxpacket = mdwidth * (fifo_size - 1);
+ *
+ * DWC3 revision 290A and onwards:
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ * maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth;
*/
- size -= mdwidth * (kbytes + 1);
- size /= kbytes;
+ if (DWC3_VER_IS_PRIOR(DWC3, 290A))
+ maxpacket = mdwidth * (size - 1);
+ else
+ maxpacket = mdwidth * ((size - 1) - 1) - mdwidth;
+ /* Functionally, space for one max packet is sufficient */
+ size = min_t(int, maxpacket, 1024);
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- dep->endpoint.max_streams = 15;
+ dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc->gadget->ep_list);
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
return dwc3_alloc_trb_pool(dep);
}
@@ -2066,22 +2848,56 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
+ u32 mdwidth;
+ int size;
+
+ mdwidth = dwc3_mdwidth(dwc);
+
+ /* MDWIDTH is represented in bits, convert to bytes */
+ mdwidth /= 8;
+
+ /* All OUT endpoints share a single RxFIFO space */
+ size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
+ if (DWC3_IP_IS(DWC3))
+ size = DWC3_GRXFIFOSIZ_RXFDEP(size);
+ else
+ size = DWC31_GRXFIFOSIZ_RXFDEP(size);
+
+ /* FIFO depth is in MDWDITH bytes */
+ size *= mdwidth;
+
+ /*
+ * To meet performance requirement, a minimum recommended RxFIFO size
+ * is defined as follow:
+ * RxFIFO size >= (3 x MaxPacketSize) +
+ * (3 x 8 bytes setup packets size) + (16 bytes clock crossing margin)
+ *
+ * Then calculate the max packet limit as below.
+ */
+ size -= (3 * 8) + 16;
+ if (size < 0)
+ size = 0;
+ else
+ size /= 3;
- usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
- dep->endpoint.max_streams = 15;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+ dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc->gadget->ep_list);
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
return dwc3_alloc_trb_pool(dep);
}
static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
{
- struct dwc3_ep *dep;
- bool direction = epnum & 1;
- int ret;
- u8 num = epnum >> 1;
+ struct dwc3_ep *dep;
+ bool direction = epnum & 1;
+ int ret;
+ u8 num = epnum >> 1;
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (!dep)
@@ -2096,7 +2912,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
dep->start_cmd_status = 0;
snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
- direction ? "in" : "out");
+ direction ? "in" : "out");
dep->endpoint.name = dep->name;
@@ -2105,8 +2921,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
dep->endpoint.comp_desc = NULL;
}
- spin_lock_init(&dep->lock);
-
if (num == 0)
ret = dwc3_gadget_init_control_endpoint(dep);
else if (direction)
@@ -2117,21 +2931,26 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
if (ret)
return ret;
+ dep->endpoint.caps.dir_in = direction;
+ dep->endpoint.caps.dir_out = !direction;
+
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
INIT_LIST_HEAD(&dep->cancelled_list);
+ dwc3_debugfs_create_endpoint_dir(dep);
+
return 0;
}
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
{
- u8 epnum;
+ u8 epnum;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ INIT_LIST_HEAD(&dwc->gadget->ep_list);
for (epnum = 0; epnum < total; epnum++) {
- int ret;
+ int ret;
ret = dwc3_gadget_init_endpoint(dwc, epnum);
if (ret)
@@ -2143,8 +2962,8 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
{
- struct dwc3_ep *dep;
- u8 epnum;
+ struct dwc3_ep *dep;
+ u8 epnum;
for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
dep = dwc->eps[epnum];
@@ -2164,6 +2983,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
list_del(&dep->endpoint.ep_list);
}
+ dwc3_debugfs_remove_endpoint_dir(dep);
kfree(dep);
}
}
@@ -2171,12 +2991,10 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
/* -------------------------------------------------------------------------- */
static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
- struct dwc3_request *req,
- struct dwc3_trb *trb,
- const struct dwc3_event_depevt *event,
- int status, int chain)
+ struct dwc3_request *req, struct dwc3_trb *trb,
+ const struct dwc3_event_depevt *event, int status, int chain)
{
- unsigned int count;
+ unsigned int count;
dwc3_ep_inc_deq(dep);
@@ -2205,15 +3023,16 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl);
frame_number &= ~(dep->interval - 1);
+ req->request.frame_number = frame_number;
}
/*
- * If we're dealing with unaligned size OUT transfer, we will be left
- * with one TRB pending in the ring. We need to manually clear HWO bit
- * from that TRB.
+ * We use bounce buffer for requests that needs extra TRB or OUT ZLP. If
+ * this TRB points to the bounce buffer address, it's a MPS alignment
+ * TRB. Don't add it to req->remaining calculation.
*/
-
- if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
+ if (trb->bpl == lower_32_bits(dep->dwc->bounce_addr) &&
+ trb->bph == upper_32_bits(dep->dwc->bounce_addr)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2227,16 +3046,20 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
if (event->status & DEPEVT_STATUS_SHORT && !chain)
return 1;
- if (event->status & DEPEVT_STATUS_IOC)
+ if ((trb->ctrl & DWC3_TRB_CTRL_ISP_IMI) &&
+ DWC3_TRB_SIZE_TRBSTS(trb->size) == DWC3_TRBSTS_MISSED_ISOC)
+ return 1;
+
+ if ((trb->ctrl & DWC3_TRB_CTRL_IOC) ||
+ (trb->ctrl & DWC3_TRB_CTRL_LST))
return 1;
return 0;
}
static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
- struct dwc3_request *req,
- const struct dwc3_event_depevt *event,
- int status)
+ struct dwc3_request *req, const struct dwc3_event_depevt *event,
+ int status)
{
struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
@@ -2244,15 +3067,24 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
event, status, false);
}
+static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
+{
+ return req->num_pending_sgs == 0 && req->num_queued_sgs == 0;
+}
+
static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event,
- struct dwc3_request *req,
- int status)
+ const struct dwc3_event_depevt *event,
+ struct dwc3_request *req, int status)
{
+ int request_status;
int ret;
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
- status);
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);
+
+ req->request.actual = req->request.length - req->remaining;
+
+ if (!dwc3_gadget_ep_request_completed(req))
+ goto out;
if (req->needs_extra_trb) {
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
@@ -2260,69 +3092,121 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->needs_extra_trb = false;
}
- req->request.actual = req->request.length - req->remaining;
+ /*
+ * The event status only reflects the status of the TRB with IOC set.
+ * For the requests that don't set interrupt on completion, the driver
+ * needs to check and return the status of the completed TRBs associated
+ * with the request. Use the status of the last TRB of the request.
+ */
+ if (req->request.no_interrupt) {
+ struct dwc3_trb *trb;
- dwc3_gadget_giveback(dep, req, status);
+ trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue);
+ switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) {
+ case DWC3_TRBSTS_MISSED_ISOC:
+ /* Isoc endpoint only */
+ request_status = -EXDEV;
+ break;
+ case DWC3_TRB_STS_XFER_IN_PROG:
+ /* Applicable when End Transfer with ForceRM=0 */
+ case DWC3_TRBSTS_SETUP_PENDING:
+ /* Control endpoint only */
+ case DWC3_TRBSTS_OK:
+ default:
+ request_status = 0;
+ break;
+ }
+ } else {
+ request_status = status;
+ }
+
+ dwc3_gadget_giveback(dep, req, request_status);
+out:
return ret;
}
static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event,
- int status)
+ const struct dwc3_event_depevt *event, int status)
{
- struct dwc3_request *req;
- struct dwc3_request *tmp;
+ struct dwc3_request *req;
- list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+ while (!list_empty(&dep->started_list)) {
int ret;
+ req = next_request(&dep->started_list);
ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
req, status);
if (ret)
break;
+ /*
+ * The endpoint is disabled, let the dwc3_remove_requests()
+ * handle the cleanup.
+ */
+ if (!dep->endpoint.desc)
+ break;
}
}
+static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct dwc3 *dwc = dep->dwc;
+
+ if (!dep->endpoint.desc || !dwc->pullups_connected ||
+ !dwc->connected)
+ return false;
+
+ if (!list_empty(&dep->pending_list))
+ return true;
+
+ /*
+ * We only need to check the first entry of the started list. We can
+ * assume the completed requests are removed from the started list.
+ */
+ req = next_request(&dep->started_list);
+ if (!req)
+ return false;
+
+ return !dwc3_gadget_ep_request_completed(req);
+}
+
static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
dep->frame_number = event->parameters;
}
-static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
+static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
{
- struct dwc3 *dwc = dep->dwc;
- unsigned status = 0;
- bool stop = false;
-
- dwc3_gadget_endpoint_frame_from_event(dep, event);
-
- if (event->status & DEPEVT_STATUS_BUSERR)
- status = -ECONNRESET;
+ struct dwc3 *dwc = dep->dwc;
+ bool no_started_trb = true;
- if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
- status = -EXDEV;
+ dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
- if (list_empty(&dep->started_list))
- stop = true;
- }
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+ goto out;
- dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
+ if (!dep->endpoint.desc)
+ return no_started_trb;
- if (stop) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ list_empty(&dep->started_list) &&
+ (list_empty(&dep->pending_list) || status == -EXDEV))
dwc3_stop_active_transfer(dep, true, true);
- dep->flags = DWC3_EP_ENABLED;
- }
+ else if (dwc3_gadget_ep_should_continue(dep))
+ if (__dwc3_gadget_kick_transfer(dep) == 0)
+ no_started_trb = false;
+out:
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
*/
- if (dwc->revision < DWC3_REVISION_183A) {
- u32 reg;
- int i;
+ if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
+ u32 reg;
+ int i;
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
dep = dwc->eps[i];
@@ -2331,7 +3215,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
continue;
if (!list_empty(&dep->started_list))
- return;
+ return no_started_trb;
}
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -2340,30 +3224,188 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
dwc->u1u2 = 0;
}
+
+ return no_started_trb;
+}
+
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ int status = 0;
+
+ if (!dep->endpoint.desc)
+ return;
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (event->status & DEPEVT_STATUS_MISSED_ISOC)
+ status = -EXDEV;
+
+ dwc3_gadget_endpoint_trbs_complete(dep, event, status);
+}
+
+static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ int status = 0;
+
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
+ dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
}
static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ /*
+ * The XferNotReady event is generated only once before the endpoint
+ * starts. It will be generated again when END_TRANSFER command is
+ * issued. For some controller versions, the XferNotReady event may be
+ * generated while the END_TRANSFER command is still in process. Ignore
+ * it and wait for the next XferNotReady event after the command is
+ * completed.
+ */
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+ return;
+
(void) __dwc3_gadget_start_isoc(dep);
}
+static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ u8 cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd != DWC3_DEPCMD_ENDTRANSFER)
+ return;
+
+ /*
+ * The END_TRANSFER command will cause the controller to generate a
+ * NoStream Event, and it's not due to the host DP NoStream rejection.
+ * Ignore the next NoStream event.
+ */
+ if (dep->stream_capable)
+ dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
+ if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
+ struct dwc3 *dwc = dep->dwc;
+
+ dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
+ if (dwc3_send_clear_stall_ep_cmd(dep)) {
+ struct usb_ep *ep0 = &dwc->eps[0]->endpoint;
+
+ dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name);
+ if (dwc->delayed_status)
+ __dwc3_gadget_ep0_set_halt(ep0, 1);
+ return;
+ }
+
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ if (dwc->clear_stall_protocol == dep->number)
+ dwc3_ep0_send_delayed_status(dwc);
+ }
+
+ if ((dep->flags & DWC3_EP_DELAY_START) &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
+
+ dep->flags &= ~DWC3_EP_DELAY_START;
+}
+
+static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ if (event->status == DEPEVT_STREAMEVT_FOUND) {
+ dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+ goto out;
+ }
+
+ /* Note: NoStream rejection event param value is 0 and not 0xFFFF */
+ switch (event->parameters) {
+ case DEPEVT_STREAM_PRIME:
+ /*
+ * If the host can properly transition the endpoint state from
+ * idle to prime after a NoStream rejection, there's no need to
+ * force restarting the endpoint to reinitiate the stream. To
+ * simplify the check, assume the host follows the USB spec if
+ * it primed the endpoint more than once.
+ */
+ if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
+ if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
+ dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
+ else
+ dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+ }
+
+ break;
+ case DEPEVT_STREAM_NOSTREAM:
+ if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
+ !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
+ (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
+ !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
+ break;
+
+ /*
+ * If the host rejects a stream due to no active stream, by the
+ * USB and xHCI spec, the endpoint will be put back to idle
+ * state. When the host is ready (buffer added/updated), it will
+ * prime the endpoint to inform the usb device controller. This
+ * triggers the device controller to issue ERDY to restart the
+ * stream. However, some hosts don't follow this and keep the
+ * endpoint in the idle state. No prime will come despite host
+ * streams are updated, and the device controller will not be
+ * triggered to generate ERDY to move the next stream data. To
+ * workaround this and maintain compatibility with various
+ * hosts, force to reinitiate the stream until the host is ready
+ * instead of waiting for the host to prime the endpoint.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
+ unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
+
+ dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
+ } else {
+ dep->flags |= DWC3_EP_DELAY_START;
+ dwc3_stop_active_transfer(dep, true, true);
+ return;
+ }
+ break;
+ }
+
+out:
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+}
+
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+ const struct dwc3_event_depevt *event)
{
- struct dwc3_ep *dep;
- u8 epnum = event->endpoint_number;
- u8 cmd;
+ struct dwc3_ep *dep;
+ u8 epnum = event->endpoint_number;
dep = dwc->eps[epnum];
if (!(dep->flags & DWC3_EP_ENABLED)) {
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
return;
/* Handle only EPCMDCMPLT when EP disabled */
- if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
+ if ((event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) &&
+ !(epnum <= 1 && event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE))
return;
}
@@ -2380,15 +3422,14 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dwc3_gadget_endpoint_transfer_not_ready(dep, event);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- cmd = DEPEVT_PARAMETER_CMD(event->parameters);
-
- if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
- dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_cleanup_cancelled_requests(dep);
- }
+ dwc3_gadget_endpoint_command_complete(dep, event);
break;
- case DWC3_DEPEVT_STREAMEVT:
case DWC3_DEPEVT_XFERCOMPLETE:
+ dwc3_gadget_endpoint_transfer_complete(dep, event);
+ break;
+ case DWC3_DEPEVT_STREAMEVT:
+ dwc3_gadget_endpoint_stream_event(dep, event);
+ break;
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
}
@@ -2396,27 +3437,27 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
- if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
+ if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(&dwc->gadget);
+ dwc->gadget_driver->disconnect(dwc->gadget);
spin_lock(&dwc->lock);
}
}
static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
- if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+ if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->suspend(&dwc->gadget);
+ dwc->gadget_driver->suspend(dwc->gadget);
spin_lock(&dwc->lock);
}
}
static void dwc3_resume_gadget(struct dwc3 *dwc)
{
- if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+ if (dwc->async_callbacks && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -2426,53 +3467,74 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
if (!dwc->gadget_driver)
return;
- if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (dwc->async_callbacks && dwc->gadget->speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
- usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
+ usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
- bool interrupt)
+void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt)
{
struct dwc3 *dwc = dep->dwc;
- struct dwc3_gadget_ep_cmd_params params;
- u32 cmd;
- int ret;
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ /*
+ * Only issue End Transfer command to the control endpoint of a started
+ * Data Phase. Typically we should only do so in error cases such as
+ * invalid/unexpected direction as described in the control transfer
+ * flow of the programming guide.
+ */
+ if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
+ return;
+
+ if (interrupt && (dep->flags & DWC3_EP_DELAY_STOP))
+ return;
+
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
+ (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
/*
+ * If a Setup packet is received but yet to DMA out, the controller will
+ * not process the End Transfer command of any endpoint. Polling of its
+ * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+ * timeout. Delay issuing the End Transfer command until the Setup TRB is
+ * prepared.
+ */
+ if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
+ dep->flags |= DWC3_EP_DELAY_STOP;
+ return;
+ }
+
+ /*
* NOTICE: We are violating what the Databook says about the
* EndTransfer command. Ideally we would _always_ wait for the
* EndTransfer Command Completion IRQ, but that's causing too
* much trouble synchronizing between us and gadget driver.
*
* We have discussed this with the IP Provider and it was
- * suggested to giveback all requests here, but give HW some
- * extra time to synchronize with the interconnect. We're using
- * an arbitraty 100us delay for that.
+ * suggested to giveback all requests here.
*
* Note also that a similar handling was tested by Synopsys
* (thanks a lot Paul) and nothing bad has come out of it.
- * In short, what we're doing is:
+ * In short, what we're doing is issuing EndTransfer with
+ * CMDIOC bit set and delay kicking transfer until the
+ * EndTransfer command had completed.
+ *
+ * As of IP version 3.10a of the DWC_usb3 IP, the controller
+ * supports a mode to work around the above limitation. The
+ * software can poll the CMDACT bit in the DEPCMD register
+ * after issuing a EndTransfer command. This mode is enabled
+ * by writing GUCTL2[14]. This polling is already done in the
+ * dwc3_send_gadget_ep_cmd() function so if the mode is
+ * enabled, the EndTransfer command will have completed upon
+ * returning from this function.
*
- * - Issue EndTransfer WITH CMDIOC bit set
- * - Wait 100us
+ * This mode is NOT available on the DWC_usb31 IP.
*/
- cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
- cmd |= DWC3_DEPCMD_CMDIOC;
- cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
- memset(&params, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
- dep->resource_index = 0;
-
- if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
- udelay(100);
+ __dwc3_stop_active_transfer(dep, force, interrupt);
}
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
@@ -2481,6 +3543,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
struct dwc3_ep *dep;
+ int ret;
dep = dwc->eps[epnum];
if (!dep)
@@ -2491,35 +3554,54 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
dep->flags &= ~DWC3_EP_STALL;
- dwc3_send_clear_stall_ep_cmd(dep);
+ ret = dwc3_send_clear_stall_ep_cmd(dep);
+ WARN_ON_ONCE(ret);
}
}
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
- int reg;
+ int reg;
+
+ dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
+
+ dwc->connected = false;
dwc3_disconnect_gadget(dwc);
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
- dwc->connected = false;
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ unsigned int dir;
+
+ dir = !!dwc->ep0_expect_in;
+ if (dwc->ep0state == EP0_DATA_PHASE)
+ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+ else
+ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+ dwc3_ep0_stall_and_restart(dwc);
+ }
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
- u32 reg;
+ u32 reg;
- dwc->connected = true;
+ /*
+ * Ideally, dwc3_reset_gadget() would trigger the function
+ * drivers to stop any active transfers through ep disable.
+ * However, for functions which defer ep disable, such as mass
+ * storage, we will need to rely on the call to stop active
+ * transfers here, and avoid allowing of request queuing.
+ */
+ dwc->connected = false;
/*
* WORKAROUND: DWC3 revisions <1.88a have an issue which
@@ -2547,16 +3629,45 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
* STAR#9000466709: RTL: Device : Disconnect event not
* generated if setup packet pending in FIFO
*/
- if (dwc->revision < DWC3_REVISION_188A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 188A)) {
if (dwc->setup_packet_pending)
dwc3_gadget_disconnect_interrupt(dwc);
}
dwc3_reset_gadget(dwc);
+ /*
+ * From SNPS databook section 8.1.2, the EP0 should be in setup
+ * phase. So ensure that EP0 is in setup phase by issuing a stall
+ * and restart if EP0 is not in setup phase.
+ */
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ unsigned int dir;
+
+ dir = !!dwc->ep0_expect_in;
+ if (dwc->ep0state == EP0_DATA_PHASE)
+ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+ else
+ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+
+ dwc->eps[0]->trb_enqueue = 0;
+ dwc->eps[1]->trb_enqueue = 0;
+
+ dwc3_ep0_stall_and_restart(dwc);
+ }
+
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
+ * needs to ensure that it sends "a DEPENDXFER command for any active
+ * transfers."
+ */
+ dwc3_stop_active_transfers(dwc);
+ dwc->connected = true;
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
dwc3_clear_stall_all_ep(dwc);
@@ -2568,22 +3679,45 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
{
- struct dwc3_ep *dep;
- int ret;
- u32 reg;
- u8 speed;
+ struct dwc3_ep *dep;
+ int ret;
+ u32 reg;
+ u8 lanes = 1;
+ u8 speed;
+
+ if (!dwc->softconnect)
+ return;
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
+ if (DWC3_IP_IS(DWC32))
+ lanes = DWC3_DSTS_CONNLANES(reg) + 1;
+
+ dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
+
+ /*
+ * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
+ * each time on Connect Done.
+ *
+ * Currently we always use the reset value. If any platform
+ * wants to set this to a different value, we need to add a
+ * setting and update GCTL.RAMCLKSEL here.
+ */
+
switch (speed) {
case DWC3_DSTS_SUPERSPEED_PLUS:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget->ep0->maxpacket = 512;
+ dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+
+ if (lanes > 1)
+ dwc->gadget->ssp_rate = USB_SSP_GEN_2x2;
+ else
+ dwc->gadget->ssp_rate = USB_SSP_GEN_2x1;
break;
- case DWC3_DCFG_SUPERSPEED:
+ case DWC3_DSTS_SUPERSPEED:
/*
* WORKAROUND: DWC3 revisions <1.90a have an issue which
* would cause a missing USB3 Reset event.
@@ -2597,36 +3731,36 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* STAR#9000483510: RTL: SS : USB3 reset event may
* not be generated always when the link enters poll
*/
- if (dwc->revision < DWC3_REVISION_190A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 190A))
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER;
+ dwc->gadget->ep0->maxpacket = 512;
+ dwc->gadget->speed = USB_SPEED_SUPER;
+
+ if (lanes > 1) {
+ dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget->ssp_rate = USB_SSP_GEN_1x2;
+ }
break;
- case DWC3_DCFG_HIGHSPEED:
+ case DWC3_DSTS_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_HIGH;
+ dwc->gadget->ep0->maxpacket = 64;
+ dwc->gadget->speed = USB_SPEED_HIGH;
break;
- case DWC3_DCFG_FULLSPEED:
- case DWC3_DCFG_FULLSPEED1:
+ case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_FULL;
- break;
- case DWC3_DCFG_LOWSPEED:
- dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
- dwc->gadget.ep0->maxpacket = 8;
- dwc->gadget.speed = USB_SPEED_LOW;
+ dwc->gadget->ep0->maxpacket = 64;
+ dwc->gadget->speed = USB_SPEED_FULL;
break;
}
- dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket;
/* Enable USB2 LPM Capability */
- if ((dwc->revision > DWC3_REVISION_194A) &&
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
+ !dwc->usb2_gadget_lpm_disable &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -2636,7 +3770,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
- reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
+ reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
+ (dwc->is_utmi_l1_suspend << 4));
/*
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
@@ -2644,17 +3779,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* BESL value in the LPM token is less than or equal to LPM
* NYET threshold.
*/
- if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum)
- WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+ WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
+ "LPM Erratum not available on dwc3 revisions < 2.40a\n");
- if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
- reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
+ if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
+ reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
+ if (dwc->usb2_gadget_lpm_disable) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
}
dep = dwc->eps[0];
@@ -2687,15 +3828,18 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
* implemented.
*/
- if (dwc->gadget_driver && dwc->gadget_driver->resume)
- dwc->gadget_driver->resume(&dwc->gadget);
+ if (dwc->async_callbacks && dwc->gadget_driver->resume) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->resume(dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
}
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
- unsigned int evtinfo)
+ unsigned int evtinfo)
{
- enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
- unsigned int pwropt;
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+ unsigned int pwropt;
/*
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
@@ -2715,11 +3859,10 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
* operational mode
*/
pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
- if ((dwc->revision < DWC3_REVISION_250A) &&
- (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A) &&
+ (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
- (next == DWC3_LINK_STATE_RESUME)) {
- dev_dbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+ (next == DWC3_LINK_STATE_RESUME)) {
return;
}
}
@@ -2742,10 +3885,10 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
* core send LGO_Ux entering U0
*/
- if (dwc->revision < DWC3_REVISION_183A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
if (next == DWC3_LINK_STATE_U0) {
- u32 u1u2;
- u32 reg;
+ u32 u1u2;
+ u32 reg;
switch (dwc->link_state) {
case DWC3_LINK_STATE_U1:
@@ -2761,7 +3904,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
reg &= ~u1u2;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
break;
default:
/* do nothing */
@@ -2777,7 +3920,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
break;
case DWC3_LINK_STATE_U2:
case DWC3_LINK_STATE_U3:
- //dwc3_suspend_gadget(dwc);
+ dwc3_suspend_gadget(dwc);
break;
case DWC3_LINK_STATE_RESUME:
dwc3_resume_gadget(dwc);
@@ -2802,12 +3945,12 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
}
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
- unsigned int evtinfo)
+ unsigned int evtinfo)
{
- unsigned int is_ss = evtinfo & (1UL << 4);
+ unsigned int is_ss = evtinfo & BIT(4);
- /**
- * WORKAROUND: DWC3 revison 2.20a with hibernation support
+ /*
+ * WORKAROUND: DWC3 revision 2.20a with hibernation support
* have a known issue which can cause USB CV TD.9.23 to fail
* randomly.
*
@@ -2826,9 +3969,8 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
}
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
- const struct dwc3_event_devt *event)
+ const struct dwc3_event_devt *event)
{
-
switch (event->type) {
case DWC3_DEVICE_EVENT_DISCONNECT:
dwc3_gadget_disconnect_interrupt(dwc);
@@ -2843,96 +3985,154 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_wakeup_interrupt(dwc);
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
- if (!dwc->has_hibernation) {
- WARN(1 ,"unexpected hibernation event\n");
+ if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
+ "unexpected hibernation event\n"))
break;
- }
+
dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
- case DWC3_DEVICE_EVENT_EOPF:
- dev_dbg(dwc->dev, "End of Periodic Frame\n");
+ case DWC3_DEVICE_EVENT_SUSPEND:
/* It changed to be suspend event for version 2.30a and above */
- if (dwc->revision >= DWC3_REVISION_230A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
/*
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
*/
- if (dwc->gadget.state >= USB_STATE_CONFIGURED)
+ if (dwc->gadget->state >= USB_STATE_CONFIGURED)
dwc3_gadget_suspend_interrupt(dwc,
event->event_info);
}
break;
case DWC3_DEVICE_EVENT_SOF:
- dev_dbg(dwc->dev, "Start of Periodic Frame\n");
- break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- dev_dbg(dwc->dev, "Erratic Error\n");
- break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- dev_dbg(dwc->dev, "Command Complete\n");
- break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- dev_dbg(dwc->dev, "Overflow\n");
break;
default:
- dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ dev_warn(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
}
}
static void dwc3_process_event_entry(struct dwc3 *dwc,
- const union dwc3_event *event)
+ const union dwc3_event *event)
{
if (!event->type.is_devspec)
dwc3_endpoint_interrupt(dwc, &event->depevt);
else if (event->type.type == DWC3_EVENT_TYPE_DEV)
dwc3_gadget_interrupt(dwc, &event->devt);
+ else
+ dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw);
}
-static void dwc3_gadget_poll(struct usb_gadget * g)
+static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- struct dwc3_event_buffer *evt = dwc->ev_buf;
+ struct dwc3 *dwc = evt->dwc;
+ irqreturn_t ret = IRQ_NONE;
+ int left;
+
+ left = evt->count;
+
+ if (!(evt->flags & DWC3_EVENT_PENDING))
+ return IRQ_NONE;
+
+ while (left > 0) {
+ union dwc3_event event;
+
+ event.raw = *(u32 *) (evt->cache + evt->lpos);
+
+ dwc3_process_event_entry(dwc, &event);
+
+ /*
+ * FIXME we wrap around correctly to the next entry as
+ * almost all entries are 4 bytes in size. There is one
+ * entry which has 12 bytes which is a regular entry
+ * followed by 8 bytes data. ATM I don't know how
+ * things are organized if we get next to the a
+ * boundary so I worry about that once we try to handle
+ * that.
+ */
+ evt->lpos = (evt->lpos + 4) % evt->length;
+ left -= 4;
+ }
+
+ evt->count = 0;
+ ret = IRQ_HANDLED;
+
+ /* Unmask interrupt */
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
+
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ }
+
+ /* Keep the clearing of DWC3_EVENT_PENDING at the end */
+ evt->flags &= ~DWC3_EVENT_PENDING;
+
+ return ret;
+}
+
+static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
+{
+ struct dwc3 *dwc = evt->dwc;
u32 amount;
u32 count;
- void *buf;
- int pos = 0;
+
+ /*
+ * With PCIe legacy interrupt, test shows that top-half irq handler can
+ * be called again after HW interrupt deassertion. Check if bottom-half
+ * irq event handler completes before caching new event to prevent
+ * losing events.
+ */
+ if (evt->flags & DWC3_EVENT_PENDING)
+ return IRQ_HANDLED;
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
- return;
+ return IRQ_NONE;
- buf = xzalloc(count);
+ evt->count = count;
+ evt->flags |= DWC3_EVENT_PENDING;
+
+ /* Mask interrupt */
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
amount = min(count, evt->length - evt->lpos);
- memcpy_fromio(buf, evt->buf + evt->lpos, amount);
+ memcpy_fromio(evt->cache + evt->lpos, evt->buf + evt->lpos, amount);
if (amount < count)
- memcpy_fromio(buf + amount, evt->buf, count - amount);
-
- evt->lpos = (evt->lpos + count) % evt->length;
+ memcpy_fromio(evt->cache, evt->buf, count - amount);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
- while (count > 0) {
- union dwc3_event event;
+ dwc3_process_event_buf(evt);
- event.raw = *(u32 *)(buf + pos);
+ return IRQ_HANDLED;
+}
- dwc3_process_event_entry(dwc, &event);
+static void dwc3_gadget_poll(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_event_buffer *evt = dwc->ev_buf;
- count -= 4;
- pos += 4;
- }
+ dwc3_check_event_buf(evt);
+}
+
+static void dwc_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev);
- free(buf);
+ kfree(gadget);
}
/**
- * dwc3_gadget_init - Initializes gadget related registers
+ * dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
*
* Returns 0 on success otherwise negative errno.
@@ -2940,46 +4140,69 @@ static void dwc3_gadget_poll(struct usb_gadget * g)
int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
+ struct device *dev;
dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2,
- &dwc->ep0_trb_addr);
+ &dwc->ep0_trb_addr);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
- goto err1;
+ goto err0;
}
- dwc->setup_buf = xzalloc(DWC3_EP0_SETUP_SIZE);
+ dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL);
if (!dwc->setup_buf) {
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
- dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE,
- &dwc->bounce_addr);
+ dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE, &dwc->bounce_addr);
if (!dwc->bounce) {
- dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ init_completion(&dwc->ep0_in_setup);
+ dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);
+ if (!dwc->gadget) {
ret = -ENOMEM;
goto err3;
}
- dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.max_speed = USB_SPEED_SUPER;
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
- dwc->gadget.name = "dwc3-gadget";
+
+ usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
+ dev = &dwc->gadget->dev;
+ dev->platform_data = dwc;
+ dwc->gadget->ops = &dwc3_gadget_ops;
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
+ dwc->gadget->sg_supported = true;
+ dwc->gadget->name = "dwc3-gadget";
+ dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable;
/*
- * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
- * on ep out.
+ * FIXME We might be setting max_speed to <SUPER, however versions
+ * <2.20a of dwc3 have an issue with metastability (documented
+ * elsewhere in this driver) which tells us we can't set max speed to
+ * anything lower than SUPER.
+ *
+ * Because gadget.max_speed is only used by composite.c and function
+ * drivers (i.e. it won't go into dwc3's registers) we are allowing this
+ * to happen so we avoid sending SuperSpeed Capability descriptor
+ * together with our BOS descriptor as that could confuse host into
+ * thinking we can handle super speed.
+ *
+ * Note that, in fact, we won't even support GetBOS requests when speed
+ * is less than super speed because we don't have means, yet, to tell
+ * composite.c that we are USB 2.0 + LPM ECN.
*/
- dwc->gadget.quirk_ep_out_aligned_size = true;
-
- if (dwc->revision < DWC3_REVISION_220A &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
- dwc->gadget.max_speed = dwc->maximum_speed;
+ dwc->gadget->max_speed = dwc->maximum_speed;
+ dwc->gadget->max_ssp_rate = dwc->max_ssp_rate;
/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -2990,26 +4213,47 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;
- ret = usb_add_gadget_udc((struct device_d *)dwc->dev, &dwc->gadget);
+ ret = usb_add_gadget(dwc->gadget);
if (ret) {
- dev_err(dwc->dev, "failed to register udc\n");
- goto err4;
+ dev_err(dwc->dev, "failed to add gadget\n");
+ goto err5;
}
- dwc3_gadget_set_speed(dwc, dwc->maximum_speed);
+ if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
+ dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate);
+ else
+ dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
return 0;
-err4:
+err5:
dwc3_gadget_free_endpoints(dwc);
+err4:
+ usb_put_gadget(dwc->gadget);
+ dwc->gadget = NULL;
err3:
- dma_free_coherent(dwc->bounce, 0, DWC3_BOUNCE_SIZE);
+ dma_free_coherent(dwc->bounce, dwc->bounce_addr, DWC3_BOUNCE_SIZE);
err2:
kfree(dwc->setup_buf);
err1:
- dma_free_coherent(dwc->ep0_trb, 0, sizeof(*dwc->ep0_trb) * 2);
+ dma_free_coherent(dwc->ep0_trb, dwc->ep0_trb_addr, sizeof(*dwc->ep0_trb) * 2);
+err0:
return ret;
}
+
+/* -------------------------------------------------------------------------- */
+
+void dwc3_gadget_exit(struct dwc3 *dwc)
+{
+ if (!dwc->gadget)
+ return;
+
+ usb_del_gadget_udc(dwc->gadget);
+ dwc3_gadget_free_endpoints(dwc);
+ dma_free_coherent(dwc->bounce, dwc->bounce_addr, DWC3_BOUNCE_SIZE);
+ kfree(dwc->setup_buf);
+ dma_free_coherent(dwc->ep0_trb, dwc->ep0_trb_addr, sizeof(*dwc->ep0_trb) * 2);
+}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 47c2d5b955..0afa10b318 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -1,70 +1,75 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/**
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported
- * to uboot.
- *
- * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending
- transfers
- *
*/
#ifndef __DRIVERS_USB_DWC3_GADGET_H
#define __DRIVERS_USB_DWC3_GADGET_H
-#include <usb/gadget.h>
+#include <linux/usb/gadget.h>
#include <linux/list.h>
#include "io.h"
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
-#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+#define gadget_to_dwc(g) (g->dev.platform_data)
/* DEPCFG parameter 1 */
-#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
-#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
-#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
-#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
-#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
-#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
-#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
-#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
-#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
-#define DWC3_DEPCFG_BULK_BASED (1 << 30)
-#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
+#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
+#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8)
+#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
+#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
+#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
+#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
+#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
+#define DWC3_DEPCFG_BULK_BASED BIT(30)
+#define DWC3_DEPCFG_FIFO_BASED BIT(31)
/* DEPCFG parameter 0 */
-#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
-#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
-#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
-#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
-#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
+#define DWC3_DEPCFG_IGN_SEQ_NUM BIT(31)
/* These apply for core versions 1.94a and later */
-#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
-#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
+#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE BIT(30)
#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
/* DEPXFERCFG parameter 0 */
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
+/* U1 Device exit Latency */
+#define DWC3_DEFAULT_U1_DEV_EXIT_LAT 0x0A /* Less then 10 microsec */
+
+/* U2 Device exit Latency */
+#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */
+
+/* Frame/Microframe Number Mask */
+#define DWC3_FRNUMBER_MASK 0x3fff
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
+/**
+ * next_request - gets the next request on the given list
+ * @list: the request list to operate on
+ *
+ * Caller should take care of locking. This function return %NULL or the first
+ * request available on @list.
+ */
static inline struct dwc3_request *next_request(struct list_head *list)
{
- if (list_empty(list))
- return NULL;
-
- return list_first_entry(list, struct dwc3_request, list);
+ return list_first_entry_or_null(list, struct dwc3_request, list);
}
/**
@@ -76,7 +81,7 @@ static inline struct dwc3_request *next_request(struct list_head *list)
*/
static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
{
- struct dwc3_ep *dep = req->dep;
+ struct dwc3_ep *dep = req->dep;
req->status = DWC3_REQUEST_STATUS_STARTED;
list_move_tail(&req->list, &dep->started_list);
@@ -85,15 +90,17 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
/**
* dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
* @req: the request to be moved
+ * @reason: cancelled reason for the dwc3 request
*
* Caller should take care of locking. This function will move @req from its
* current list to the endpoint's cancelled_list.
*/
-static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
+static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req,
+ unsigned int reason)
{
- struct dwc3_ep *dep = req->dep;
+ struct dwc3_ep *dep = req->dep;
- req->status = DWC3_REQUEST_STATUS_CANCELLED;
+ req->status = reason;
list_move_tail(&req->list, &dep->cancelled_list);
}
@@ -103,11 +110,14 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
-void dwc3_gadget_handle_interrupt(struct dwc3 *dwc);
+void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
+void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
@@ -118,10 +128,24 @@ void dwc3_gadget_handle_interrupt(struct dwc3 *dwc);
*/
static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
- u32 res_id;
+ u32 res_id;
res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
+/**
+ * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change
+ * @dwc: pointer to our context structure
+ * @value: value to write to DCTL
+ *
+ * Use this function when doing read-modify-write to DCTL. It will not
+ * send link state change request.
+ */
+static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
+{
+ value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, value);
+}
+
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 0c5e1527b5..281d016a86 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -16,7 +16,7 @@
int dwc3_host_init(struct dwc3 *dwc)
{
struct resource *io;
- struct device_d *dev = dwc->dev;
+ struct device *dev = dwc->dev;
io = dev_get_resource(dev, IORESOURCE_MEM, 0);
if (IS_ERR(io)) {
@@ -24,13 +24,17 @@ int dwc3_host_init(struct dwc3 *dwc)
return PTR_ERR(io);
}
- dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL,
- io->start, resource_size(io),
- IORESOURCE_MEM, NULL);
+ dwc->xhci = add_child_device(dev, "xHCI", DEVICE_ID_DYNAMIC, NULL,
+ io->start, resource_size(io),
+ IORESOURCE_MEM, NULL);
if (!dwc->xhci) {
dev_err(dev, "Failed to register xHCI device\n");
return -ENODEV;
}
-
+
return 0;
}
+
+void dwc3_host_exit(struct dwc3 *dwc)
+{
+}
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 96c51768f6..517255f477 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -2,11 +2,9 @@
menuconfig USB_GADGET
select USB
select POLLER
+ select NLS
bool "USB gadget support"
-config USB_GADGET_DRIVER_ARC_PBL
- bool
-
if USB_GADGET
config USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 36d71f9b8e..f45b23f22d 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -1,11 +1,5 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.o multi.o
-obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o
-obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
-obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o
-obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o storage_common.o
-obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o
-pbl-$(CONFIG_USB_GADGET_DRIVER_ARC_PBL) += fsl_udc_pbl.o
-obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o
-obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o
+obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o functions.o config.o
+
+obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 396e7387a8..f55ae5698e 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0+
/*
* composite.c - infrastructure for Composite USB Gadgets
*
@@ -12,11 +12,31 @@
#include <dma.h>
#include <linux/err.h>
#include <linux/bitmap.h>
-#include <usb/composite.h>
+#include <linux/usb/composite.h>
+#include <linux/bitfield.h>
+#include <linux/uuid.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
-static unsigned int usb_gadget_vbus_draw_ma = 2;
+#include "u_os_desc.h"
+
+#define CONFIG_USB_GADGET_VBUS_DRAW 2 /* FIXME */
+
+/**
+ * struct usb_os_string - represents OS String to be reported by a gadget
+ * @bLength: total length of the entire descritor, always 0x12
+ * @bDescriptorType: USB_DT_STRING
+ * @qwSignature: the OS String proper
+ * @bMS_VendorCode: code used by the host for subsequent requests
+ * @bPad: not used, must be zero
+ */
+struct usb_os_string {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 qwSignature[OS_STRING_QW_SIGN_LEN];
+ __u8 bMS_VendorCode;
+ __u8 bPad;
+} __packed;
/*
* The code in this file is utility code, used to build a gadget driver
@@ -32,40 +52,89 @@ static struct usb_gadget_strings **get_containers_gs(
}
/**
- * next_ep_desc() - advance to the next EP descriptor
+ * function_descriptors() - get function descriptors for speed
+ * @f: the function
+ * @speed: the speed
+ *
+ * Returns the descriptors or NULL if not set.
+ */
+static struct usb_descriptor_header **
+function_descriptors(struct usb_function *f,
+ enum usb_device_speed speed)
+{
+ struct usb_descriptor_header **descriptors;
+
+ /*
+ * NOTE: we try to help gadget drivers which might not be setting
+ * max_speed appropriately.
+ */
+
+ switch (speed) {
+ case USB_SPEED_SUPER_PLUS:
+ descriptors = f->ssp_descriptors;
+ if (descriptors)
+ break;
+ fallthrough;
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ if (descriptors)
+ break;
+ fallthrough;
+ case USB_SPEED_HIGH:
+ descriptors = f->hs_descriptors;
+ if (descriptors)
+ break;
+ fallthrough;
+ default:
+ descriptors = f->fs_descriptors;
+ }
+
+ /*
+ * if we can't find any descriptors at all, then this gadget deserves to
+ * Oops with a NULL pointer dereference
+ */
+
+ return descriptors;
+}
+
+/**
+ * next_desc() - advance to the next desc_type descriptor
* @t: currect pointer within descriptor array
+ * @desc_type: descriptor type
*
- * Return: next EP descriptor or NULL
+ * Return: next desc_type descriptor or NULL
*
- * Iterate over @t until either EP descriptor found or
+ * Iterate over @t until either desc_type descriptor found or
* NULL (that indicates end of list) encountered
*/
static struct usb_descriptor_header**
-next_ep_desc(struct usb_descriptor_header **t)
+next_desc(struct usb_descriptor_header **t, u8 desc_type)
{
for (; *t; t++) {
- if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
+ if ((*t)->bDescriptorType == desc_type)
return t;
}
return NULL;
}
/*
- * for_each_ep_desc()- iterate over endpoint descriptors in the
- * descriptors list
- * @start: pointer within descriptor array.
- * @ep_desc: endpoint descriptor to use as the loop cursor
+ * for_each_desc() - iterate over desc_type descriptors in the
+ * descriptors list
+ * @start: pointer within descriptor array.
+ * @iter_desc: desc_type descriptor to use as the loop cursor
+ * @desc_type: wanted descriptr type
*/
-#define for_each_ep_desc(start, ep_desc) \
- for (ep_desc = next_ep_desc(start); \
- ep_desc; ep_desc = next_ep_desc(ep_desc+1))
+#define for_each_desc(start, iter_desc, desc_type) \
+ for (iter_desc = next_desc(start, desc_type); \
+ iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type))
/**
- * config_ep_by_speed() - configures the given endpoint
+ * config_ep_by_speed_and_alt() - configures the given endpoint
* according to gadget speed.
* @g: pointer to the gadget
* @f: usb function
* @_ep: the endpoint to configure
+ * @alt: alternate setting number
*
* Return: error code, 0 on success
*
@@ -78,44 +147,80 @@ next_ep_desc(struct usb_descriptor_header **t)
* Note: the supplied function should hold all the descriptors
* for supported speeds
*/
-int config_ep_by_speed(struct usb_gadget *g,
- struct usb_function *f,
- struct usb_ep *_ep)
+int config_ep_by_speed_and_alt(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep,
+ u8 alt)
{
- struct usb_composite_dev *cdev;
struct usb_endpoint_descriptor *chosen_desc = NULL;
+ struct usb_interface_descriptor *int_desc = NULL;
struct usb_descriptor_header **speed_desc = NULL;
struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
int want_comp_desc = 0;
struct usb_descriptor_header **d_spd; /* cursor for speed desc */
+ struct usb_composite_dev *cdev;
+ bool incomplete_desc = false;
if (!g || !f || !_ep)
return -EIO;
- cdev = get_gadget_data(g);
-
/* select desired speed */
switch (g->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ if (gadget_is_superspeed_plus(g)) {
+ if (f->ssp_descriptors) {
+ speed_desc = f->ssp_descriptors;
+ want_comp_desc = 1;
+ break;
+ }
+ incomplete_desc = true;
+ }
+ fallthrough;
case USB_SPEED_SUPER:
if (gadget_is_superspeed(g)) {
- speed_desc = f->ss_descriptors;
- want_comp_desc = 1;
- break;
+ if (f->ss_descriptors) {
+ speed_desc = f->ss_descriptors;
+ want_comp_desc = 1;
+ break;
+ }
+ incomplete_desc = true;
}
- /* else: Fall trough */
+ fallthrough;
case USB_SPEED_HIGH:
if (gadget_is_dualspeed(g)) {
- speed_desc = f->hs_descriptors;
- break;
+ if (f->hs_descriptors) {
+ speed_desc = f->hs_descriptors;
+ break;
+ }
+ incomplete_desc = true;
}
- /* else: fall through */
+ fallthrough;
default:
speed_desc = f->fs_descriptors;
}
+
+ cdev = get_gadget_data(g);
+ if (incomplete_desc)
+ WARNING(cdev,
+ "%s doesn't hold the descriptors for current speed\n",
+ f->name);
+
+ /* find correct alternate setting descriptor */
+ for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) {
+ int_desc = (struct usb_interface_descriptor *)*d_spd;
+
+ if (int_desc->bAlternateSetting == alt) {
+ speed_desc = d_spd;
+ goto intf_found;
+ }
+ }
+ return -EIO;
+
+intf_found:
/* find descriptors */
- for_each_ep_desc(speed_desc, d_spd) {
+ for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) {
chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
if (chosen_desc->bEndpointAddress == _ep->address)
goto ep_found;
@@ -128,7 +233,12 @@ ep_found:
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
- _ep->mult = 0;
+ _ep->mult = 1;
+
+ if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) ||
+ usb_endpoint_xfer_int(_ep->desc)))
+ _ep->mult = usb_endpoint_maxp_mult(_ep->desc);
+
if (!want_comp_desc)
return 0;
@@ -141,11 +251,12 @@ ep_found:
(comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
return -EIO;
_ep->comp_desc = comp_desc;
- if (g->speed == USB_SPEED_SUPER) {
+ if (g->speed >= USB_SPEED_SUPER) {
switch (usb_endpoint_type(_ep->desc)) {
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
- _ep->mult = comp_desc->bmAttributes & 0x3;
+ _ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
+ fallthrough;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
_ep->maxburst = comp_desc->bMaxBurst + 1;
@@ -159,6 +270,32 @@ ep_found:
}
return 0;
}
+EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt);
+
+/**
+ * config_ep_by_speed() - configures the given endpoint
+ * according to gadget speed.
+ * @g: pointer to the gadget
+ * @f: usb function
+ * @_ep: the endpoint to configure
+ *
+ * Return: error code, 0 on success
+ *
+ * This function chooses the right descriptors for a given
+ * endpoint according to gadget speed and saves it in the
+ * endpoint desc field. If the endpoint already has a descriptor
+ * assigned to it - overwrites it with currently corresponding
+ * descriptor. The endpoint maxpacket field is updated according
+ * to the chosen descriptor.
+ * Note: the supplied function should hold all the descriptors
+ * for supported speeds
+ */
+int config_ep_by_speed(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep)
+{
+ return config_ep_by_speed_and_alt(g, f, _ep, 0);
+}
EXPORT_SYMBOL_GPL(config_ep_by_speed);
/**
@@ -190,6 +327,12 @@ int usb_add_function(struct usb_configuration *config,
function->config = config;
list_add_tail(&function->list, &config->functions);
+ if (function->bind_deactivated) {
+ value = usb_function_deactivate(function);
+ if (value)
+ goto done;
+ }
+
/* REVISIT *require* function->bind? */
if (function->bind) {
value = function->bind(config, function);
@@ -211,6 +354,8 @@ int usb_add_function(struct usb_configuration *config,
config->highspeed = true;
if (!config->superspeed && function->ss_descriptors)
config->superspeed = true;
+ if (!config->superspeed_plus && function->ssp_descriptors)
+ config->superspeed_plus = true;
done:
if (value)
@@ -229,6 +374,9 @@ void usb_remove_function(struct usb_configuration *c, struct usb_function *f)
list_del(&f->list);
if (f->unbind)
f->unbind(c, f);
+
+ if (f->bind_deactivated)
+ usb_function_activate(f);
}
EXPORT_SYMBOL_GPL(usb_remove_function);
@@ -254,13 +402,20 @@ EXPORT_SYMBOL_GPL(usb_remove_function);
int usb_function_deactivate(struct usb_function *function)
{
struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
int status = 0;
- if (cdev->deactivations == 0)
- status = usb_gadget_disconnect(cdev->gadget);
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->deactivations == 0) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ status = usb_gadget_deactivate(cdev->gadget);
+ spin_lock_irqsave(&cdev->lock, flags);
+ }
if (status == 0)
cdev->deactivations++;
+ spin_unlock_irqrestore(&cdev->lock, flags);
return status;
}
EXPORT_SYMBOL_GPL(usb_function_deactivate);
@@ -278,16 +433,23 @@ EXPORT_SYMBOL_GPL(usb_function_deactivate);
int usb_function_activate(struct usb_function *function)
{
struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
int status = 0;
+ spin_lock_irqsave(&cdev->lock, flags);
+
if (WARN_ON(cdev->deactivations == 0))
status = -EINVAL;
else {
cdev->deactivations--;
- if (cdev->deactivations == 0)
- status = usb_gadget_connect(cdev->gadget);
+ if (cdev->deactivations == 0) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ status = usb_gadget_activate(cdev->gadget);
+ spin_lock_irqsave(&cdev->lock, flags);
+ }
}
+ spin_unlock_irqrestore(&cdev->lock, flags);
return status;
}
EXPORT_SYMBOL_GPL(usb_function_activate);
@@ -334,18 +496,20 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
{
unsigned val;
- if (c->MaxPower)
+ if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
val = c->MaxPower;
else
- val = usb_gadget_vbus_draw_ma;
+ val = CONFIG_USB_GADGET_VBUS_DRAW;
if (!val)
return 0;
- switch (speed) {
- case USB_SPEED_SUPER:
- return DIV_ROUND_UP(val, 8);
- default:
- return DIV_ROUND_UP(val, 2);
- }
+ if (speed < USB_SPEED_SUPER)
+ return min(val, 500U) / 2;
+ else
+ /*
+ * USB 3.x supports up to 900mA, but since 900 isn't divisible
+ * by 8 the integral division will effectively cap to 896mA.
+ */
+ return min(val, 900U) / 8;
}
static int config_buf(struct usb_configuration *config,
@@ -383,17 +547,7 @@ static int config_buf(struct usb_configuration *config,
list_for_each_entry(f, &config->functions, list) {
struct usb_descriptor_header **descriptors;
- switch (speed) {
- case USB_SPEED_SUPER:
- descriptors = f->ss_descriptors;
- break;
- case USB_SPEED_HIGH:
- descriptors = f->hs_descriptors;
- break;
- default:
- descriptors = f->fs_descriptors;
- }
-
+ descriptors = function_descriptors(f, speed);
if (!descriptors)
continue;
status = usb_descriptor_fillbuf(next, len,
@@ -413,10 +567,11 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
{
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c;
+ struct list_head *pos;
u8 type = w_value >> 8;
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
- if (gadget->speed == USB_SPEED_SUPER)
+ if (gadget->speed >= USB_SPEED_SUPER)
speed = gadget->speed;
else if (gadget_is_dualspeed(gadget)) {
int hs = 0;
@@ -431,9 +586,26 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
/* This is a lookup by config *INDEX* */
w_value &= 0xff;
- list_for_each_entry(c, &cdev->configs, list) {
+
+ pos = &cdev->configs;
+ c = cdev->os_desc_config;
+ if (c)
+ goto check_config;
+
+ while ((pos = pos->next) != &cdev->configs) {
+ c = list_entry(pos, typeof(*c), list);
+
+ /* skip OS Descriptors config which is handled separately */
+ if (c == cdev->os_desc_config)
+ continue;
+
+check_config:
/* ignore configs that won't work at this speed */
switch (speed) {
+ case USB_SPEED_SUPER_PLUS:
+ if (!c->superspeed_plus)
+ continue;
+ break;
case USB_SPEED_SUPER:
if (!c->superspeed)
continue;
@@ -461,18 +633,24 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
unsigned count = 0;
int hs = 0;
int ss = 0;
+ int ssp = 0;
if (gadget_is_dualspeed(gadget)) {
if (gadget->speed == USB_SPEED_HIGH)
hs = 1;
if (gadget->speed == USB_SPEED_SUPER)
ss = 1;
+ if (gadget->speed == USB_SPEED_SUPER_PLUS)
+ ssp = 1;
if (type == USB_DT_DEVICE_QUALIFIER)
hs = !hs;
}
list_for_each_entry(c, &cdev->configs, list) {
/* ignore configs that won't work at this speed */
- if (ss) {
+ if (ssp) {
+ if (!c->superspeed_plus)
+ continue;
+ } else if (ss) {
if (!c->superspeed)
continue;
} else if (hs) {
@@ -499,9 +677,9 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
static int bos_desc(struct usb_composite_dev *cdev)
{
struct usb_ext_cap_descriptor *usb_ext;
- struct usb_ss_cap_descriptor *ss_cap;
struct usb_dcd_config_params dcd_config_params;
struct usb_bos_descriptor *bos = cdev->req->buf;
+ unsigned int besl = 0;
bos->bLength = USB_DT_BOS_SIZE;
bos->bDescriptorType = USB_DT_BOS;
@@ -509,45 +687,173 @@ static int bos_desc(struct usb_composite_dev *cdev)
bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
bos->bNumDeviceCaps = 0;
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params) {
+ cdev->gadget->ops->get_config_params(cdev->gadget,
+ &dcd_config_params);
+ } else {
+ dcd_config_params.besl_baseline =
+ USB_DEFAULT_BESL_UNSPECIFIED;
+ dcd_config_params.besl_deep =
+ USB_DEFAULT_BESL_UNSPECIFIED;
+ dcd_config_params.bU1devExitLat =
+ USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+
+ if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED)
+ besl = USB_BESL_BASELINE_VALID |
+ USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline);
+
+ if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED)
+ besl |= USB_BESL_DEEP_VALID |
+ USB_SET_BESL_DEEP(dcd_config_params.besl_deep);
+
/*
* A SuperSpeed device shall include the USB2.0 extension descriptor
* and shall support LPM when operating in USB2.0 HS mode.
*/
- usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
- bos->bNumDeviceCaps++;
- le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
- usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
- usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
- usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
- usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
+ if (cdev->gadget->lpm_capable) {
+ usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
+ usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
+ usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
+ usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT |
+ USB_BESL_SUPPORT | besl);
+ }
/*
* The Superspeed USB Capability descriptor shall be implemented by all
* SuperSpeed devices.
*/
- ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
- bos->bNumDeviceCaps++;
- le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
- ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
- ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
- ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
- ss_cap->bmAttributes = 0; /* LTM is not supported yet */
- ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
- USB_FULL_SPEED_OPERATION |
- USB_HIGH_SPEED_OPERATION |
- USB_5GBPS_OPERATION);
- ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+ if (gadget_is_superspeed(cdev->gadget)) {
+ struct usb_ss_cap_descriptor *ss_cap;
+
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
+ }
- /* Get Controller configuration */
- if (cdev->gadget->ops->get_config_params)
- cdev->gadget->ops->get_config_params(&dcd_config_params);
- else {
- dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
- dcd_config_params.bU2DevExitLat =
- cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ /* The SuperSpeedPlus USB Device Capability descriptor */
+ if (gadget_is_superspeed_plus(cdev->gadget)) {
+ struct usb_ssp_cap_descriptor *ssp_cap;
+ u8 ssac = 1;
+ u8 ssic;
+ int i;
+
+ if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x2)
+ ssac = 3;
+
+ /*
+ * Paired RX and TX sublink speed attributes share
+ * the same SSID.
+ */
+ ssic = (ssac + 1) / 2 - 1;
+
+ ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac));
+ ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac);
+ ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE;
+ ssp_cap->bReserved = 0;
+ ssp_cap->wReserved = 0;
+
+ ssp_cap->bmAttributes =
+ cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic));
+
+ ssp_cap->wFunctionalitySupport =
+ cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) |
+ FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) |
+ FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1));
+
+ /*
+ * Use 1 SSID if the gadget supports up to gen2x1 or not
+ * specified:
+ * - SSID 0 for symmetric RX/TX sublink speed of 10 Gbps.
+ *
+ * Use 1 SSID if the gadget supports up to gen1x2:
+ * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps.
+ *
+ * Use 2 SSIDs if the gadget supports up to gen2x2:
+ * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps.
+ * - SSID 1 for symmetric RX/TX sublink speed of 10 Gbps.
+ */
+ for (i = 0; i < ssac + 1; i++) {
+ u8 ssid;
+ u8 mantissa;
+ u8 type;
+
+ ssid = i >> 1;
+
+ if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x1 ||
+ cdev->gadget->max_ssp_rate == USB_SSP_GEN_UNKNOWN)
+ mantissa = 10;
+ else
+ mantissa = 5 << ssid;
+
+ if (i % 2)
+ type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX;
+ else
+ type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX;
+
+ ssp_cap->bmSublinkSpeedAttr[i] =
+ cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE,
+ USB_SSP_SUBLINK_SPEED_LSE_GBPS) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP,
+ USB_SSP_SUBLINK_SPEED_LP_SSP) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, mantissa));
+ }
+ }
+
+ /* The WebUSB Platform Capability descriptor */
+ if (cdev->use_webusb) {
+ struct usb_plat_dev_cap_descriptor *webusb_cap;
+ struct usb_webusb_cap_data *webusb_cap_data;
+ guid_t webusb_uuid = WEBUSB_UUID;
+
+ webusb_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ webusb_cap_data = (struct usb_webusb_cap_data *) webusb_cap->CapabilityData;
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength,
+ USB_DT_USB_PLAT_DEV_CAP_SIZE(USB_WEBUSB_CAP_DATA_SIZE));
+
+ webusb_cap->bLength = USB_DT_USB_PLAT_DEV_CAP_SIZE(USB_WEBUSB_CAP_DATA_SIZE);
+ webusb_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ webusb_cap->bDevCapabilityType = USB_PLAT_DEV_CAP_TYPE;
+ webusb_cap->bReserved = 0;
+ export_guid(webusb_cap->UUID, &webusb_uuid);
+
+ if (cdev->bcd_webusb_version != 0)
+ webusb_cap_data->bcdVersion = cpu_to_le16(cdev->bcd_webusb_version);
+ else
+ webusb_cap_data->bcdVersion = WEBUSB_VERSION_1_00;
+
+ webusb_cap_data->bVendorCode = cdev->b_webusb_vendor_code;
+
+ if (strnlen(cdev->landing_page, sizeof(cdev->landing_page)) > 0)
+ webusb_cap_data->iLandingPage = WEBUSB_LANDING_PAGE_PRESENT;
+ else
+ webusb_cap_data->iLandingPage = WEBUSB_LANDING_PAGE_NOT_PRESENT;
}
- ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
- ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
return le16_to_cpu(bos->wTotalLength);
}
@@ -575,45 +881,41 @@ static void reset_config(struct usb_composite_dev *cdev)
{
struct usb_function *f;
- if (cdev->in_reset_config)
- return;
-
- cdev->in_reset_config = 1;
-
DBG(cdev, "reset config\n");
list_for_each_entry(f, &cdev->config->functions, list) {
if (f->disable)
f->disable(f);
+
bitmap_zero(f->endpoints, 32);
}
cdev->config = NULL;
cdev->delayed_status = 0;
- cdev->in_reset_config = 0;
}
static int set_config(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl, unsigned number)
{
struct usb_gadget *gadget = cdev->gadget;
- struct usb_configuration *c = NULL;
+ struct usb_configuration *c = NULL, *iter;
int result = -EINVAL;
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
if (number) {
- list_for_each_entry(c, &cdev->configs, list) {
- if (c->bConfigurationValue == number) {
- /*
- * We disable the FDs of the previous
- * configuration only if the new configuration
- * is a valid one
- */
- if (cdev->config)
- reset_config(cdev);
- result = 0;
- break;
- }
+ list_for_each_entry(iter, &cdev->configs, list) {
+ if (iter->bConfigurationValue != number)
+ continue;
+ /*
+ * We disable the FDs of the previous
+ * configuration only if the new configuration
+ * is a valid one
+ */
+ if (cdev->config)
+ reset_config(cdev);
+ c = iter;
+ result = 0;
+ break;
}
if (result < 0)
goto done;
@@ -624,12 +926,13 @@ static int set_config(struct usb_composite_dev *cdev,
}
INFO(cdev, "%s config #%d: %s\n",
- usb_speed_string(gadget->speed),
- number, c ? c->label : "unconfigured");
+ usb_speed_string(gadget->speed),
+ number, c ? c->label : "unconfigured");
if (!c)
goto done;
+ usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
cdev->config = c;
/* Initialize all interfaces by setting them to altsetting zero. */
@@ -646,16 +949,7 @@ static int set_config(struct usb_composite_dev *cdev,
* function's setup callback instead of the current
* configuration's setup callback.
*/
- switch (gadget->speed) {
- case USB_SPEED_SUPER:
- descriptors = f->ss_descriptors;
- break;
- case USB_SPEED_HIGH:
- descriptors = f->hs_descriptors;
- break;
- default:
- descriptors = f->fs_descriptors;
- }
+ descriptors = function_descriptors(f, gadget->speed);
for (; *descriptors; ++descriptors) {
struct usb_endpoint_descriptor *ep;
@@ -690,8 +984,21 @@ static int set_config(struct usb_composite_dev *cdev,
}
/* when we return, be sure our power usage is valid */
- power = c->MaxPower ? c->MaxPower : usb_gadget_vbus_draw_ma;
+ if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
+ power = c->MaxPower;
+ else
+ power = CONFIG_USB_GADGET_VBUS_DRAW;
+
+ if (gadget->speed < USB_SPEED_SUPER)
+ power = min(power, 500U);
+ else
+ power = min(power, 900U);
done:
+ if (power <= USB_SELF_POWER_VBUS_MAX_DRAW)
+ usb_gadget_set_selfpowered(gadget);
+ else
+ usb_gadget_clear_selfpowered(gadget);
+
usb_gadget_vbus_draw(gadget, power);
if (result >= 0 && cdev->delayed_status)
result = USB_GADGET_DELAYED_STATUS;
@@ -775,8 +1082,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
} else {
unsigned i;
- DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
+ DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
config->bConfigurationValue, config,
+ config->superspeed_plus ? " superplus" : "",
config->superspeed ? " super" : "",
config->highspeed ? " high" : "",
config->fullspeed
@@ -795,9 +1103,7 @@ int usb_add_config(struct usb_composite_dev *cdev,
}
}
- /* set_alt(), or next bind(), sets up
- * ep->driver_data as needed.
- */
+ /* set_alt(), or next bind(), sets up ep->claimed as needed */
usb_ep_autoconfig_reset(cdev->gadget);
done:
@@ -816,12 +1122,8 @@ static void remove_config(struct usb_composite_dev *cdev,
f = list_first_entry(&config->functions,
struct usb_function, list);
- list_del(&f->list);
- if (f->unbind) {
- DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
- f->unbind(config, f);
- /* may free memory for "f" */
- }
+
+ usb_remove_function(config, f);
}
list_del(&config->list);
if (config->unbind) {
@@ -843,9 +1145,15 @@ static void remove_config(struct usb_composite_dev *cdev,
void usb_remove_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
if (cdev->config == config)
reset_config(cdev);
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
remove_config(cdev, config);
}
@@ -853,7 +1161,7 @@ void usb_remove_config(struct usb_composite_dev *cdev,
/* We support strings in multiple languages ... string descriptor zero
* says which languages are supported. The typical case will be that
- * only one language (probably English) is used, with I18N handled on
+ * only one language (probably English) is used, with i18n handled on
* the host side.
*/
@@ -866,7 +1174,7 @@ static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
while (*sp) {
s = *sp;
language = cpu_to_le16(s->language);
- for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+ for (tmp = buf; *tmp && tmp < &buf[USB_MAX_STRING_LEN]; tmp++) {
if (*tmp == language)
goto repeat;
}
@@ -906,7 +1214,7 @@ static int get_string(struct usb_composite_dev *cdev,
struct usb_function *f;
int len;
- /* Yes, not only is USB's I18N support probably more than most
+ /* Yes, not only is USB's i18n support probably more than most
* folk will ever care about ... also, it's all supported here.
* (Except for UTF8 support for Unicode's "Astral Planes".)
*/
@@ -941,7 +1249,7 @@ static int get_string(struct usb_composite_dev *cdev,
collect_langs(sp, s->wData);
}
- for (len = 0; len <= 126 && s->wData[len]; len++)
+ for (len = 0; len <= USB_MAX_STRING_LEN && s->wData[len]; len++)
continue;
if (!len)
return -EINVAL;
@@ -950,6 +1258,19 @@ static int get_string(struct usb_composite_dev *cdev,
return s->bLength;
}
+ if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) {
+ struct usb_os_string *b = buf;
+ b->bLength = sizeof(*b);
+ b->bDescriptorType = USB_DT_STRING;
+ compiletime_assert(
+ sizeof(b->qwSignature) == sizeof(cdev->qw_sign),
+ "qwSignature size must be equal to qw_sign");
+ memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature));
+ b->bMS_VendorCode = cdev->b_vendor_code;
+ b->bPad = 0;
+ return sizeof(*b);
+ }
+
list_for_each_entry(uc, &cdev->gstrings, list) {
struct usb_gadget_strings **sp;
@@ -1013,7 +1334,7 @@ int usb_string_id(struct usb_composite_dev *cdev)
EXPORT_SYMBOL_GPL(usb_string_id);
/**
- * usb_string_ids() - allocate unused string IDs in batch
+ * usb_string_ids_tab() - allocate unused string IDs in batch
* @cdev: the device whose string descriptor IDs are being allocated
* @str: an array of usb_string objects to assign numbers to
* Context: single threaded during gadget setup
@@ -1105,11 +1426,11 @@ static struct usb_gadget_string_container *copy_gadget_strings(
* This function will create a deep copy of usb_gadget_strings and usb_string
* and attach it to the cdev. The actual string (usb_string.s) will not be
* copied but only a referenced will be made. The struct usb_gadget_strings
- * array may contain multiple languges and should be NULL terminated.
+ * array may contain multiple languages and should be NULL terminated.
* The ->language pointer of each struct usb_gadget_strings has to contain the
* same amount of entries.
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
- * usb_string entry of es-ES containts the translation of the first usb_string
+ * usb_string entry of es-ES contains the translation of the first usb_string
* entry of en-US. Therefore both entries become the same id assign.
*/
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
@@ -1211,6 +1532,8 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
if (cdev->req == req)
cdev->setup_pending = false;
+ else if (cdev->os_desc_req == req)
+ cdev->os_desc_pending = false;
else
WARN(1, "unknown request %p\n", req);
}
@@ -1224,6 +1547,8 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev,
if (ret == 0) {
if (cdev->req == req)
cdev->setup_pending = true;
+ else if (cdev->os_desc_req == req)
+ cdev->os_desc_pending = true;
else
WARN(1, "unknown request %p\n", req);
}
@@ -1231,6 +1556,156 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev,
return ret;
}
+static int count_ext_compat(struct usb_configuration *c)
+{
+ int i, res;
+
+ res = 0;
+ for (i = 0; i < c->next_interface_id; ++i) {
+ struct usb_function *f;
+ int j;
+
+ f = c->interface[i];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ struct usb_os_desc *d;
+
+ if (i != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d && d->ext_compat_id)
+ ++res;
+ }
+ }
+ BUG_ON(res > 255);
+ return res;
+}
+
+static int fill_ext_compat(struct usb_configuration *c, u8 *buf)
+{
+ int i, count;
+
+ count = 16;
+ buf += 16;
+ for (i = 0; i < c->next_interface_id; ++i) {
+ struct usb_function *f;
+ int j;
+
+ f = c->interface[i];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ struct usb_os_desc *d;
+
+ if (i != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d && d->ext_compat_id) {
+ *buf++ = i;
+ *buf++ = 0x01;
+ memcpy(buf, d->ext_compat_id, 16);
+ buf += 22;
+ } else {
+ ++buf;
+ *buf = 0x01;
+ buf += 23;
+ }
+ count += 24;
+ if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
+ }
+ }
+
+ return count;
+}
+
+static int count_ext_prop(struct usb_configuration *c, int interface)
+{
+ struct usb_function *f;
+ int j;
+
+ f = c->interface[interface];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ struct usb_os_desc *d;
+
+ if (interface != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d && d->ext_compat_id)
+ return d->ext_prop_count;
+ }
+ return 0;
+}
+
+static int len_ext_prop(struct usb_configuration *c, int interface)
+{
+ struct usb_function *f;
+ struct usb_os_desc *d;
+ int j, res;
+
+ res = 10; /* header length */
+ f = c->interface[interface];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ if (interface != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d)
+ return min(res + d->ext_prop_len, 4096);
+ }
+ return res;
+}
+
+static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
+{
+ struct usb_function *f;
+ struct usb_os_desc *d;
+ struct usb_os_desc_ext_prop *ext_prop;
+ int j, count, n, ret;
+
+ f = c->interface[interface];
+ count = 10; /* header length */
+ buf += 10;
+ for (j = 0; j < f->os_desc_n; ++j) {
+ if (interface != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d)
+ list_for_each_entry(ext_prop, &d->ext_prop, entry) {
+ n = ext_prop->data_len +
+ ext_prop->name_len + 14;
+ if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
+ usb_ext_prop_put_size(buf, n);
+ usb_ext_prop_put_type(buf, ext_prop->type);
+ ret = usb_ext_prop_put_name(buf, ext_prop->name,
+ ext_prop->name_len);
+ if (ret < 0)
+ return ret;
+ switch (ext_prop->type) {
+ case USB_EXT_PROP_UNICODE:
+ case USB_EXT_PROP_UNICODE_ENV:
+ case USB_EXT_PROP_UNICODE_LINK:
+ usb_ext_prop_put_unicode(buf, ret,
+ ext_prop->data,
+ ext_prop->data_len);
+ break;
+ case USB_EXT_PROP_BINARY:
+ usb_ext_prop_put_binary(buf, ret,
+ ext_prop->data,
+ ext_prop->data_len);
+ break;
+ case USB_EXT_PROP_LE32:
+ /* not implemented */
+ case USB_EXT_PROP_BE32:
+ /* not implemented */
+ default:
+ return -EINVAL;
+ }
+ buf += n;
+ count += n;
+ }
+ }
+
+ return count;
+}
+
/*
* The setup() callback implements all the ep0 functionality that's
* not handled lower down, in hardware or the hardware driver(like
@@ -1250,8 +1725,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
+ struct usb_function *iter;
u8 endp;
+ if (w_length > USB_COMP_EP0_BUFSIZ) {
+ if (ctrl->bRequestType & USB_DIR_IN) {
+ /* Cast away the const, we are going to overwrite on purpose. */
+ __le16 *temp = (__le16 *)&ctrl->wLength;
+
+ *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ);
+ w_length = USB_COMP_EP0_BUFSIZ;
+ } else {
+ goto done;
+ }
+ }
+
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
@@ -1262,6 +1750,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = 0;
gadget->ep0->driver_data = cdev;
+ /*
+ * Don't let non-standard requests match any of the cases below
+ * by accident.
+ */
+ if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+ goto unknown;
+
switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */
@@ -1277,11 +1772,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
cdev->gadget->ep0->maxpacket;
if (gadget_is_superspeed(gadget)) {
if (gadget->speed >= USB_SPEED_SUPER) {
- cdev->desc.bcdUSB = cpu_to_le16(0x0300);
+ cdev->desc.bcdUSB = cpu_to_le16(0x0320);
cdev->desc.bMaxPacketSize0 = 9;
} else {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
}
+ } else {
+ if (gadget->lpm_capable || cdev->use_webusb)
+ cdev->desc.bcdUSB = cpu_to_le16(0x0201);
+ else
+ cdev->desc.bcdUSB = cpu_to_le16(0x0200);
}
value = min(w_length, (u16) sizeof cdev->desc);
@@ -1299,7 +1799,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (!gadget_is_dualspeed(gadget) ||
gadget->speed >= USB_SPEED_SUPER)
break;
- /* FALLTHROUGH */
+ fallthrough;
case USB_DT_CONFIG:
value = config_desc(cdev, w_value);
if (value >= 0)
@@ -1312,11 +1812,32 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
value = min(w_length, (u16) value);
break;
case USB_DT_BOS:
- if (gadget_is_superspeed(gadget)) {
+ if (gadget_is_superspeed(gadget) ||
+ gadget->lpm_capable || cdev->use_webusb) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
break;
+ case USB_DT_OTG:
+ if (gadget_is_otg(gadget)) {
+ struct usb_configuration *config;
+ int otg_desc_len = 0;
+
+ if (cdev->config)
+ config = cdev->config;
+ else
+ config = list_first_entry(
+ &cdev->configs,
+ struct usb_configuration, list);
+ if (!config)
+ goto done;
+
+ otg_desc_len += sizeof(struct usb_otg_descriptor);
+
+ value = min_t(int, w_length, otg_desc_len);
+ memcpy(req->buf, config->descriptors[0], value);
+ }
+ break;
}
break;
@@ -1332,7 +1853,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
else
VDBG(cdev, "HNP inactive\n");
}
+ spin_lock(&cdev->lock);
value = set_config(cdev, ctrl, w_value);
+ spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
@@ -1344,9 +1867,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
value = min(w_length, (u16) 1);
break;
- /* function drivers must handle get/set altsetting; if there's
- * no get() method, we know only altsetting zero works.
- */
+ /* function drivers must handle get/set altsetting */
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
@@ -1355,8 +1876,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = cdev->config->interface[intf];
if (!f)
break;
- if (w_value && !f->set_alt)
+
+ /*
+ * If there's no get_alt() method, we know only altsetting zero
+ * works. There is no need to check if set_alt() is not NULL
+ * as we check this in usb_add_function().
+ */
+ if (w_value && !f->get_alt)
break;
+
+ spin_lock(&cdev->lock);
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
DBG(cdev,
@@ -1366,6 +1895,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
DBG(cdev, "delayed_status count %d\n",
cdev->delayed_status);
}
+ spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
@@ -1382,15 +1912,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
*((u8 *)req->buf) = value;
value = min(w_length, (u16) 1);
break;
-
- /*
- * USB 3.0 additions:
- * Function driver should handle get_status request. If such cb
- * wasn't supplied we respond with default value = 0
- * Note: function driver should supply such cb only for the first
- * interface of the function
- */
case USB_REQ_GET_STATUS:
+ if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
+ (w_index == OTG_STS_SELECTOR)) {
+ if (ctrl->bRequestType != (USB_DIR_IN |
+ USB_RECIP_DEVICE))
+ goto unknown;
+ *((u8 *)req->buf) = gadget->host_request_flag;
+ value = 1;
+ break;
+ }
+
+ /*
+ * USB 3.0 additions:
+ * Function driver should handle get_status request. If such cb
+ * wasn't supplied we respond with default value = 0
+ * Note: function driver should supply such cb only for the
+ * first interface of the function
+ */
if (!gadget_is_superspeed(gadget))
goto unknown;
if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
@@ -1439,6 +1978,116 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
break;
default:
unknown:
+ /*
+ * OS descriptors handling
+ */
+ if (cdev->use_os_string && cdev->os_desc_config &&
+ (ctrl->bRequestType & USB_TYPE_VENDOR) &&
+ ctrl->bRequest == cdev->b_vendor_code) {
+ struct usb_configuration *os_desc_cfg;
+ u8 *buf;
+ int interface;
+ int count = 0;
+
+ req = cdev->os_desc_req;
+ req->context = cdev;
+ req->complete = composite_setup_complete;
+ buf = req->buf;
+ os_desc_cfg = cdev->os_desc_config;
+ w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
+ memset(buf, 0, w_length);
+ buf[5] = 0x01;
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ if (w_index != 0x4 || (w_value >> 8))
+ break;
+ buf[6] = w_index;
+ /* Number of ext compat interfaces */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ if (w_length > 0x10) {
+ value = fill_ext_compat(os_desc_cfg, buf);
+ value = min_t(u16, w_length, value);
+ }
+ break;
+ case USB_RECIP_INTERFACE:
+ if (w_index != 0x5 || (w_value >> 8))
+ break;
+ interface = w_value & 0xFF;
+ if (interface >= MAX_CONFIG_INTERFACES ||
+ !os_desc_cfg->interface[interface])
+ break;
+ buf[6] = w_index;
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ if (w_length > 0x0A) {
+ value = fill_ext_prop(os_desc_cfg,
+ interface, buf);
+ if (value >= 0)
+ value = min_t(u16, w_length, value);
+ }
+ break;
+ }
+
+ goto check_value;
+ }
+
+ /*
+ * WebUSB URL descriptor handling, following:
+ * https://wicg.github.io/webusb/#device-requests
+ */
+ if (cdev->use_webusb &&
+ ctrl->bRequestType == (USB_DIR_IN | USB_TYPE_VENDOR) &&
+ w_index == WEBUSB_GET_URL &&
+ w_value == WEBUSB_LANDING_PAGE_PRESENT &&
+ ctrl->bRequest == cdev->b_webusb_vendor_code) {
+ unsigned int landing_page_length;
+ unsigned int landing_page_offset;
+ struct webusb_url_descriptor *url_descriptor =
+ (struct webusb_url_descriptor *)cdev->req->buf;
+
+ url_descriptor->bDescriptorType = WEBUSB_URL_DESCRIPTOR_TYPE;
+
+ if (strncasecmp(cdev->landing_page, "https://", 8) == 0) {
+ landing_page_offset = 8;
+ url_descriptor->bScheme = WEBUSB_URL_SCHEME_HTTPS;
+ } else if (strncasecmp(cdev->landing_page, "http://", 7) == 0) {
+ landing_page_offset = 7;
+ url_descriptor->bScheme = WEBUSB_URL_SCHEME_HTTP;
+ } else {
+ landing_page_offset = 0;
+ url_descriptor->bScheme = WEBUSB_URL_SCHEME_NONE;
+ }
+
+ landing_page_length = strnlen(cdev->landing_page,
+ sizeof(url_descriptor->URL)
+ - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset);
+
+ if (ctrl->wLength < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH
+ + landing_page_length)
+ landing_page_length = ctrl->wLength
+ - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset;
+
+ memcpy(url_descriptor->URL,
+ cdev->landing_page + landing_page_offset,
+ landing_page_length - landing_page_offset);
+ url_descriptor->bLength = landing_page_length
+ - landing_page_offset + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH;
+
+ value = url_descriptor->bLength;
+
+ goto check_value;
+ }
+
VDBG(cdev,
"non-core control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
@@ -1447,11 +2096,22 @@ unknown:
/* functions always handle their interfaces and endpoints...
* punt other recipients (other, WUSB, ...) to the current
* configuration code.
- *
- * REVISIT it could make sense to let the composite device
- * take such requests too, if that's ever needed: to work
- * in config 0, etc.
*/
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list)
+ if (f->req_match &&
+ f->req_match(f, ctrl, false))
+ goto try_fun_setup;
+ } else {
+ struct usb_configuration *c;
+ list_for_each_entry(c, &cdev->configs, list)
+ list_for_each_entry(f, &c->functions, list)
+ if (f->req_match &&
+ f->req_match(f, ctrl, true))
+ goto try_fun_setup;
+ }
+ f = NULL;
+
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
@@ -1460,16 +2120,18 @@ unknown:
break;
case USB_RECIP_ENDPOINT:
+ if (!cdev->config)
+ break;
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
- list_for_each_entry(f, &cdev->config->functions, list) {
- if (test_bit(endp, f->endpoints))
+ list_for_each_entry(iter, &cdev->config->functions, list) {
+ if (test_bit(endp, iter->endpoints)) {
+ f = iter;
break;
+ }
}
- if (&f->list == &cdev->config->functions)
- f = NULL;
break;
}
-
+try_fun_setup:
if (f && f->setup)
value = f->setup(f, ctrl);
else {
@@ -1497,9 +2159,11 @@ unknown:
goto done;
}
+check_value:
/* respond with data transfer before status phase? */
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
+ req->context = cdev;
req->zero = value < w_length;
value = composite_ep0_queue(cdev, req);
if (value < 0) {
@@ -1518,17 +2182,38 @@ done:
return value;
}
-void composite_disconnect(struct usb_gadget *gadget)
+static void __composite_disconnect(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ unsigned long flags;
/* REVISIT: should we have config and device level
* disconnect callbacks?
*/
+ spin_lock_irqsave(&cdev->lock, flags);
+ cdev->suspended = 0;
if (cdev->config)
reset_config(cdev);
if (cdev->driver->disconnect)
cdev->driver->disconnect(cdev);
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+void composite_disconnect(struct usb_gadget *gadget)
+{
+ usb_gadget_vbus_draw(gadget, 0);
+ __composite_disconnect(gadget);
+}
+
+void composite_reset(struct usb_gadget *gadget)
+{
+ /*
+ * Section 1.4.13 Standard Downstream Port of the USB battery charging
+ * specification v1.2 states that a device connected on a SDP shall only
+ * draw at max 100mA while in a connected, but unconfigured state.
+ */
+ usb_gadget_vbus_draw(gadget, 100);
+ __composite_disconnect(gadget);
}
/*-------------------------------------------------------------------------*/
@@ -1536,6 +2221,8 @@ void composite_disconnect(struct usb_gadget *gadget)
static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_gadget_strings *gstr = cdev->driver->strings[0];
+ struct usb_string *dev_str = gstr->strings;
/* composite_disconnect() must already have been called
* by the underlying peripheral controller driver!
@@ -1555,6 +2242,9 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
composite_dev_cleanup(cdev);
+ if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer)
+ dev_str[USB_GADGET_MANUFACTURER_IDX].s = "";
+
kfree(cdev->def_manufacturer);
kfree(cdev);
set_gadget_data(gadget, NULL);
@@ -1619,6 +2309,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
goto fail;
cdev->req->complete = composite_setup_complete;
+ cdev->req->context = cdev;
gadget->ep0->driver_data = cdev;
cdev->driver = composite;
@@ -1628,7 +2319,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
* more than 100mA from USB must report itself as bus-powered in
* the GetStatus(DEVICE) call.
*/
- if (usb_gadget_vbus_draw_ma <= USB_SELF_POWER_VBUS_MAX_DRAW)
+ if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero via kzalloc.
@@ -1644,21 +2335,73 @@ fail:
return ret;
}
+int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
+ struct usb_ep *ep0)
+{
+ int ret = 0;
+
+ cdev->os_desc_req = usb_ep_alloc_request(ep0);
+ if (!cdev->os_desc_req) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ,
+ GFP_KERNEL);
+ if (!cdev->os_desc_req->buf) {
+ ret = -ENOMEM;
+ usb_ep_free_request(ep0, cdev->os_desc_req);
+ goto end;
+ }
+ cdev->os_desc_req->context = cdev;
+ cdev->os_desc_req->complete = composite_setup_complete;
+end:
+ return ret;
+}
+
void composite_dev_cleanup(struct usb_composite_dev *cdev)
{
struct usb_gadget_string_container *uc, *tmp;
+ struct usb_ep *ep, *tmp_ep;
list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
list_del(&uc->list);
kfree(uc);
}
+ if (cdev->os_desc_req) {
+ if (cdev->os_desc_pending)
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req);
+
+ kfree(cdev->os_desc_req->buf);
+ cdev->os_desc_req->buf = NULL;
+ usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
+ cdev->os_desc_req = NULL;
+ }
if (cdev->req) {
if (cdev->setup_pending)
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
+
kfree(cdev->req->buf);
+ cdev->req->buf = NULL;
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
+ cdev->req = NULL;
}
cdev->next_string_id = 0;
+
+ /*
+ * Some UDC backends have a dynamic EP allocation scheme.
+ *
+ * In that case, the dispose() callback is used to notify the
+ * backend that the EPs are no longer in use.
+ *
+ * Note: The UDC backend can remove the EP from the ep_list as
+ * a result, so we need to use the _safe list iterator.
+ */
+ list_for_each_entry_safe(ep, tmp_ep,
+ &cdev->gadget->ep_list, ep_list) {
+ if (ep->ops->dispose)
+ ep->ops->dispose(ep);
+ }
}
static int composite_bind(struct usb_gadget *gadget,
@@ -1672,6 +2415,7 @@ static int composite_bind(struct usb_gadget *gadget,
if (!cdev)
return status;
+ spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->configs);
@@ -1689,6 +2433,12 @@ static int composite_bind(struct usb_gadget *gadget,
if (status < 0)
goto fail;
+ if (cdev->use_os_string) {
+ status = composite_os_desc_req_prepare(cdev, gadget->ep0);
+ if (status)
+ goto fail;
+ }
+
update_unchanged_dev_desc(&cdev->desc, composite->dev);
/* has userspace failed to provide a serial number? */
@@ -1710,6 +2460,7 @@ static const struct usb_gadget_driver composite_driver_template = {
.unbind = composite_unbind,
.setup = composite_setup,
+ .reset = composite_reset,
.disconnect = composite_disconnect,
};
@@ -1745,8 +2496,9 @@ int usb_composite_probe(struct usb_composite_driver *driver)
gadget_driver->function = (char *) driver->name;
gadget_driver->driver.name = driver->name;
gadget_driver->max_speed = driver->max_speed;
+ gadget_driver->match_existing_only = true;
- return usb_gadget_probe_driver(gadget_driver);
+ return usb_gadget_register_driver(gadget_driver);
}
EXPORT_SYMBOL_GPL(usb_composite_probe);
@@ -1777,8 +2529,10 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
{
int value;
struct usb_request *req = cdev->req;
+ unsigned long flags;
DBG(cdev, "%s\n", __func__);
+ spin_lock_irqsave(&cdev->lock, flags);
if (cdev->delayed_status == 0) {
WARN(cdev, "%s: Unexpected call\n", __func__);
@@ -1786,6 +2540,7 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);
req->length = 0;
+ req->context = cdev;
value = composite_ep0_queue(cdev, req);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
@@ -1793,12 +2548,14 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
composite_setup_complete(cdev->gadget->ep0, req);
}
}
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_composite_setup_continue);
static char *composite_default_mfr(struct usb_gadget *gadget)
{
- return basprintf("barebox %s", gadget->name);
+ return basprintf("barebox with %s", gadget->name);
}
void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 369eb2ba5e..27e4dda52f 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -7,9 +7,9 @@
#include <common.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
-#include <usb/composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
@@ -152,7 +152,8 @@ EXPORT_SYMBOL_GPL(usb_copy_descriptors);
int usb_assign_descriptors(struct usb_function *f,
struct usb_descriptor_header **fs,
struct usb_descriptor_header **hs,
- struct usb_descriptor_header **ss)
+ struct usb_descriptor_header **ss,
+ struct usb_descriptor_header **ssp)
{
struct usb_gadget *g = f->config->cdev->gadget;
@@ -171,6 +172,11 @@ int usb_assign_descriptors(struct usb_function *f,
if (!f->ss_descriptors)
goto err;
}
+ if (ssp && gadget_is_superspeed_plus(g)) {
+ f->ssp_descriptors = usb_copy_descriptors(ssp);
+ if (!f->ssp_descriptors)
+ goto err;
+ }
return 0;
err:
usb_free_all_descriptors(f);
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index e9f13f4262..ff16abaf12 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -11,10 +11,10 @@
#include <linux/ctype.h>
#include <asm/byteorder.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
-#include "gadget_chips.h"
+#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
/*
* This should work with endpoints from controller drivers sharing the
@@ -182,18 +182,6 @@ ep_matches (
return 1;
}
-static struct usb_ep *
-find_ep (struct usb_gadget *gadget, const char *name)
-{
- struct usb_ep *ep;
-
- list_for_each_entry (ep, &gadget->ep_list, ep_list) {
- if (0 == strcmp (ep->name, name))
- return ep;
- }
- return NULL;
-}
-
/**
* usb_ep_autoconfig_ss() - choose an endpoint matching the ep
* descriptor and ep companion descriptor
@@ -249,34 +237,6 @@ struct usb_ep *usb_ep_autoconfig_ss(
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
- /* First, apply chip-specific "best usage" knowledge.
- * This might make a good usb_gadget_ops hook ...
- */
- if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
- /* ep-e, ep-f are PIO with only 64 byte fifos */
- ep = find_ep (gadget, "ep-e");
- if (ep && ep_matches(gadget, ep, desc, ep_comp))
- goto found_ep;
- ep = find_ep (gadget, "ep-f");
- if (ep && ep_matches(gadget, ep, desc, ep_comp))
- goto found_ep;
-
- } else if (gadget_is_goku (gadget)) {
- if (USB_ENDPOINT_XFER_INT == type) {
- /* single buffering is enough */
- ep = find_ep(gadget, "ep3-bulk");
- if (ep && ep_matches(gadget, ep, desc, ep_comp))
- goto found_ep;
- } else if (USB_ENDPOINT_XFER_BULK == type
- && (USB_DIR_IN & desc->bEndpointAddress)) {
- /* DMA may be available */
- ep = find_ep(gadget, "ep2-bulk");
- if (ep && ep_matches(gadget, ep, desc,
- ep_comp))
- goto found_ep;
- }
- }
-
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches(gadget, ep, desc, ep_comp))
diff --git a/drivers/usb/gadget/fsl_udc_pbl.c b/drivers/usb/gadget/fsl_udc_pbl.c
deleted file mode 100644
index 8b714d4c8b..0000000000
--- a/drivers/usb/gadget/fsl_udc_pbl.c
+++ /dev/null
@@ -1,221 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-
-#include <common.h>
-#include <usb/ch9.h>
-#include <soc/fsl/fsl_udc.h>
-#include <mach/imx8mm-regs.h>
-#include <mach/imx6-regs.h>
-
-static void fsl_queue_td(struct usb_dr_device *dr, struct ep_td_struct *dtd,
- int ep_is_in)
-{
- int ep_index = 0;
- int i = ep_index * 2 + ep_is_in;
- u32 bitmask;
- volatile struct ep_queue_head *dQH =
- (void *)(unsigned long)readl(&dr->endpointlistaddr);
- unsigned long td_dma = (unsigned long)dtd;
-
- dQH = &dQH[i];
-
- bitmask = ep_is_in ? (1 << (ep_index + 16)) : (1 << (ep_index));
-
- dQH->next_dtd_ptr = cpu_to_le32(td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK);
-
- dQH->size_ioc_int_sts &= cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
- | EP_QUEUE_HEAD_STATUS_HALT));
-
- writel(bitmask, &dr->endpointprime);
-}
-
-static struct ep_td_struct dtd_data __attribute__((aligned(64)));
-static struct ep_td_struct dtd_status __attribute__((aligned(64)));
-
-static int fsl_ep_queue(struct usb_dr_device *dr, struct ep_td_struct *dtd,
- void *buf, int len)
-{
- u32 swap_temp;
-
- memset(dtd, 0, sizeof(*dtd));
-
- /* Clear reserved field */
- swap_temp = cpu_to_le32(dtd->size_ioc_sts);
- swap_temp &= ~DTD_RESERVED_FIELDS;
- dtd->size_ioc_sts = cpu_to_le32(swap_temp);
-
- swap_temp = (unsigned long)buf;
- dtd->buff_ptr0 = cpu_to_le32(swap_temp);
- dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
- dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
- dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
- dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
-
- /* Fill in the transfer size; set active bit */
- swap_temp = ((len << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE) | DTD_IOC;
-
- writel(cpu_to_le32(swap_temp), &dtd->size_ioc_sts);
-
- dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
-
- fsl_queue_td(dr, dtd, len ? 0 : 1);
-
- return 0;
-}
-
-enum state {
- state_init = 0,
- state_expect_command,
- state_transfer_data,
- state_complete,
-};
-
-#define MAX_TRANSFER_SIZE 2048
-
-static enum state state;
-static uint8_t databuf[MAX_TRANSFER_SIZE] __attribute__((aligned(64)));
-static int actual;
-static int to_transfer;
-static void *image;
-
-static void tripwire_handler(struct usb_dr_device *dr, u8 ep_num)
-{
- uint32_t val;
- struct ep_queue_head *qh;
- struct ep_queue_head *dQH = (void *)(unsigned long)readl(&dr->endpointlistaddr);
- struct usb_ctrlrequest *ctrl;
-
- qh = &dQH[ep_num * 2];
-
- val = readl(&dr->endptsetupstat);
- val |= 1 << ep_num;
- writel(val, &dr->endptsetupstat);
-
- do {
- val = readl(&dr->usbcmd);
- val |= USB_CMD_SUTW;
- writel(val, &dr->usbcmd);
-
- ctrl = (void *)qh->setup_buffer;
- if ((ctrl->wValue & 0xff) == 1)
- state = state_expect_command;
-
- } while (!(readl(&dr->usbcmd) & USB_CMD_SUTW));
-
- val = readl(&dr->usbcmd);
- val &= ~USB_CMD_SUTW;
- writel(val, &dr->usbcmd);
-
- fsl_ep_queue(dr, &dtd_data, databuf, MAX_TRANSFER_SIZE);
-}
-
-static void dtd_complete_irq(struct usb_dr_device *dr)
-{
- struct ep_td_struct *dtd = &dtd_data;
- u32 bit_pos;
- int len;
-
- /* Clear the bits in the register */
- bit_pos = readl(&dr->endptcomplete);
- writel(bit_pos, &dr->endptcomplete);
-
- if (!(bit_pos & 1))
- return;
-
- len = MAX_TRANSFER_SIZE -
- (le32_to_cpu(dtd->size_ioc_sts) >> DTD_LENGTH_BIT_POS);
-
- if (state == state_expect_command) {
- state = state_transfer_data;
- to_transfer = databuf[8] << 24 |
- databuf[9] << 16 |
- databuf[10] << 8 |
- databuf[11];
- } else {
- memcpy(image + actual, &databuf[1], len - 1);
- actual += len - 1;
- to_transfer -= len - 1;
-
- if (to_transfer == 0)
- state = state_complete;
- }
-
- fsl_ep_queue(dr, &dtd_status, NULL, 0);
-}
-
-static int usb_irq(struct usb_dr_device *dr)
-{
- uint32_t irq_src = readl(&dr->usbsts);
-
- irq_src &= ~0x80;
-
- if (!irq_src)
- return -EAGAIN;
-
- /* Clear notification bits */
- writel(irq_src, &dr->usbsts);
-
- /* USB Interrupt */
- if (irq_src & USB_STS_INT) {
- /* Setup package, we only support ep0 as control ep */
- if (readl(&dr->endptsetupstat) & EP_SETUP_STATUS_EP0)
- tripwire_handler(dr, 0);
-
- /* completion of dtd */
- if (readl(&dr->endptcomplete))
- dtd_complete_irq(dr);
- }
-
- if (state == state_complete)
- return 0;
- else
- return -EAGAIN;
-}
-
-int imx_barebox_load_usb(void __iomem *dr, void *dest)
-{
- int ret;
-
- image = dest;
-
- while (1) {
- ret = usb_irq(dr);
- if (!ret)
- break;
- }
-
- return 0;
-}
-
-int imx_barebox_start_usb(void __iomem *dr, void *dest)
-{
- void __noreturn (*bb)(void);
- int ret;
-
- ret = imx_barebox_load_usb(dr, dest);
- if (ret)
- return ret;
-
- printf("Downloading complete, start barebox\n");
- bb = dest;
- bb();
-}
-
-int imx6_barebox_load_usb(void *dest)
-{
- return imx_barebox_load_usb(IOMEM(MX6_OTG_BASE_ADDR), dest);
-}
-
-int imx6_barebox_start_usb(void *dest)
-{
- return imx_barebox_start_usb(IOMEM(MX6_OTG_BASE_ADDR), dest);
-}
-
-int imx8mm_barebox_load_usb(void *dest)
-{
- return imx_barebox_load_usb(IOMEM(MX8MM_USB1_BASE_ADDR), dest);
-}
-
-int imx8mm_barebox_start_usb(void *dest)
-{
- return imx_barebox_start_usb(IOMEM(MX8MM_USB1_BASE_ADDR), dest);
-}
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
new file mode 100644
index 0000000000..de306b929f
--- /dev/null
+++ b/drivers/usb/gadget/function/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o f_serial.o f_acm.o
+obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
+obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o
+obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o storage_common.o
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/function/dfu.c
index 0b7ca82c4a..3a6d2cf385 100644
--- a/drivers/usb/gadget/dfu.c
+++ b/drivers/usb/gadget/function/dfu.c
@@ -26,15 +26,15 @@
#include <dma.h>
#include <asm/byteorder.h>
-#include <usb/composite.h>
+#include <linux/usb/composite.h>
#include <linux/types.h>
#include <linux/list.h>
-#include <usb/gadget.h>
+#include <linux/usb/gadget.h>
#include <linux/stat.h>
#include <libfile.h>
#include <linux/err.h>
-#include <usb/ch9.h>
-#include <usb/dfu.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/dfu.h>
#include <config.h>
#include <common.h>
#include <malloc.h>
@@ -361,7 +361,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_interface_descriptor *desc;
struct file_list_entry *fentry;
struct f_dfu *dfu = func_to_dfu(f);
- int i;
+ int i, n_entries;
int status;
struct usb_string *us;
@@ -372,7 +372,9 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f)
dfu_files = opts->files;
}
- dfu_string_defs = xzalloc(sizeof(struct usb_string) * (dfu_files->num_entries + 2));
+ n_entries = list_count_nodes(&dfu_files->list);
+
+ dfu_string_defs = xzalloc(sizeof(struct usb_string) * (n_entries + 2));
dfu_string_defs[0].s = "Generic DFU";
i = 0;
file_list_for_each_entry(dfu_files, fentry) {
@@ -396,7 +398,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f)
dfu->dnreq->complete = dn_complete;
dfu->dnreq->zero = 0;
- us = usb_gstrings_attach(cdev, dfu_strings, dfu_files->num_entries + 1);
+ us = usb_gstrings_attach(cdev, dfu_strings, n_entries + 1);
if (IS_ERR(us)) {
status = PTR_ERR(us);
goto out;
@@ -411,9 +413,9 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto out;
- header = xzalloc(sizeof(void *) * (dfu_files->num_entries + 2));
- desc = xzalloc(sizeof(struct usb_interface_descriptor) * dfu_files->num_entries);
- for (i = 0; i < dfu_files->num_entries; i++) {
+ header = xzalloc(sizeof(void *) * (n_entries + 2));
+ desc = xzalloc(sizeof(struct usb_interface_descriptor) * n_entries);
+ for (i = 0; i < n_entries; i++) {
desc[i].bLength = USB_DT_INTERFACE_SIZE;
desc[i].bDescriptorType = USB_DT_INTERFACE;
desc[i].bNumEndpoints = 0;
@@ -427,7 +429,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f)
header[i] = (struct usb_descriptor_header *) &usb_dfu_func;
header[i + 1] = NULL;
- status = usb_assign_descriptors(f, header, header, NULL);
+ status = usb_assign_descriptors(f, header, header, header, header);
free(desc);
free(header);
@@ -485,6 +487,20 @@ static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
return -EINVAL;
}
+static int dfu_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct file_list_entry *fentry;
+ int i = 0;
+
+ file_list_for_each_entry(dfu_files, fentry) {
+ if (fentry == dfu_file_entry)
+ return i;
+ i++;
+ }
+
+ return -EINVAL;
+}
+
static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_dfu *dfu = func_to_dfu(f);
@@ -830,6 +846,7 @@ static struct usb_function *dfu_alloc_func(struct usb_function_instance *fi)
/* descriptors are per-instance copies */
dfu->func.bind = dfu_bind;
dfu->func.set_alt = dfu_set_alt;
+ dfu->func.get_alt = dfu_get_alt;
dfu->func.setup = dfu_setup;
dfu->func.disable = dfu_disable;
dfu->func.unbind = dfu_unbind;
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 42438947c1..3532fd5892 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -12,14 +12,13 @@
/* #define VERBOSE_DEBUG */
#include <common.h>
-#include <usb/cdc.h>
+#include <linux/usb/cdc.h>
#include <linux/err.h>
#include <linux/spinlock.h>
#include <asm/byteorder.h>
-#include <usb/composite.h>
+#include <linux/usb/composite.h>
#include "u_serial.h"
-#include "gadget_chips.h"
/*
@@ -679,7 +678,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
- acm_ss_function);
+ acm_ss_function, acm_ss_function);
if (status)
goto fail;
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/function/f_fastboot.c
index 96924c4cb9..30d257b500 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/function/f_fastboot.c
@@ -25,7 +25,7 @@
#include <unistd.h>
#include <progress.h>
#include <fastboot.h>
-#include <usb/fastboot.h>
+#include <linux/usb/fastboot.h>
#define FASTBOOT_INTERFACE_CLASS 0xff
#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
@@ -39,7 +39,7 @@ struct f_fastboot {
/* IN/OUT EP's and corresponding requests */
struct usb_ep *in_ep, *out_ep;
- struct usb_request *in_req, *out_req;
+ struct usb_request *out_req;
struct work_queue wq;
};
@@ -109,6 +109,36 @@ static struct usb_descriptor_header *fb_hs_descs[] = {
NULL,
};
+static struct usb_endpoint_descriptor ss_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = {
+ .bLength = sizeof(fb_ss_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *fb_ss_descs[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&ss_ep_in,
+ (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *)&ss_ep_out,
+ (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+ NULL,
+};
+
/*
* static strings, in UTF-8
*/
@@ -134,10 +164,6 @@ static int fastboot_write_usb(struct fastboot *fb, const char *buffer,
unsigned int buffer_size);
static void fastboot_start_download_usb(struct fastboot *fb);
-static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
-{
-}
-
struct fastboot_work {
struct work_struct work;
struct f_fastboot *f_fb;
@@ -173,16 +199,26 @@ static struct usb_request *fastboot_alloc_request(struct usb_ep *ep)
return NULL;
req->length = EP_BUFFER_SIZE;
- req->buf = dma_alloc(EP_BUFFER_SIZE);
+ req->buf = dma_zalloc(EP_BUFFER_SIZE);
if (!req->buf) {
usb_ep_free_request(ep, req);
return NULL;
}
- memset(req->buf, 0, EP_BUFFER_SIZE);
return req;
}
+static void fastboot_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+ free(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ fastboot_free_request(ep, req);
+}
+
static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
@@ -248,6 +284,8 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+ ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+ ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
f_fb->out_req = fastboot_alloc_request(f_fb->out_ep);
if (!f_fb->out_req) {
@@ -259,24 +297,13 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
f_fb->out_req->complete = rx_handler_command;
f_fb->out_req->context = f_fb;
- f_fb->in_req = fastboot_alloc_request(f_fb->in_ep);
- if (!f_fb->in_req) {
- puts("failed alloc req in\n");
- ret = -EINVAL;
- goto err_free_out_req;
- }
- f_fb->in_req->complete = fastboot_complete;
-
- ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, NULL);
+ ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, fb_ss_descs, fb_ss_descs);
if (ret)
goto err_free_in_req;
return 0;
err_free_in_req:
- free(f_fb->in_req->buf);
- usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
-err_free_out_req:
free(f_fb->out_req->buf);
usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
fb_generic_free:
@@ -291,12 +318,6 @@ static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_fastboot *f_fb = func_to_fastboot(f);
- usb_ep_dequeue(f_fb->in_ep, f_fb->in_req);
- free(f_fb->in_req->buf);
- usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
- f_fb->in_req = NULL;
-
- usb_ep_dequeue(f_fb->out_ep, f_fb->out_req);
free(f_fb->out_req->buf);
usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
f_fb->out_req = NULL;
@@ -404,28 +425,23 @@ DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func
static int fastboot_write_usb(struct fastboot *fb, const char *buffer, unsigned int buffer_size)
{
struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot);
- struct usb_request *in_req = f_fb->in_req;
- uint64_t start;
+ struct usb_request *in_req;
int ret;
+ in_req = fastboot_alloc_request(f_fb->in_ep);
+ if (!in_req)
+ return -ENOMEM;
+
memcpy(in_req->buf, buffer, buffer_size);
in_req->length = buffer_size;
+ in_req->complete = fastboot_complete;
ret = usb_ep_queue(f_fb->in_ep, in_req);
- if (ret)
+ if (ret) {
+ fastboot_free_request(f_fb->in_ep, in_req);
pr_err("Error %d on queue\n", ret);
-
- start = get_time_ns();
-
- while (in_req->status == -EINPROGRESS) {
- if (is_timeout(start, 2 * SECOND))
- return -ETIMEDOUT;
- usb_gadget_poll();
}
- if (in_req->status)
- pr_err("Failed to send answer: %d\n", in_req->status);
-
return 0;
}
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 1c26c4d996..2c934c621a 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -225,12 +225,12 @@
#include <scsi.h>
#include <linux/err.h>
-#include <usb/mass_storage.h>
+#include <linux/usb/mass_storage.h>
#include <asm/unaligned.h>
#include <linux/bitops.h>
-#include <usb/gadget.h>
-#include <usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
#include <linux/bitmap.h>
#include <linux/completion.h>
#include <bthread.h>
@@ -2194,15 +2194,18 @@ reset:
fsg = common->fsg;
/* Enable the endpoints */
- fsg->bulk_in->desc = fsg_ep_desc(common->gadget,
- &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
+ rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+ if (rc)
+ goto reset;
rc = enable_endpoint(common, fsg->bulk_in);
if (rc)
goto reset;
fsg->bulk_in_enabled = 1;
- fsg->bulk_out->desc = fsg_ep_desc(common->gadget,
- &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
+ rc = config_ep_by_speed(common->gadget, &(fsg->function),
+ fsg->bulk_out);
+ if (rc)
+ goto reset;
rc = enable_endpoint(common, fsg->bulk_out);
if (rc)
goto reset;
@@ -2615,7 +2618,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_gadget *gadget = c->cdev->gadget;
int ret;
struct usb_ep *ep;
- struct usb_descriptor_header **hs_function = NULL;
+ unsigned max_burst;
struct fsg_common *common = fsg->common;
if (!ums_files) {
@@ -2656,17 +2659,26 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
ep->driver_data = common; /* claim the endpoint */
fsg->bulk_out = ep;
- if (gadget_is_dualspeed(gadget)) {
- /* Assume endpoint addresses are the same for both speeds */
- fsg_hs_bulk_in_desc.bEndpointAddress =
- fsg_fs_bulk_in_desc.bEndpointAddress;
- fsg_hs_bulk_out_desc.bEndpointAddress =
- fsg_fs_bulk_out_desc.bEndpointAddress;
- hs_function = fsg_hs_function;
- }
+ /* Assume endpoint addresses are the same for both speeds */
+ fsg_hs_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_hs_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+
+ /* Calculate bMaxBurst, we know packet size is 1024 */
+ max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
+
+ fsg_ss_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+ fsg_ss_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
/* Copy descriptors */
- return usb_assign_descriptors(f, fsg_fs_function, hs_function, NULL);
+ return usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
+ fsg_ss_function, fsg_ss_function);
autoconf_fail:
ERROR(fsg, "unable to autoconfigure all endpoints\n");
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 33cf54dc1c..a768c580ea 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -12,7 +12,6 @@
#include <linux/err.h>
#include "u_serial.h"
-#include "gadget_chips.h"
/*
@@ -232,7 +231,7 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
- gser_ss_function);
+ gser_ss_function, gser_ss_function);
if (status)
goto fail;
DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 69fcd06565..60e0994235 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -104,6 +104,48 @@ struct usb_descriptor_header *fsg_hs_function[] = {
NULL,
};
+struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+struct usb_descriptor_header *fsg_ss_function[] = {
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+ NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_ss_function);
+
/* Maxpacket and other transfer characteristics vary by speed. */
struct usb_endpoint_descriptor *
fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index 61ed5b1ded..29afe77685 100644
--- a/drivers/usb/gadget/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -4,9 +4,9 @@
#define USB_STORAGE_COMMON_H
#include <driver.h>
-#include <usb/storage.h>
+#include <linux/usb/storage.h>
#include <asm/unaligned.h>
-#include <usb/mass_storage.h>
+#include <linux/usb/mass_storage.h>
#ifndef DEBUG
#undef VERBOSE_DEBUG
@@ -136,7 +136,7 @@ struct fsg_lun {
u32 sense_data_info;
u32 unit_attention_data;
- struct device_d dev;
+ struct device dev;
};
#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
@@ -232,6 +232,12 @@ extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
extern struct usb_descriptor_header *fsg_hs_function[];
+extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
+extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
+extern struct usb_descriptor_header *fsg_ss_function[];
+
int fsg_lun_open(struct fsg_lun *curlun, unsigned int num_sectors,
const char *filename);
void fsg_lun_close(struct fsg_lun *curlun);
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 2ce3f1c791..ca4e77c5ff 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -16,7 +16,7 @@
#include <common.h>
#include <complete.h>
-#include <usb/cdc.h>
+#include <linux/usb/cdc.h>
#include <kfifo.h>
#include <clock.h>
#include <linux/err.h>
@@ -155,12 +155,13 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_port *port = ep->driver_data;
+ list_add_tail(&req->list, &port->read_pool);
+ port->read_nb_queued--;
+
if (req->status == -ESHUTDOWN)
return;
kfifo_put(port->recv_fifo, req->buf, req->actual);
- list_add_tail(&req->list, &port->read_pool);
- port->read_nb_queued--;
gs_start_rx(port);
}
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index 80450ccc86..44fcace030 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -9,8 +9,8 @@
#ifndef __U_SERIAL_H
#define __U_SERIAL_H
-#include <usb/composite.h>
-#include <usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
#define MAX_U_SERIAL_PORTS 4
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
index 9b08938a33..7a57915007 100644
--- a/drivers/usb/gadget/functions.c
+++ b/drivers/usb/gadget/functions.c
@@ -2,7 +2,7 @@
#include <common.h>
#include <linux/err.h>
-#include <usb/composite.h>
+#include <linux/usb/composite.h>
static LIST_HEAD(func_list);
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
deleted file mode 100644
index 78e0601027..0000000000
--- a/drivers/usb/gadget/gadget_chips.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * USB device controllers have lots of quirks. Use these macros in
- * gadget drivers or other code that needs to deal with them, and which
- * autoconfigures instead of using early binding to the hardware.
- *
- * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
- * some config file that gets updated as new hardware is supported.
- * (And avoiding all runtime comparisons in typical one-choice configs!)
- *
- * NOTE: some of these controller drivers may not be available yet.
- * Some are available on 2.4 kernels; several are available, but not
- * yet pushed in the 2.6 mainline tree.
- */
-
-#ifndef __GADGET_CHIPS_H
-#define __GADGET_CHIPS_H
-
-#include <usb/gadget.h>
-
-/*
- * NOTICE: the entries below are alphabetical and should be kept
- * that way.
- *
- * Always be sure to add new entries to the correct position or
- * accept the bashing later.
- *
- * If you have forgotten the alphabetical order let VIM/EMACS
- * do that for you.
- */
-#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
-#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
-#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
-#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
-#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
-#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
-
-/**
- * gadget_supports_altsettings - return true if altsettings work
- * @gadget: the gadget in question
- */
-static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
-{
- /* PXA 21x/25x/26x has no altsettings at all */
- if (gadget_is_pxa(gadget))
- return false;
-
- /* PXA 27x and 3xx have *broken* altsetting support */
- if (gadget_is_pxa27x(gadget))
- return false;
-
- /* Everything else is *presumably* fine ... */
- return true;
-}
-
-#endif /* __GADGET_CHIPS_H */
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
new file mode 100644
index 0000000000..5d5382a673
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ccflags-y := -I$(srctree)/drivers/usb/gadget/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/function/
+
+obj-$(CONFIG_USB_GADGET_SERIAL) += serial.o
+obj-$(CONFIG_USB_GADGET) += multi.o
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c
index 6225e9a313..ddb3d4158c 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -9,7 +9,7 @@
*/
#include <common.h>
-#include <usb/gadget-multi.h>
+#include <linux/usb/gadget-multi.h>
#include <linux/err.h>
#include "u_serial.h"
@@ -275,7 +275,7 @@ static struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
- .max_speed = USB_SPEED_HIGH,
+ .max_speed = USB_SPEED_SUPER,
.bind = multi_bind,
.unbind = multi_unbind,
.needs_serial = 1,
@@ -294,10 +294,8 @@ int usb_multi_register(struct f_multi_opts *opts)
gadget_multi_opts = opts;
ret = usb_composite_probe(&multi_driver);
- if (ret) {
- usb_composite_unregister(&multi_driver);
+ if (ret)
gadget_multi_opts = NULL;
- }
return ret;
}
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c
index 0ef2665b69..913d174a91 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/legacy/serial.c
@@ -11,14 +11,13 @@
#include <errno.h>
#include <init.h>
#include <linux/err.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
-#include <usb/composite.h>
-#include <usb/usbserial.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/usbserial.h>
#include <asm/byteorder.h>
#include "u_serial.h"
-#include "gadget_chips.h"
/* Defines */
@@ -248,8 +247,17 @@ static struct usb_composite_driver gserial_driver = {
.unbind = gs_unbind,
};
+static bool usb_serial_registered;
+
int usb_serial_register(struct usb_serial_pdata *pdata)
{
+ int ret;
+
+ if (usb_serial_registered) {
+ pr_err("USB serial gadget already registered\n");
+ return -EBUSY;
+ }
+
/* We *could* export two configs; that'd be much cleaner...
* but neither of these product IDs was defined that way.
*/
@@ -274,10 +282,18 @@ int usb_serial_register(struct usb_serial_pdata *pdata)
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
}
- return usb_composite_probe(&gserial_driver);
+ ret = usb_composite_probe(&gserial_driver);
+ if (!ret)
+ usb_serial_registered = true;
+
+ return ret;
}
void usb_serial_unregister(void)
{
+ if (!usb_serial_registered)
+ return;
+
usb_composite_unregister(&gserial_driver);
+ usb_serial_registered = false;
}
diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h
new file mode 100644
index 0000000000..5d7d35c8cc
--- /dev/null
+++ b/drivers/usb/gadget/u_os_desc.h
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_os_desc.h
+ *
+ * Utility definitions for "OS Descriptors" support
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
+ */
+
+#ifndef __U_OS_DESC_H__
+#define __U_OS_DESC_H__
+
+#include <asm/unaligned.h>
+#include <linux/nls.h>
+
+#define USB_EXT_PROP_DW_SIZE 0
+#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4
+#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8
+#define USB_EXT_PROP_B_PROPERTY_NAME 10
+#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10
+#define USB_EXT_PROP_B_PROPERTY_DATA 14
+
+#define USB_EXT_PROP_RESERVED 0
+#define USB_EXT_PROP_UNICODE 1
+#define USB_EXT_PROP_UNICODE_ENV 2
+#define USB_EXT_PROP_BINARY 3
+#define USB_EXT_PROP_LE32 4
+#define USB_EXT_PROP_BE32 5
+#define USB_EXT_PROP_UNICODE_LINK 6
+#define USB_EXT_PROP_UNICODE_MULTI 7
+
+static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
+{
+ return buf + offset;
+}
+
+static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
+}
+
+static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
+}
+
+static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
+}
+
+static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
+}
+
+static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf,
+ USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
+}
+
+static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
+}
+
+static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
+{
+ put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
+}
+
+static inline void usb_ext_prop_put_type(u8 *buf, int type)
+{
+ put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
+}
+
+static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
+{
+ int result;
+
+ put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
+ result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN,
+ (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2);
+ if (result < 0)
+ return result;
+
+ put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
+
+ return pnl;
+}
+
+static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data,
+ int data_len)
+{
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+ memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
+}
+
+static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
+ int data_len)
+{
+ int result;
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+ result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN,
+ (wchar_t *) usb_ext_prop_data_ptr(buf, pnl),
+ data_len - 2);
+ if (result < 0)
+ return result;
+
+ put_unaligned_le16(0,
+ &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
+
+ return data_len;
+}
+
+#endif /* __U_OS_DESC_H__ */
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
deleted file mode 100644
index e37a39977a..0000000000
--- a/drivers/usb/gadget/udc-core.c
+++ /dev/null
@@ -1,354 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/**
- * udc.c - Core UDC Framework
- *
- * Copyright (C) 2010 Texas Instruments
- * Author: Felipe Balbi <balbi@ti.com>
- */
-#define VERBOSE_DEBUG
-#include <common.h>
-#include <driver.h>
-#include <init.h>
-#include <poller.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
-
-/**
- * struct usb_udc - describes one usb device controller
- * @driver - the gadget driver pointer. For use by the class code
- * @dev - the child device to the actual controller
- * @gadget - the gadget. For use by the class code
- * @list - for use by the udc class driver
- *
- * This represents the internal data structure which is used by the UDC-class
- * to hold information about udc driver and gadget together.
- */
-struct usb_udc {
- struct usb_gadget_driver *driver;
- struct usb_gadget *gadget;
- struct device_d dev;
- struct list_head list;
- struct poller_struct poller;
-};
-
-static LIST_HEAD(udc_list);
-
-/* ------------------------------------------------------------------------- */
-
-void usb_gadget_set_state(struct usb_gadget *gadget,
- enum usb_device_state state)
-{
- gadget->state = state;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_set_state);
-
-/**
- * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
- * @gadget: The gadget which bus reset occurs
- * @driver: The gadget driver we want to notify
- *
- * If the udc driver has bus reset handler, it needs to call this when the bus
- * reset occurs, it notifies the gadget driver that the bus reset occurs as
- * well as updates gadget state.
- */
-void usb_gadget_udc_reset(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
-{
- usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
-}
-EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
-/* ------------------------------------------------------------------------- */
-
-/**
- * usb_gadget_udc_start - tells usb device controller to start up
- * @gadget: The gadget we want to get started
- * @driver: The driver we want to bind to @gadget
- *
- * This call is issued by the UDC Class driver when it's about
- * to register a gadget driver to the device controller, before
- * calling gadget driver's bind() method.
- *
- * It allows the controller to be powered off until strictly
- * necessary to have it powered on.
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
-{
- return gadget->ops->udc_start(gadget, driver);
-}
-
-/**
- * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
- * @gadget: The device we want to stop activity
- * @driver: The driver to unbind from @gadget
- *
- * This call is issued by the UDC Class driver after calling
- * gadget driver's unbind() method.
- *
- * The details are implementation specific, but it can go as
- * far as powering off UDC completely and disable its data
- * line pullups.
- */
-static inline void usb_gadget_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
-{
- gadget->ops->udc_stop(gadget, driver);
-}
-
-int usb_gadget_poll(void)
-{
- struct usb_udc *udc;
-
- list_for_each_entry(udc, &udc_list, list) {
- if (udc->gadget->ops->udc_poll)
- udc->gadget->ops->udc_poll(udc->gadget);
- }
-
- return 0;
-}
-
-/**
- * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
- * @parent: the parent device to this udc. Usually the controller driver's
- * device.
- * @gadget: the gadget to be added to the list.
- * @release: a gadget release function.
- *
- * Returns zero on success, negative errno otherwise.
- */
-int usb_add_gadget_udc_release(struct device_d *parent, struct usb_gadget *gadget,
- void (*release)(struct device_d *dev))
-{
- struct usb_udc *udc;
- int ret = -ENOMEM;
-
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
- if (!udc)
- goto err1;
-
- dev_set_name(&gadget->dev, "usbgadget");
- gadget->dev.id = DEVICE_ID_SINGLE;
- gadget->dev.parent = parent;
-
- ret = register_device(&gadget->dev);
- if (ret)
- goto err2;
-
- dev_add_param_uint32(&gadget->dev, "product", NULL, NULL,
- &gadget->product_id, "0x%04x", NULL);
- dev_add_param_uint32(&gadget->dev, "vendor", NULL, NULL,
- &gadget->vendor_id, "0x%04x", NULL);
- gadget->manufacturer = xstrdup("barebox");
- dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL,
- &gadget->manufacturer, NULL);
- gadget->productname = xstrdup(barebox_get_model());
- dev_add_param_string(&gadget->dev, "productname", NULL, NULL,
- &gadget->productname, NULL);
- gadget->serialnumber = xstrdup("");
- dev_add_param_string(&gadget->dev, "serialnumber", NULL, NULL,
- &gadget->serialnumber, NULL);
-
- dev_set_name(&udc->dev, "udc");
- udc->dev.id = DEVICE_ID_DYNAMIC;
-
- udc->gadget = gadget;
-
- list_add_tail(&udc->list, &udc_list);
-
- register_device(&udc->dev);
-
- usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
-
- return 0;
-err2:
- kfree(udc);
-
-err1:
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
-
-/**
- * usb_add_gadget_udc - adds a new gadget to the udc class driver list
- * @parent: the parent device to this udc. Usually the controller
- * driver's device.
- * @gadget: the gadget to be added to the list
- *
- * Returns zero on success, negative errno otherwise.
- */
-int usb_add_gadget_udc(struct device_d *parent, struct usb_gadget *gadget)
-{
- return usb_add_gadget_udc_release(parent, gadget, NULL);
-}
-EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
-
-static void usb_gadget_remove_driver(struct usb_udc *udc)
-{
- dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
- udc->gadget->name);
-
- if (udc->gadget->ops->udc_poll)
- poller_unregister(&udc->poller);
-
- usb_gadget_disconnect(udc->gadget);
- udc->driver->disconnect(udc->gadget);
- udc->driver->unbind(udc->gadget);
- usb_gadget_udc_stop(udc->gadget, NULL);
-
- udc->driver = NULL;
- udc->dev.driver = NULL;
- udc->gadget->dev.driver = NULL;
-}
-
-/**
- * usb_del_gadget_udc - deletes @udc from udc_list
- * @gadget: the gadget to be removed.
- *
- * This, will call usb_gadget_unregister_driver() if
- * the @udc is still busy.
- */
-void usb_del_gadget_udc(struct usb_gadget *gadget)
-{
- struct usb_udc *udc = NULL;
-
- list_for_each_entry(udc, &udc_list, list)
- if (udc->gadget == gadget)
- goto found;
-
- dev_err(gadget->dev.parent, "gadget not registered.\n");
-
- return;
-
-found:
- dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
-
- list_del(&udc->list);
-
- if (udc->driver)
- usb_gadget_remove_driver(udc);
-
- unregister_device(&udc->dev);
- unregister_device(&gadget->dev);
-}
-EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
-
-/* ------------------------------------------------------------------------- */
-
-static void udc_poll_driver(struct poller_struct *poller)
-{
- struct usb_udc *udc = container_of(poller, struct usb_udc, poller);
-
- udc->gadget->ops->udc_poll(udc->gadget);
-}
-
-static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
-{
- int ret;
-
- dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
- driver->function);
-
- udc->driver = driver;
- udc->dev.driver = &driver->driver;
- udc->gadget->dev.driver = &driver->driver;
-
- if (udc->gadget->ops->udc_poll) {
- udc->poller.func = udc_poll_driver;
- ret = poller_register(&udc->poller, dev_name(&udc->dev));
- if (ret)
- return ret;
- }
-
- ret = driver->bind(udc->gadget, driver);
- if (ret)
- goto err1;
-
- ret = usb_gadget_udc_start(udc->gadget, driver);
- if (ret) {
- driver->unbind(udc->gadget);
- goto err1;
- }
- usb_gadget_connect(udc->gadget);
-
- return 0;
-err1:
- if (udc->gadget->ops->udc_poll)
- poller_unregister(&udc->poller);
-
- if (ret != -EISNAM)
- dev_err(&udc->dev, "failed to start %s: %d\n",
- udc->driver->function, ret);
- udc->driver = NULL;
- udc->dev.driver = NULL;
- udc->gadget->dev.driver = NULL;
- return ret;
-}
-
-int udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
-{
- struct usb_udc *udc = NULL;
- int ret = -ENODEV;
-
- list_for_each_entry(udc, &udc_list, list) {
- ret = strcmp(name, dev_name(&udc->dev));
- if (!ret)
- break;
- }
- if (ret) {
- ret = -ENODEV;
- goto out;
- }
- if (udc->driver) {
- ret = -EBUSY;
- goto out;
- }
- ret = udc_bind_to_driver(udc, driver);
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(udc_attach_driver);
-
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
-{
- struct usb_udc *udc = NULL;
- int ret;
-
- if (!driver || !driver->bind || !driver->setup)
- return -EINVAL;
-
- list_for_each_entry(udc, &udc_list, list) {
- /* For now we take the first one */
- if (!udc->driver)
- goto found;
- }
-
- pr_debug("couldn't find an available UDC\n");
-
- return -ENODEV;
-found:
- ret = udc_bind_to_driver(udc, driver);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
-
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
-{
- struct usb_udc *udc = NULL;
- int ret = -ENODEV;
-
- if (!driver || !driver->unbind)
- return -EINVAL;
-
- list_for_each_entry(udc, &udc_list, list)
- if (udc->driver == driver) {
- usb_gadget_remove_driver(udc);
- ret = 0;
- break;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
new file mode 100644
index 0000000000..6e79e80cfa
--- /dev/null
+++ b/drivers/usb/gadget/udc/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_USB_GADGET) += core.o
+
+obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o
+obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o
+obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index 80cbd36118..ffd39f489f 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -16,8 +16,8 @@
#include <gpio.h>
#include <io.h>
#include <clock.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
#include <of_gpio.h>
#include <linux/list.h>
@@ -26,11 +26,11 @@
#include <asm/byteorder.h>
-#include <mach/hardware.h>
-#include <mach/at91sam9261.h>
-#include <mach/board.h>
-#include <mach/cpu.h>
-#include <mach/at91sam9261_matrix.h>
+#include <mach/at91/hardware.h>
+#include <mach/at91/at91sam9261.h>
+#include <mach/at91/board.h>
+#include <mach/at91/cpu.h>
+#include <mach/at91/at91sam9261_matrix.h>
#include "at91_udc.h"
@@ -1240,7 +1240,7 @@ static int at91_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *d
return 0;
}
-static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
+static int at91_udc_stop(struct usb_gadget *gadget)
{
struct at91_udc *udc = container_of(gadget, struct at91_udc, gadget);
@@ -1248,7 +1248,8 @@ static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *dr
at91_udp_write(udc, AT91_UDP_IDR, ~0);
udc->driver = NULL;
- DBG(udc, "unbound from %s\n", driver->function);
+ DBG(udc, "unbound\n");
+
return 0;
}
@@ -1389,7 +1390,7 @@ static void __init at91udc_of_init(struct at91_udc *udc, struct device_node *np)
/*-------------------------------------------------------------------------*/
-static int __init at91udc_probe(struct device_d *dev)
+static int __init at91udc_probe(struct device *dev)
{
struct resource *iores;
struct at91_udc *udc = &controller;
@@ -1407,12 +1408,12 @@ static int __init at91udc_probe(struct device_d *dev)
iclk_name = "udc_clk";
fclk_name = "udpck";
} else {
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node) {
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node) {
dev_err(dev, "no DT and no platform_data\n");
return -ENODEV;
}
- at91udc_of_init(udc, dev->device_node);
+ at91udc_of_init(udc, dev->of_node);
iclk_name = "pclk";
fclk_name = "hclk";
}
@@ -1521,8 +1522,9 @@ static const struct of_device_id at91_udc_dt_ids[] = {
{ .compatible = "atmel,at91sam9263-udc" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, at91_udc_dt_ids);
-static struct driver_d at91_udc_driver = {
+static struct driver at91_udc_driver = {
.name = driver_name,
.probe = at91udc_probe,
.of_compatible = DRV_OF_COMPAT(at91_udc_dt_ids),
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
index 9e2b976e57..cecaa5b52e 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/udc/at91_udc.h
@@ -127,7 +127,7 @@ struct at91_udc {
u32 gpio_vbus_val;
struct at91_udc_data board;
struct clk *iclk, *fclk;
- struct device_d *dev;
+ struct device *dev;
void __iomem *udp_baseaddr;
int udp_irq;
};
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
new file mode 100644
index 0000000000..e7cfa0d5d8
--- /dev/null
+++ b/drivers/usb/gadget/udc/core.c
@@ -0,0 +1,1517 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * udc.c - Core UDC Framework
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Felipe Balbi <balbi@ti.com>
+ */
+
+#define pr_fmt(fmt) "UDC core: " fmt
+
+#include <common.h>
+#include <dma.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/ch9.h>
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+#include <linux/err.h>
+
+static int gadget_id_numbers;
+
+static struct bus_type gadget_bus_type;
+
+/**
+ * struct usb_udc - describes one usb device controller
+ * @driver: the gadget driver pointer. For use by the class code
+ * @dev: the child device to the actual controller
+ * @gadget: the gadget. For use by the class code
+ * @list: for use by the udc class driver
+ * @vbus: for udcs who care about vbus status, this value is real vbus status;
+ * for udcs who do not care about vbus status, this value is always true
+ * @started: the UDC's started state. True if the UDC had started.
+ *
+ * This represents the internal data structure which is used by the UDC-class
+ * to hold information about udc driver and gadget together.
+ */
+struct usb_udc {
+ struct usb_gadget_driver *driver;
+ struct usb_gadget *gadget;
+ struct device dev;
+ struct list_head list;
+ bool vbus;
+ bool started;
+ struct poller_struct poller;
+};
+
+static LIST_HEAD(udc_list);
+
+/* Protects udc_list, udc->driver, driver->is_bound, and related calls */
+static DEFINE_MUTEX(udc_lock);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint
+ * @ep:the endpoint being configured
+ * @maxpacket_limit:value of maximum packet size limit
+ *
+ * This function should be used only in UDC drivers to initialize endpoint
+ * (usually in probe function).
+ */
+void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
+ unsigned maxpacket_limit)
+{
+ ep->maxpacket_limit = maxpacket_limit;
+ ep->maxpacket = maxpacket_limit;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
+
+/**
+ * usb_ep_enable - configure endpoint, making it usable
+ * @ep:the endpoint being configured. may not be the endpoint named "ep0".
+ * drivers discover endpoints through the ep_list of a usb_gadget.
+ *
+ * When configurations are set, or when interface settings change, the driver
+ * will enable or disable the relevant endpoints. while it is enabled, an
+ * endpoint may be used for i/o until the driver receives a disconnect() from
+ * the host or until the endpoint is disabled.
+ *
+ * the ep0 implementation (which calls this routine) must ensure that the
+ * hardware capabilities of each endpoint match the descriptor provided
+ * for it. for example, an endpoint named "ep2in-bulk" would be usable
+ * for interrupt transfers as well as bulk, but it likely couldn't be used
+ * for iso transfers or for endpoint 14. some endpoints are fully
+ * configurable, with more generic names like "ep-a". (remember that for
+ * USB, "in" means "towards the USB host".)
+ *
+ * This routine may be called in an atomic (interrupt) context.
+ *
+ * returns zero, or a negative error code.
+ */
+int usb_ep_enable(struct usb_ep *ep)
+{
+ int ret = 0;
+
+ if (ep->enabled)
+ goto out;
+
+ /* UDC drivers can't handle endpoints with maxpacket size 0 */
+ if (usb_endpoint_maxp(ep->desc) == 0) {
+ /*
+ * We should log an error message here, but we can't call
+ * dev_err() because there's no way to find the gadget
+ * given only ep.
+ */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ep->ops->enable(ep, ep->desc);
+ if (ret)
+ goto out;
+
+ ep->enabled = true;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_enable);
+
+/**
+ * usb_ep_disable - endpoint is no longer usable
+ * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0".
+ *
+ * no other task may be using this endpoint when this is called.
+ * any pending and uncompleted requests will complete with status
+ * indicating disconnect (-ESHUTDOWN) before this call returns.
+ * gadget drivers must call usb_ep_enable() again before queueing
+ * requests to the endpoint.
+ *
+ * This routine may be called in an atomic (interrupt) context.
+ *
+ * returns zero, or a negative error code.
+ */
+int usb_ep_disable(struct usb_ep *ep)
+{
+ int ret = 0;
+
+ if (!ep->enabled)
+ goto out;
+
+ ret = ep->ops->disable(ep);
+ if (ret)
+ goto out;
+
+ ep->enabled = false;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_disable);
+
+/**
+ * usb_ep_alloc_request - allocate a request object to use with this endpoint
+ * @ep:the endpoint to be used with with the request
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Request objects must be allocated with this call, since they normally
+ * need controller-specific setup and may even need endpoint-specific
+ * resources such as allocation of DMA descriptors.
+ * Requests may be submitted with usb_ep_queue(), and receive a single
+ * completion callback. Free requests with usb_ep_free_request(), when
+ * they are no longer needed.
+ *
+ * Returns the request, or null if one could not be allocated.
+ */
+struct usb_request *usb_ep_alloc_request(struct usb_ep *ep)
+{
+ struct usb_request *req = NULL;
+
+ req = ep->ops->alloc_request(ep);
+
+ return req;
+}
+EXPORT_SYMBOL_GPL(usb_ep_alloc_request);
+
+/**
+ * usb_ep_free_request - frees a request object
+ * @ep:the endpoint associated with the request
+ * @req:the request being freed
+ *
+ * Reverses the effect of usb_ep_alloc_request().
+ * Caller guarantees the request is not queued, and that it will
+ * no longer be requeued (or otherwise used).
+ */
+void usb_ep_free_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ ep->ops->free_request(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_ep_free_request);
+
+/**
+ * usb_ep_queue - queues (submits) an I/O request to an endpoint.
+ * @ep:the endpoint associated with the request
+ * @req:the request being submitted
+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
+ * pre-allocate all necessary memory with the request.
+ *
+ * This tells the device controller to perform the specified request through
+ * that endpoint (reading or writing a buffer). When the request completes,
+ * including being canceled by usb_ep_dequeue(), the request's completion
+ * routine is called to return the request to the driver. Any endpoint
+ * (except control endpoints like ep0) may have more than one transfer
+ * request queued; they complete in FIFO order. Once a gadget driver
+ * submits a request, that request may not be examined or modified until it
+ * is given back to that driver through the completion callback.
+ *
+ * Each request is turned into one or more packets. The controller driver
+ * never merges adjacent requests into the same packet. OUT transfers
+ * will sometimes use data that's already buffered in the hardware.
+ * Drivers can rely on the fact that the first byte of the request's buffer
+ * always corresponds to the first byte of some USB packet, for both
+ * IN and OUT transfers.
+ *
+ * Bulk endpoints can queue any amount of data; the transfer is packetized
+ * automatically. The last packet will be short if the request doesn't fill it
+ * out completely. Zero length packets (ZLPs) should be avoided in portable
+ * protocols since not all usb hardware can successfully handle zero length
+ * packets. (ZLPs may be explicitly written, and may be implicitly written if
+ * the request 'zero' flag is set.) Bulk endpoints may also be used
+ * for interrupt transfers; but the reverse is not true, and some endpoints
+ * won't support every interrupt transfer. (Such as 768 byte packets.)
+ *
+ * Interrupt-only endpoints are less functional than bulk endpoints, for
+ * example by not supporting queueing or not handling buffers that are
+ * larger than the endpoint's maxpacket size. They may also treat data
+ * toggle differently.
+ *
+ * Control endpoints ... after getting a setup() callback, the driver queues
+ * one response (even if it would be zero length). That enables the
+ * status ack, after transferring data as specified in the response. Setup
+ * functions may return negative error codes to generate protocol stalls.
+ * (Note that some USB device controllers disallow protocol stall responses
+ * in some cases.) When control responses are deferred (the response is
+ * written after the setup callback returns), then usb_ep_set_halt() may be
+ * used on ep0 to trigger protocol stalls. Depending on the controller,
+ * it may not be possible to trigger a status-stage protocol stall when the
+ * data stage is over, that is, from within the response's completion
+ * routine.
+ *
+ * For periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+ * have queued some data to transfer at that time.
+ *
+ * Note that @req's ->complete() callback must never be called from
+ * within usb_ep_queue() as that can create deadlock situations.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code. Endpoints that are not enabled
+ * report errors; errors will also be
+ * reported when the usb peripheral is disconnected.
+ *
+ * If and only if @req is successfully queued (the return value is zero),
+ * @req->complete() will be called exactly once, when the Gadget core and
+ * UDC are finished with the request. When the completion function is called,
+ * control of the request is returned to the device driver which submitted it.
+ * The completion handler may then immediately free or reuse @req.
+ */
+int usb_ep_queue(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ int ret = 0;
+
+ if (WARN_ON_ONCE(!ep->enabled && ep->address)) {
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ ret = ep->ops->queue(ep, req);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_queue);
+
+/**
+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
+ * @ep:the endpoint associated with the request
+ * @req:the request being canceled
+ *
+ * If the request is still active on the endpoint, it is dequeued and
+ * eventually its completion routine is called (with status -ECONNRESET);
+ * else a negative error code is returned. This routine is asynchronous,
+ * that is, it may return before the completion routine runs.
+ *
+ * Note that some hardware can't clear out write fifos (to unlink the request
+ * at the head of the queue) except as part of disconnecting from usb. Such
+ * restrictions prevent drivers from supporting configuration changes,
+ * even to configuration zero (a "chapter 9" requirement).
+ *
+ * This routine may be called in interrupt context.
+ */
+int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ int ret;
+
+ ret = ep->ops->dequeue(ep, req);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_dequeue);
+
+/**
+ * usb_ep_set_halt - sets the endpoint halt feature.
+ * @ep: the non-isochronous endpoint being stalled
+ *
+ * Use this to stall an endpoint, perhaps as an error report.
+ * Except for control endpoints,
+ * the endpoint stays halted (will not stream any data) until the host
+ * clears this feature; drivers may need to empty the endpoint's request
+ * queue first, to make sure no inappropriate transfers happen.
+ *
+ * Note that while an endpoint CLEAR_FEATURE will be invisible to the
+ * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the
+ * current altsetting, see usb_ep_clear_halt(). When switching altsettings,
+ * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code. On success, this call sets
+ * underlying hardware state that blocks data transfers.
+ * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
+ * transfer requests are still queued, or if the controller hardware
+ * (usually a FIFO) still holds bytes that the host hasn't collected.
+ */
+int usb_ep_set_halt(struct usb_ep *ep)
+{
+ int ret;
+
+ ret = ep->ops->set_halt(ep, 1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_halt);
+
+/**
+ * usb_ep_clear_halt - clears endpoint halt, and resets toggle
+ * @ep:the bulk or interrupt endpoint being reset
+ *
+ * Use this when responding to the standard usb "set interface" request,
+ * for endpoints that aren't reconfigured, after clearing any other state
+ * in the endpoint's i/o queue.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code. On success, this call clears
+ * the underlying hardware state reflecting endpoint halt and data toggle.
+ * Note that some hardware can't support this request (like pxa2xx_udc),
+ * and accordingly can't correctly implement interface altsettings.
+ */
+int usb_ep_clear_halt(struct usb_ep *ep)
+{
+ int ret;
+
+ ret = ep->ops->set_halt(ep, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_clear_halt);
+
+/**
+ * usb_ep_set_wedge - sets the halt feature and ignores clear requests
+ * @ep: the endpoint being wedged
+ *
+ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
+ * requests. If the gadget driver clears the halt status, it will
+ * automatically unwedge the endpoint.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_ep_set_wedge(struct usb_ep *ep)
+{
+ int ret;
+
+ if (ep->ops->set_wedge)
+ ret = ep->ops->set_wedge(ep);
+ else
+ ret = ep->ops->set_halt(ep, 1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_wedge);
+
+/**
+ * usb_ep_fifo_status - returns number of bytes in fifo, or error
+ * @ep: the endpoint whose fifo status is being checked.
+ *
+ * FIFO endpoints may have "unclaimed data" in them in certain cases,
+ * such as after aborted transfers. Hosts may not have collected all
+ * the IN data written by the gadget driver (and reported by a request
+ * completion). The gadget driver may not have collected all the data
+ * written OUT to it by the host. Drivers that need precise handling for
+ * fault reporting or recovery may need to use this call.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * This returns the number of such bytes in the fifo, or a negative
+ * errno if the endpoint doesn't use a FIFO or doesn't support such
+ * precise handling.
+ */
+int usb_ep_fifo_status(struct usb_ep *ep)
+{
+ int ret;
+
+ if (ep->ops->fifo_status)
+ ret = ep->ops->fifo_status(ep);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_fifo_status);
+
+/**
+ * usb_ep_fifo_flush - flushes contents of a fifo
+ * @ep: the endpoint whose fifo is being flushed.
+ *
+ * This call may be used to flush the "unclaimed data" that may exist in
+ * an endpoint fifo after abnormal transaction terminations. The call
+ * must never be used except when endpoint is not being used for any
+ * protocol translation.
+ *
+ * This routine may be called in interrupt context.
+ */
+void usb_ep_fifo_flush(struct usb_ep *ep)
+{
+ if (ep->ops->fifo_flush)
+ ep->ops->fifo_flush(ep);
+}
+EXPORT_SYMBOL_GPL(usb_ep_fifo_flush);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_frame_number - returns the current frame number
+ * @gadget: controller that reports the frame number
+ *
+ * Returns the usb frame number, normally eleven bits from a SOF packet,
+ * or negative errno if this device doesn't support this capability.
+ */
+int usb_gadget_frame_number(struct usb_gadget *gadget)
+{
+ int ret;
+
+ ret = gadget->ops->get_frame(gadget);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_frame_number);
+
+/**
+ * usb_gadget_wakeup - tries to wake up the host connected to this gadget
+ * @gadget: controller used to wake up the host
+ *
+ * Returns zero on success, else negative error code if the hardware
+ * doesn't support such attempts, or its support has not been enabled
+ * by the usb host. Drivers must return device descriptors that report
+ * their ability to support this, or hosts won't enable it.
+ *
+ * This may also try to use SRP to wake the host and start enumeration,
+ * even if OTG isn't otherwise in use. OTG devices may also start
+ * remote wakeup even when hosts don't explicitly enable it.
+ */
+int usb_gadget_wakeup(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->wakeup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->wakeup(gadget);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_wakeup);
+
+/**
+ * usb_gadget_set_selfpowered - sets the device selfpowered feature.
+ * @gadget:the device being declared as self-powered
+ *
+ * this affects the device status reported by the hardware driver
+ * to reflect that it now has a local power supply.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->set_selfpowered) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->set_selfpowered(gadget, 1);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered);
+
+/**
+ * usb_gadget_clear_selfpowered - clear the device selfpowered feature.
+ * @gadget:the device being declared as bus-powered
+ *
+ * this affects the device status reported by the hardware driver.
+ * some hardware may not support bus-powered operation, in which
+ * case this feature's value can never change.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->set_selfpowered) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->set_selfpowered(gadget, 0);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered);
+
+/**
+ * usb_gadget_vbus_connect - Notify controller that VBUS is powered
+ * @gadget:The device which now has VBUS power.
+ * Context: can sleep
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session starting. Common responses include
+ * resuming the controller, activating the D+ (or D-) pullup to let the
+ * host detect that a USB device is attached, and starting to draw power
+ * (8mA or possibly more, especially after SET_CONFIGURATION).
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_connect(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->vbus_session) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->vbus_session(gadget, 1);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect);
+
+/**
+ * usb_gadget_vbus_draw - constrain controller's VBUS power usage
+ * @gadget:The device whose VBUS usage is being described
+ * @mA:How much current to draw, in milliAmperes. This should be twice
+ * the value listed in the configuration descriptor bMaxPower field.
+ *
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume. For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ int ret = 0;
+
+ if (!gadget->ops->vbus_draw) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->vbus_draw(gadget, mA);
+ if (!ret)
+ gadget->mA = mA;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw);
+
+/**
+ * usb_gadget_vbus_disconnect - notify controller about VBUS session end
+ * @gadget:the device whose VBUS supply is being described
+ * Context: can sleep
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session ending. Common responses include
+ * reversing everything done in usb_gadget_vbus_connect().
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->vbus_session) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->vbus_session(gadget, 0);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
+
+/**
+ * usb_gadget_connect - software-controlled connect to USB host
+ * @gadget:the peripheral being connected
+ *
+ * Enables the D+ (or potentially D-) pullup. The host will start
+ * enumerating this gadget when the pullup is active and a VBUS session
+ * is active (the link is powered).
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_connect(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->pullup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (gadget->deactivated) {
+ /*
+ * If gadget is deactivated we only save new state.
+ * Gadget will be connected automatically after activation.
+ */
+ gadget->connected = true;
+ goto out;
+ }
+
+ ret = gadget->ops->pullup(gadget, 1);
+ if (!ret)
+ gadget->connected = 1;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_connect);
+
+/**
+ * usb_gadget_disconnect - software-controlled disconnect from USB host
+ * @gadget:the peripheral being disconnected
+ *
+ * Disables the D+ (or potentially D-) pullup, which the host may see
+ * as a disconnect (when a VBUS session is active). Not all systems
+ * support software pullup controls.
+ *
+ * Following a successful disconnect, invoke the ->disconnect() callback
+ * for the current gadget driver so that UDC drivers don't need to.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_disconnect(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->pullup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!gadget->connected)
+ goto out;
+
+ if (gadget->deactivated) {
+ /*
+ * If gadget is deactivated we only save new state.
+ * Gadget will stay disconnected after activation.
+ */
+ gadget->connected = false;
+ goto out;
+ }
+
+ ret = gadget->ops->pullup(gadget, 0);
+ if (!ret)
+ gadget->connected = 0;
+
+ mutex_lock(&udc_lock);
+ if (gadget->udc->driver)
+ gadget->udc->driver->disconnect(gadget);
+ mutex_unlock(&udc_lock);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_disconnect);
+
+/**
+ * usb_gadget_deactivate - deactivate function which is not ready to work
+ * @gadget: the peripheral being deactivated
+ *
+ * This routine may be used during the gadget driver bind() call to prevent
+ * the peripheral from ever being visible to the USB host, unless later
+ * usb_gadget_activate() is called. For example, user mode components may
+ * need to be activated before the system can talk to hosts.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_deactivate(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (gadget->deactivated)
+ goto out;
+
+ if (gadget->connected) {
+ ret = usb_gadget_disconnect(gadget);
+ if (ret)
+ goto out;
+
+ /*
+ * If gadget was being connected before deactivation, we want
+ * to reconnect it in usb_gadget_activate().
+ */
+ gadget->connected = true;
+ }
+ gadget->deactivated = true;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_deactivate);
+
+/**
+ * usb_gadget_activate - activate function which is not ready to work
+ * @gadget: the peripheral being activated
+ *
+ * This routine activates gadget which was previously deactivated with
+ * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_activate(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->deactivated)
+ goto out;
+
+ gadget->deactivated = false;
+
+ /*
+ * If gadget has been connected before deactivation, or became connected
+ * while it was being deactivated, we call usb_gadget_connect().
+ */
+ if (gadget->connected)
+ ret = usb_gadget_connect(gadget);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_activate);
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef CONFIG_HAS_DMA
+
+int usb_gadget_map_request_by_dev(struct device *dev,
+ struct usb_request *req, int is_in)
+{
+ if (req->length == 0)
+ return 0;
+
+ req->dma = dma_map_single(dev, req->buf, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, req->dma)) {
+ dev_err(dev, "failed to map buffer\n");
+ return -EFAULT;
+ }
+
+ req->dma_mapped = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev);
+
+int usb_gadget_map_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
+{
+ return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_map_request);
+
+void usb_gadget_unmap_request_by_dev(struct device *dev,
+ struct usb_request *req, int is_in)
+{
+ if (req->length == 0)
+ return;
+
+ if (req->dma_mapped) {
+ dma_unmap_single(dev, req->dma, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->dma_mapped = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
+
+void usb_gadget_unmap_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
+{
+ usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
+
+#endif /* CONFIG_HAS_DMA */
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_giveback_request - give the request back to the gadget layer
+ * @ep: the endpoint to be used with with the request
+ * @req: the request being given back
+ *
+ * This is called by device controller drivers in order to return the
+ * completed request back to the gadget layer.
+ */
+void usb_gadget_giveback_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ req->complete(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * gadget_find_ep_by_name - returns ep whose name is the same as sting passed
+ * in second parameter or NULL if searched endpoint not found
+ * @g: controller to check for quirk
+ * @name: name of searched endpoint
+ */
+struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name)
+{
+ struct usb_ep *ep;
+
+ gadget_for_each_ep(ep, g) {
+ if (!strcmp(ep->name, name))
+ return ep;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gadget_find_ep_by_name);
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
+ struct usb_ep *ep, struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp)
+{
+ u8 type;
+ u16 max;
+ int num_req_streams = 0;
+
+ /* endpoint already claimed? */
+ if (ep->claimed)
+ return 0;
+
+ type = usb_endpoint_type(desc);
+ max = usb_endpoint_maxp(desc);
+
+ if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
+ return 0;
+ if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
+ return 0;
+
+ if (max > ep->maxpacket_limit)
+ return 0;
+
+ /* "high bandwidth" works only at high speed */
+ if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1)
+ return 0;
+
+ switch (type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* only support ep0 for portable CONTROL traffic */
+ return 0;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!ep->caps.type_iso)
+ return 0;
+ /* ISO: limit 1023 bytes full speed, 1024 high/super speed */
+ if (!gadget_is_dualspeed(gadget) && max > 1023)
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if (!ep->caps.type_bulk)
+ return 0;
+ if (ep_comp && gadget_is_superspeed(gadget)) {
+ /* Get the number of required streams from the
+ * EP companion descriptor and see if the EP
+ * matches it
+ */
+ num_req_streams = ep_comp->bmAttributes & 0x1f;
+ if (num_req_streams > ep->max_streams)
+ return 0;
+ }
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ /* Bulk endpoints handle interrupt transfers,
+ * except the toggle-quirky iso-synch kind
+ */
+ if (!ep->caps.type_int && !ep->caps.type_bulk)
+ return 0;
+ /* INT: limit 64 bytes full speed, 1024 high/super speed */
+ if (!gadget_is_dualspeed(gadget) && max > 64)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
+
+/**
+ * usb_gadget_check_config - checks if the UDC can support the binded
+ * configuration
+ * @gadget: controller to check the USB configuration
+ *
+ * Ensure that a UDC is able to support the requested resources by a
+ * configuration, and that there are no resource limitations, such as
+ * internal memory allocated to all requested endpoints.
+ *
+ * Returns zero on success, else a negative errno.
+ */
+int usb_gadget_check_config(struct usb_gadget *gadget)
+{
+ if (gadget->ops->check_config)
+ return gadget->ops->check_config(gadget);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_check_config);
+
+/* ------------------------------------------------------------------------- */
+
+void usb_gadget_set_state(struct usb_gadget *gadget,
+ enum usb_device_state state)
+{
+ gadget->state = state;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_state);
+
+/* ------------------------------------------------------------------------- */
+
+static void usb_udc_connect_control(struct usb_udc *udc)
+{
+ if (udc->vbus)
+ usb_gadget_connect(udc->gadget);
+ else
+ usb_gadget_disconnect(udc->gadget);
+}
+
+/**
+ * usb_udc_vbus_handler - updates the udc core vbus status, and try to
+ * connect or disconnect gadget
+ * @gadget: The gadget which vbus change occurs
+ * @status: The vbus status
+ *
+ * The udc driver calls it when it wants to connect or disconnect gadget
+ * according to vbus status.
+ */
+void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
+{
+ struct usb_udc *udc = gadget->udc;
+
+ if (udc) {
+ udc->vbus = status;
+ usb_udc_connect_control(udc);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
+
+/**
+ * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
+ * @gadget: The gadget which bus reset occurs
+ * @driver: The gadget driver we want to notify
+ *
+ * If the udc driver has bus reset handler, it needs to call this when the bus
+ * reset occurs, it notifies the gadget driver that the bus reset occurs as
+ * well as updates gadget state.
+ */
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ driver->reset(gadget);
+ usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
+
+/**
+ * usb_gadget_udc_start - tells usb device controller to start up
+ * @udc: The UDC to be started
+ *
+ * This call is issued by the UDC Class driver when it's about
+ * to register a gadget driver to the device controller, before
+ * calling gadget driver's bind() method.
+ *
+ * It allows the controller to be powered off until strictly
+ * necessary to have it powered on.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int usb_gadget_udc_start(struct usb_udc *udc)
+{
+ int ret;
+
+ if (udc->started) {
+ dev_err(&udc->dev, "UDC had already started\n");
+ return -EBUSY;
+ }
+
+ ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver);
+ if (!ret)
+ udc->started = true;
+
+ return ret;
+}
+
+/**
+ * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
+ * @udc: The UDC to be stopped
+ *
+ * This call is issued by the UDC Class driver after calling
+ * gadget driver's unbind() method.
+ *
+ * The details are implementation specific, but it can go as
+ * far as powering off UDC completely and disable its data
+ * line pullups.
+ */
+static inline void usb_gadget_udc_stop(struct usb_udc *udc)
+{
+ if (!udc->started) {
+ dev_err(&udc->dev, "UDC had already stopped\n");
+ return;
+ }
+
+ udc->gadget->ops->udc_stop(udc->gadget);
+ udc->started = false;
+}
+
+/**
+ * usb_gadget_udc_set_speed - tells usb device controller speed supported by
+ * current driver
+ * @udc: The device we want to set maximum speed
+ * @speed: The maximum speed to allowed to run
+ *
+ * This call is issued by the UDC Class driver before calling
+ * usb_gadget_udc_start() in order to make sure that we don't try to
+ * connect on speeds the gadget driver doesn't support.
+ */
+static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
+ enum usb_device_speed speed)
+{
+ struct usb_gadget *gadget = udc->gadget;
+ enum usb_device_speed s;
+
+ if (speed == USB_SPEED_UNKNOWN)
+ s = gadget->max_speed;
+ else
+ s = min(speed, gadget->max_speed);
+
+ if (s == USB_SPEED_SUPER_PLUS && gadget->ops->udc_set_ssp_rate)
+ gadget->ops->udc_set_ssp_rate(gadget, gadget->max_ssp_rate);
+ else if (gadget->ops->udc_set_speed)
+ gadget->ops->udc_set_speed(gadget, s);
+}
+
+/**
+ * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks
+ * @udc: The UDC which should enable async callbacks
+ *
+ * This routine is used when binding gadget drivers. It undoes the effect
+ * of usb_gadget_disable_async_callbacks(); the UDC driver should enable IRQs
+ * (if necessary) and resume issuing callbacks.
+ *
+ * This routine will always be called in process context.
+ */
+static inline void usb_gadget_enable_async_callbacks(struct usb_udc *udc)
+{
+ struct usb_gadget *gadget = udc->gadget;
+
+ if (gadget->ops->udc_async_callbacks)
+ gadget->ops->udc_async_callbacks(gadget, true);
+}
+
+/**
+ * usb_gadget_disable_async_callbacks - tell usb device controller to disable asynchronous callbacks
+ * @udc: The UDC which should disable async callbacks
+ *
+ * This routine is used when unbinding gadget drivers. It prevents a race:
+ * The UDC driver doesn't know when the gadget driver's ->unbind callback
+ * runs, so unless it is told to disable asynchronous callbacks, it might
+ * issue a callback (such as ->disconnect) after the unbind has completed.
+ *
+ * After this function runs, the UDC driver must suppress all ->suspend,
+ * ->resume, ->disconnect, ->reset, and ->setup callbacks to the gadget driver
+ * until async callbacks are again enabled. A simple-minded but effective
+ * way to accomplish this is to tell the UDC hardware not to generate any
+ * more IRQs.
+ *
+ * Request completion callbacks must still be issued. However, it's okay
+ * to defer them until the request is cancelled, since the pull-up will be
+ * turned off during the time period when async callbacks are disabled.
+ *
+ * This routine will always be called in process context.
+ */
+static inline void usb_gadget_disable_async_callbacks(struct usb_udc *udc)
+{
+ struct usb_gadget *gadget = udc->gadget;
+
+ if (gadget->ops->udc_async_callbacks)
+ gadget->ops->udc_async_callbacks(gadget, false);
+}
+
+/**
+ * usb_initialize_gadget - initialize a gadget and its embedded struct device
+ * @parent: the parent device to this udc. Usually the controller driver's
+ * device.
+ * @gadget: the gadget to be initialized.
+ * @release: a gadget release function.
+ */
+void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
+{
+ gadget->dev.parent = parent;
+ gadget->dev.bus = &gadget_bus_type;
+}
+EXPORT_SYMBOL_GPL(usb_initialize_gadget);
+
+/**
+ * usb_add_gadget - adds a new gadget to the udc class driver list
+ * @gadget: the gadget to be added to the list.
+ *
+ * Returns zero on success, negative errno otherwise.
+ * Does not do a final usb_put_gadget() if an error occurs.
+ */
+int usb_add_gadget(struct usb_gadget *gadget)
+{
+ struct usb_udc *udc;
+ int ret = -ENOMEM;
+
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ goto error;
+
+ udc->dev.parent = gadget->dev.parent;
+ ret = dev_set_name(&udc->dev, "usbgadget");
+ if (ret)
+ goto err_put_udc;
+
+ udc->gadget = gadget;
+ gadget->udc = udc;
+
+ udc->started = false;
+
+ mutex_lock(&udc_lock);
+ list_add_tail(&udc->list, &udc_list);
+ mutex_unlock(&udc_lock);
+
+ ret = register_device(&udc->dev);
+ if (ret)
+ goto err_unlist_udc;
+
+ usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+ udc->vbus = true;
+
+ ret = gadget_id_numbers++;
+ if (ret < 0)
+ goto err_del_udc;
+ gadget->id_number = ret;
+ dev_set_name(&gadget->dev, "gadget");
+ gadget->dev.id = ret;
+
+ ret = register_device(&gadget->dev);
+ if (ret)
+ goto err_free_id;
+
+ dev_add_param_uint32(&gadget->dev, "product", NULL, NULL,
+ &gadget->product_id, "0x%04x", NULL);
+ dev_add_param_uint32(&gadget->dev, "vendor", NULL, NULL,
+ &gadget->vendor_id, "0x%04x", NULL);
+ gadget->manufacturer = xstrdup("barebox");
+ dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL,
+ &gadget->manufacturer, NULL);
+ gadget->productname = xstrdup(barebox_get_model());
+ dev_add_param_string(&gadget->dev, "productname", NULL, NULL,
+ &gadget->productname, NULL);
+ gadget->serialnumber = xstrdup(barebox_get_serial_number() ? : "unset");
+ dev_add_param_string(&gadget->dev, "serialnumber", NULL, NULL,
+ &gadget->serialnumber, NULL);
+
+ return 0;
+
+ err_free_id:
+ err_del_udc:
+ unregister_device(&udc->dev);
+
+ err_unlist_udc:
+ mutex_lock(&udc_lock);
+ list_del(&udc->list);
+ mutex_unlock(&udc_lock);
+
+ err_put_udc:
+ error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget);
+
+int usb_gadget_poll(void)
+{
+ struct usb_udc *udc;
+
+ list_for_each_entry(udc, &udc_list, list) {
+ if (udc->gadget->ops->udc_poll)
+ udc->gadget->ops->udc_poll(udc->gadget);
+ }
+
+ return 0;
+}
+
+/**
+ * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller driver's
+ * device.
+ * @gadget: the gadget to be added to the list.
+ * @release: a gadget release function.
+ *
+ * Returns zero on success, negative errno otherwise.
+ * Calls the gadget release function in the latter case.
+ */
+int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
+{
+ int ret;
+
+ usb_initialize_gadget(parent, gadget, release);
+ ret = usb_add_gadget(gadget);
+ if (ret)
+ usb_put_gadget(gadget);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
+
+/**
+ * usb_get_gadget_udc_name - get the name of the first UDC controller
+ * This functions returns the name of the first UDC controller in the system.
+ * Please note that this interface is usefull only for legacy drivers which
+ * assume that there is only one UDC controller in the system and they need to
+ * get its name before initialization. There is no guarantee that the UDC
+ * of the returned name will be still available, when gadget driver registers
+ * itself.
+ *
+ * Returns pointer to string with UDC controller name on success, NULL
+ * otherwise. Caller should kfree() returned string.
+ */
+char *usb_get_gadget_udc_name(void)
+{
+ struct usb_udc *udc;
+ char *name = NULL;
+
+ /* For now we take the first available UDC */
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list) {
+ if (!udc->driver) {
+ name = kstrdup(udc->gadget->name, GFP_KERNEL);
+ break;
+ }
+ }
+ mutex_unlock(&udc_lock);
+ return name;
+}
+EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name);
+
+/**
+ * usb_add_gadget_udc - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller
+ * driver's device.
+ * @gadget: the gadget to be added to the list
+ *
+ * Returns zero on success, negative errno otherwise.
+ */
+int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
+{
+ return usb_add_gadget_udc_release(parent, gadget, NULL);
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
+
+/**
+ * usb_del_gadget - deletes a gadget and unregisters its udc
+ * @gadget: the gadget to be deleted.
+ *
+ * This will unbind @gadget, if it is bound.
+ * It will not do a final usb_put_gadget().
+ */
+void usb_del_gadget(struct usb_gadget *gadget)
+{
+ struct usb_udc *udc = gadget->udc;
+
+ if (!udc)
+ return;
+
+ dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+
+ mutex_lock(&udc_lock);
+ list_del(&udc->list);
+ mutex_unlock(&udc_lock);
+
+ unregister_device(&gadget->dev);
+ unregister_device(&udc->dev);
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget);
+
+/**
+ * usb_del_gadget_udc - unregisters a gadget
+ * @gadget: the gadget to be unregistered.
+ *
+ * Calls usb_del_gadget() and does a final usb_put_gadget().
+ */
+void usb_del_gadget_udc(struct usb_gadget *gadget)
+{
+ usb_del_gadget(gadget);
+ usb_put_gadget(gadget);
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
+
+/* ------------------------------------------------------------------------- */
+
+static int gadget_match_driver(struct device *dev, struct driver *drv)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ struct usb_gadget_driver *driver = container_of(drv,
+ struct usb_gadget_driver, driver);
+
+ /* If the driver specifies a udc_name, it must match the UDC's name */
+ if (driver->udc_name &&
+ strcmp(driver->udc_name, dev_name(&udc->dev)) != 0)
+ return -1;
+
+ /* If the driver is already bound to a gadget, it doesn't match */
+ if (driver->is_bound)
+ return -1;
+
+ /* Otherwise any gadget driver matches any UDC */
+ return 0;
+}
+
+static void udc_poll_driver(struct poller_struct *poller)
+{
+ struct usb_udc *udc = container_of(poller, struct usb_udc, poller);
+
+ udc->gadget->ops->udc_poll(udc->gadget);
+}
+
+static int gadget_bind_driver(struct device *dev)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ struct usb_gadget_driver *driver = container_of(dev->driver,
+ struct usb_gadget_driver, driver);
+ int ret = 0;
+
+ mutex_lock(&udc_lock);
+ if (driver->is_bound) {
+ mutex_unlock(&udc_lock);
+ return -ENXIO; /* Driver binds to only one gadget */
+ }
+ driver->is_bound = true;
+ udc->driver = driver;
+ mutex_unlock(&udc_lock);
+
+ dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function);
+
+ usb_gadget_udc_set_speed(udc, driver->max_speed);
+
+ if (udc->gadget->ops->udc_poll) {
+ udc->poller.func = udc_poll_driver;
+ ret = poller_register(&udc->poller, dev_name(&udc->dev));
+ if (ret)
+ return ret;
+ }
+
+ ret = driver->bind(udc->gadget, driver);
+ if (ret)
+ goto err_bind;
+
+ ret = usb_gadget_udc_start(udc);
+ if (ret)
+ goto err_start;
+ usb_gadget_enable_async_callbacks(udc);
+ usb_udc_connect_control(udc);
+
+ return 0;
+
+ err_start:
+ driver->unbind(udc->gadget);
+
+ err_bind:
+ if (ret != -EISNAM)
+ dev_err(&udc->dev, "failed to start %s: %d\n",
+ driver->function, ret);
+
+ if (udc->gadget->ops->udc_poll)
+ poller_unregister(&udc->poller);
+
+ mutex_lock(&udc_lock);
+ udc->driver = NULL;
+ driver->is_bound = false;
+ mutex_unlock(&udc_lock);
+
+ return ret;
+}
+
+static void gadget_unbind_driver(struct device *dev)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ struct usb_gadget_driver *driver = udc->driver;
+
+ dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
+
+ if (udc->gadget->ops->udc_poll)
+ poller_unregister(&udc->poller);
+
+ usb_gadget_disconnect(gadget);
+ usb_gadget_disable_async_callbacks(udc);
+ udc->driver->unbind(gadget);
+ usb_gadget_udc_stop(udc);
+
+ mutex_lock(&udc_lock);
+ driver->is_bound = false;
+ udc->driver = NULL;
+ mutex_unlock(&udc_lock);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int ret;
+
+ if (!driver || !driver->bind || !driver->setup)
+ return -EINVAL;
+
+ driver->driver.bus = &gadget_bus_type;
+ ret = register_driver(&driver->driver);
+ if (ret) {
+ pr_warn("%s: driver registration failed: %d\n",
+ driver->function, ret);
+ return ret;
+ }
+
+ mutex_lock(&udc_lock);
+ if (!driver->is_bound) {
+ if (driver->match_existing_only) {
+ pr_warn("%s: couldn't find an available UDC or it's busy\n",
+ driver->function);
+ ret = -EBUSY;
+ } else {
+ pr_info("%s: couldn't find an available UDC\n",
+ driver->function);
+ ret = 0;
+ }
+ }
+ mutex_unlock(&udc_lock);
+
+ if (ret)
+ unregister_driver(&driver->driver);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ if (!driver || !driver->unbind)
+ return -EINVAL;
+
+ unregister_driver(&driver->driver);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+/* ------------------------------------------------------------------------- */
+
+static struct bus_type gadget_bus_type = {
+ .name = "gadget",
+ .probe = gadget_bind_driver,
+ .remove = gadget_unbind_driver,
+ .match = gadget_match_driver,
+};
+
+static int usb_udc_init(void)
+{
+ bus_register(&gadget_bus_type);
+
+ return 0;
+}
+coredevice_initcall(usb_udc_init);
diff --git a/drivers/usb/gadget/fsl_udc.c b/drivers/usb/gadget/udc/fsl_udc.c
index e8bdab5765..41de44b30d 100644
--- a/drivers/usb/gadget/fsl_udc.c
+++ b/drivers/usb/gadget/udc/fsl_udc.c
@@ -4,9 +4,9 @@
#include <dma.h>
#include <init.h>
#include <clock.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
-#include <usb/fsl_usb2.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/fsl_usb2.h>
#include <io.h>
#include <asm/byteorder.h>
#include <linux/err.h>
@@ -197,8 +197,8 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
dma_free_coherent(curr_td, 0, sizeof(struct ep_td_struct));
}
- dma_sync_single_for_cpu((unsigned long)req->req.buf, req->req.length,
- DMA_BIDIRECTIONAL);
+ dma_sync_single_for_cpu(udc->gadget.dev.parent, (unsigned long)req->req.buf,
+ req->req.length, DMA_BIDIRECTIONAL);
if (status && (status != -ESHUTDOWN))
VDBG("complete %s req %p stat %d len %u/%u",
@@ -885,8 +885,8 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req)
req->ep = ep;
- dma_sync_single_for_device((unsigned long)req->req.buf, req->req.length,
- DMA_BIDIRECTIONAL);
+ dma_sync_single_for_device(udc->gadget.dev.parent, (unsigned long)req->req.buf,
+ req->req.length, DMA_BIDIRECTIONAL);
req->req.status = -EINPROGRESS;
req->req.actual = 0;
@@ -980,7 +980,8 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
done(ep, req, -ECONNRESET);
/* Enable EP */
-out: epctrl = readl(&dr_regs->endptctrl[ep_num]);
+out:
+ epctrl = readl(&dr_regs->endptctrl[ep_num]);
if (ep_is_in(ep))
epctrl |= EPCTRL_TX_ENABLE;
else
@@ -1646,7 +1647,7 @@ static int fsl_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *dr
}
/* Disconnect from gadget driver */
-static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
+static int fsl_udc_stop(struct usb_gadget *gadget)
{
struct fsl_udc *udc = to_fsl_udc(gadget);
struct fsl_ep *loop_ep;
@@ -1670,7 +1671,7 @@ static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *dri
}
static int struct_udc_setup(struct fsl_udc *udc,
- struct device_d *dev)
+ struct device *dev)
{
struct fsl_usb2_platform_data *pdata = dev->platform_data;
size_t size;
@@ -1853,7 +1854,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
return 0;
}
-struct fsl_udc *ci_udc_register(struct device_d *dev, void __iomem *regs)
+struct fsl_udc *ci_udc_register(struct device *dev, void __iomem *regs)
{
struct fsl_udc *udc_controller;
int ret, i;
@@ -1935,7 +1936,7 @@ void ci_udc_unregister(struct fsl_udc *udc)
free(udc);
}
-static int fsl_udc_probe(struct device_d *dev)
+static int fsl_udc_probe(struct device *dev)
{
struct fsl_udc *udc;
void __iomem *regs = dev_request_mem_region(dev, 0);
@@ -1952,14 +1953,14 @@ static int fsl_udc_probe(struct device_d *dev)
return 0;
}
-static void fsl_udc_remove(struct device_d *dev)
+static void fsl_udc_remove(struct device *dev)
{
struct fsl_udc *udc = dev->priv;
ci_udc_unregister(udc);
}
-static struct driver_d fsl_udc_driver = {
+static struct driver fsl_udc_driver = {
.name = "fsl-udc",
.probe = fsl_udc_probe,
.remove = fsl_udc_remove,
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index e598057564..20148f4878 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -14,12 +14,12 @@
#include <gpio.h>
#include <init.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
#include "pxa27x_udc.h"
-#include <mach/udc_pxa2xx.h>
-#include <mach/pxa-regs.h>
+#include <mach/pxa/udc_pxa2xx.h>
+#include <mach/pxa/pxa-regs.h>
#define DRIVER_VERSION "2008-04-18"
#define DRIVER_DESC "PXA 27x USB Device Controller driver"
@@ -869,7 +869,7 @@ static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
}
static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
-static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
+static int pxa_udc_stop(struct usb_gadget *gadget);
static void pxa_udc_gadget_poll(struct usb_gadget *gadget);
static const struct usb_gadget_ops pxa_udc_ops = {
@@ -882,12 +882,12 @@ static const struct usb_gadget_ops pxa_udc_ops = {
.udc_poll = pxa_udc_gadget_poll,
};
-static void clk_enable(void)
+static void usb_clk_enable(void)
{
CKEN |= CKEN_USB;
}
-static void clk_disable(void)
+static void usb_clk_disable(void)
{
CKEN &= ~CKEN_USB;
}
@@ -901,7 +901,7 @@ static void udc_disable(struct pxa_udc *udc)
udc_writel(udc, UDCICR1, 0);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
- clk_disable();
+ usb_clk_disable();
ep0_idle(udc);
udc->gadget.speed = USB_SPEED_UNKNOWN;
@@ -946,7 +946,7 @@ static void udc_enable(struct pxa_udc *udc)
udc_writel(udc, UDCICR1, 0);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
- clk_enable();
+ usb_clk_enable();
ep0_idle(udc);
udc->gadget.speed = USB_SPEED_FULL;
@@ -987,36 +987,26 @@ static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *dr
return 0;
}
-static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
+static void stop_activity(struct pxa_udc *udc)
{
int i;
- /* don't disconnect drivers more than once */
- if (udc->gadget.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
udc->gadget.speed = USB_SPEED_UNKNOWN;
for (i = 0; i < NR_USB_ENDPOINTS; i++)
pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep);
-
- if (driver)
- driver->disconnect(&udc->gadget);
}
-static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
+static int pxa_udc_stop(struct usb_gadget *gadget)
{
struct pxa_udc *udc = the_controller;
if (!udc)
return -ENODEV;
- if (!driver || driver != udc->driver || !driver->unbind)
- return -EINVAL;
- stop_activity(udc, driver);
+ stop_activity(udc);
udc_disable(udc);
- driver->disconnect(&udc->gadget);
- driver->unbind(&udc->gadget);
udc->driver = NULL;
/*
@@ -1348,7 +1338,7 @@ static void irq_udc_reset(struct pxa_udc *udc)
if ((udccr & UDCCR_UDA) == 0) {
dev_dbg(udc->dev, "USB reset start\n");
- stop_activity(udc, udc->driver);
+ stop_activity(udc);
}
udc->gadget.speed = USB_SPEED_FULL;
@@ -1369,7 +1359,7 @@ static void pxa_udc_gadget_poll(struct usb_gadget *gadget)
if (should_enable_udc(udc))
udc_enable(udc);
if (should_disable_udc(udc)) {
- stop_activity(udc, udc->driver);
+ stop_activity(udc);
udc_disable(udc);
}
@@ -1437,7 +1427,7 @@ static struct pxa_udc memory = {
}
};
-static int __init pxa_udc_probe(struct device_d *dev)
+static int __init pxa_udc_probe(struct device *dev)
{
struct resource *iores;
struct pxa_udc *udc = &memory;
@@ -1472,7 +1462,7 @@ static int __init pxa_udc_probe(struct device_d *dev)
#define pxa27x_clear_otgph() do {} while (0)
-static struct driver_d udc_driver = {
+static struct driver udc_driver = {
.name = "pxa27x-udc",
.probe = pxa_udc_probe,
};
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index b7f05f46f9..80a93cdcf9 100644
--- a/drivers/usb/gadget/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
@@ -336,7 +336,7 @@ struct pxa_udc {
void __iomem *regs;
int irq;
struct clk *clk;
- struct device_d *dev;
+ struct device *dev;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 474c666e53..58eb28ad1a 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -6,8 +6,8 @@
#include <common.h>
#include <errno.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
static inline void put_unaligned_le16(u16 val, u8 *p)
{
@@ -97,7 +97,7 @@ fail:
* characters (which are also widely used in C strings).
*/
int
-usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf)
{
struct usb_string *s;
int len;
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index ba24709e13..f176babfa7 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -8,9 +8,9 @@
#include <linux/err.h>
#include <driver.h>
#include <init.h>
-#include <usb/usb.h>
-#include <usb/usb_defs.h>
-#include <usb/ehci.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usb_defs.h>
+#include <linux/usb/ehci.h>
#include <errno.h>
#include <io.h>
@@ -18,7 +18,7 @@
struct atmel_ehci_priv {
struct ehci_host *ehci;
- struct device_d *dev;
+ struct device *dev;
struct clk *iclk;
struct clk *uclk;
};
@@ -47,7 +47,7 @@ static void atmel_stop_clock(struct atmel_ehci_priv *atehci)
clk_disable(atehci->uclk);
}
-static int atmel_ehci_probe(struct device_d *dev)
+static int atmel_ehci_probe(struct device *dev)
{
int ret;
struct resource *iores;
@@ -56,7 +56,7 @@ static int atmel_ehci_probe(struct device_d *dev)
const char *uclk_name;
struct ehci_host *ehci;
- uclk_name = (dev->device_node) ? "usb_clk" : "uhpck";
+ uclk_name = (dev->of_node) ? "usb_clk" : "uhpck";
atehci = xzalloc(sizeof(*atehci));
atehci->dev = dev;
@@ -97,7 +97,7 @@ static int atmel_ehci_probe(struct device_d *dev)
return 0;
}
-static void atmel_ehci_remove(struct device_d *dev)
+static void atmel_ehci_remove(struct device *dev)
{
struct atmel_ehci_priv *atehci = dev->priv;
@@ -113,8 +113,9 @@ static const struct of_device_id atmel_ehci_dt_ids[] = {
{ .compatible = "atmel,at91sam9g45-ehci" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids);
-static struct driver_d atmel_ehci_driver = {
+static struct driver atmel_ehci_driver = {
.name = "atmel-ehci",
.probe = atmel_ehci_probe,
.remove = atmel_ehci_remove,
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 068504557b..7ae3a285a0 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -10,7 +10,7 @@
#include <common.h>
#include <dma.h>
#include <asm/byteorder.h>
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
#include <io.h>
#include <malloc.h>
#include <driver.h>
@@ -19,7 +19,7 @@
#include <clock.h>
#include <errno.h>
#include <of.h>
-#include <usb/ehci.h>
+#include <linux/usb/ehci.h>
#include <linux/err.h>
#include <linux/sizes.h>
#include <linux/clk.h>
@@ -29,7 +29,7 @@
struct ehci_host {
int rootdev;
- struct device_d *dev;
+ struct device *dev;
struct ehci_hccr *hccr;
struct ehci_hcor *hcor;
struct usb_host host;
@@ -242,7 +242,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz)
return 0;
}
-static int ehci_prepare_qtd(struct device_d *dev,
+static int ehci_prepare_qtd(struct device *dev,
struct qTD *td, uint32_t token,
void *buffer, size_t length,
dma_addr_t *buffer_dma,
@@ -510,7 +510,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
}
#if defined(CONFIG_MACH_EFIKA_MX_SMARTBOOK) && defined(CONFIG_USB_ULPI)
-#include <usb/ulpi.h>
+#include <linux/usb/ulpi.h>
/*
* Add support for setting CHRGVBUS to workaround a hardware bug on efika mx/sb
* boards.
@@ -1341,7 +1341,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
return result;
}
-struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data)
+struct ehci_host *ehci_register(struct device *dev, struct ehci_data *data)
{
struct usb_host *host;
struct ehci_host *ehci;
@@ -1398,12 +1398,12 @@ void ehci_unregister(struct ehci_host *ehci)
free(ehci);
}
-static int ehci_probe(struct device_d *dev)
+static int ehci_probe(struct device *dev)
{
struct resource *iores;
struct ehci_data data = {};
struct ehci_platform_data *pdata = dev->platform_data;
- struct device_node *dn = dev->device_node;
+ struct device_node *dn = dev->of_node;
struct ehci_host *ehci;
struct clk_bulk_data *clks;
int num_clocks, ret;
@@ -1465,7 +1465,7 @@ static int ehci_probe(struct device_d *dev)
return 0;
}
-static void ehci_remove(struct device_d *dev)
+static void ehci_remove(struct device *dev)
{
struct ehci_host *ehci = dev->priv;
@@ -1479,8 +1479,9 @@ static __maybe_unused struct of_device_id ehci_platform_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ehci_platform_dt_ids);
-static struct driver_d ehci_driver = {
+static struct driver ehci_driver = {
.name = "ehci",
.probe = ehci_probe,
.remove = ehci_remove,
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 858dc55f32..e364796a1a 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -10,16 +10,16 @@
/*-------------------------------------------------------------------------*/
#include <mfd/twl4030.h>
-#include <usb/twl4030.h>
-#include <mach/ehci.h>
+#include <linux/usb/twl4030.h>
+#include <mach/omap/ehci.h>
#include <common.h>
#include <io.h>
#include <clock.h>
#include <gpio.h>
-#include <mach/omap3-silicon.h>
-#include <mach/omap3-clock.h>
-#include <mach/cm-regbits-34xx.h>
-#include <mach/sys_info.h>
+#include <mach/omap/omap3-silicon.h>
+#include <mach/omap/omap3-clock.h>
+#include <mach/omap/cm-regbits-34xx.h>
+#include <mach/omap/sys_info.h>
void omap_usb_utmi_init(struct omap_hcd *omap, u8 tll_channel_mask)
{
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index a77e2cae14..867c0977be 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -7,14 +7,14 @@
#include <linux/clk.h>
#include <driver.h>
#include <init.h>
-#include <usb/usb.h>
-#include <usb/usb_defs.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usb_defs.h>
#include <errno.h>
#include <gpio.h>
#include <of_gpio.h>
#include <io.h>
-#include <mach/board.h>
+#include <mach/at91/board.h>
#include "ohci.h"
@@ -23,7 +23,7 @@
struct ohci_at91_priv {
- struct device_d *dev;
+ struct device *dev;
struct clk *iclk;
struct clk *fclk;
struct ohci_regs __iomem *regs;
@@ -54,13 +54,13 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91)
clk_disable(ohci_at91->iclk);
}
-static int at91_ohci_probe_dt(struct device_d *dev)
+static int at91_ohci_probe_dt(struct device *dev)
{
u32 ports;
int i, ret, gpio;
enum of_gpio_flags flags;
struct at91_usbh_data *pdata;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
pdata = xzalloc(sizeof(*pdata));
dev->platform_data = pdata;
@@ -107,7 +107,7 @@ static int at91_ohci_probe_dt(struct device_d *dev)
return 0;
}
-static int at91_ohci_probe(struct device_d *dev)
+static int at91_ohci_probe(struct device *dev)
{
int ret;
struct resource *io;
@@ -116,7 +116,7 @@ static int at91_ohci_probe(struct device_d *dev)
dev->priv = ohci_at91;
ohci_at91->dev = dev;
- if (dev->device_node) {
+ if (dev->of_node) {
ret = at91_ohci_probe_dt(dev);
if (ret < 0)
return ret;
@@ -159,7 +159,7 @@ static int at91_ohci_probe(struct device_d *dev)
return 0;
}
-static void at91_ohci_remove(struct device_d *dev)
+static void at91_ohci_remove(struct device *dev)
{
struct at91_usbh_data *pdata = dev->platform_data;
struct ohci_at91_priv *ohci_at91 = dev->priv;
@@ -194,8 +194,9 @@ static const struct of_device_id at91_ohci_dt_ids[] = {
{ .compatible = "atmel,at91rm9200-ohci" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids);
-static struct driver_d at91_ohci_driver = {
+static struct driver at91_ohci_driver = {
.name = "at91_ohci",
.probe = at91_ohci_probe,
.remove = at91_ohci_remove,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index ce5d39166b..ae4c34e818 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -30,8 +30,8 @@
#include <clock.h>
#include <dma.h>
#include <malloc.h>
-#include <usb/usb.h>
-#include <usb/usb_defs.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usb_defs.h>
#include <init.h>
#include <errno.h>
#include <linux/err.h>
@@ -842,7 +842,8 @@ static void td_fill(struct ohci *ohci, unsigned int info,
td->hwNextTD = virt_to_phys((void *)m32_swap((unsigned long)td_pt));
- dma_sync_single_for_device((unsigned long)data, len, DMA_BIDIRECTIONAL);
+ dma_sync_single_for_device(ohci->host.hw_dev, (unsigned long)data,
+ len, DMA_BIDIRECTIONAL);
/* append to queue */
td->ed->hwTailP = td->hwNextTD;
@@ -1078,7 +1079,7 @@ static int dl_done_list(struct ohci *ohci)
unsigned long ptdphys = virt_to_phys(ptd);
struct td *td_list;
- dma_sync_single_for_device((unsigned long)ptdphys,
+ dma_sync_single_for_device(ohci->host.hw_dev, (unsigned long)ptdphys,
sizeof(struct td) * NUM_TD, DMA_BIDIRECTIONAL);
td_list = dl_reverse_done_list(ohci);
@@ -1515,7 +1516,7 @@ static int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *b
dev->status = stat;
dev->act_len = urb->actual_length;
- dma_sync_single_for_cpu((unsigned long)buffer, transfer_len,
+ dma_sync_single_for_cpu(host->hw_dev, (unsigned long)buffer, transfer_len,
DMA_BIDIRECTIONAL);
pkt_print(urb, dev, pipe, buffer, transfer_len,
@@ -1775,7 +1776,7 @@ static int ohci_init(struct usb_host *host)
return 0;
}
-static int ohci_probe(struct device_d *dev)
+static int ohci_probe(struct device *dev)
{
struct resource *iores;
struct usb_host *host;
@@ -1810,7 +1811,7 @@ static int ohci_probe(struct device_d *dev)
return 0;
}
-static struct driver_d ohci_driver = {
+static struct driver ohci_driver = {
.name = "ohci",
.probe = ohci_probe,
};
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index ae20560eb5..e962bfde3f 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -20,8 +20,8 @@
#include <io.h>
#include <linux/err.h>
#include <linux/sizes.h>
-#include <usb/usb.h>
-#include <usb/xhci.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/xhci.h>
#include <asm/unaligned.h>
#include "xhci.h"
@@ -82,11 +82,11 @@ static void xhci_free(struct xhci_ctrl *ctrl, void *ptr)
* @param size size of memory to be allocated
* @return allocates the memory and returns the aligned pointer
*/
-static void *xhci_malloc(struct xhci_ctrl *ctrl, unsigned int size)
+static void *xhci_malloc(struct xhci_ctrl *ctrl, unsigned int size, dma_addr_t *dma_addr)
{
void *ptr;
- ptr = dma_alloc_coherent(size, DMA_ADDRESS_BROKEN);
+ ptr = dma_alloc_coherent(size, dma_addr);
if (!ptr)
return NULL;
@@ -145,7 +145,7 @@ static void xhci_scratchpad_free(struct xhci_ctrl *ctrl)
ctrl->dcbaa->dev_context_ptrs[0] = 0;
- xhci_free(ctrl, (void *)(uintptr_t)ctrl->scratchpad->sp_array[0]);
+ xhci_free(ctrl, ctrl->scratchpad->scratchpad);
xhci_free(ctrl, ctrl->scratchpad->sp_array);
free(ctrl->scratchpad);
ctrl->scratchpad = NULL;
@@ -234,14 +234,13 @@ static void xhci_link_segments(struct xhci_segment *prev,
struct xhci_segment *next, bool link_trbs)
{
u32 val;
- u64 val_64 = 0;
if (!prev || !next)
return;
prev->next = next;
if (link_trbs) {
- val_64 = (uintptr_t)next->trbs;
- prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64;
+ prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr =
+ cpu_to_le64(next->dma);
/*
* Set the last TRB in the segment to
@@ -249,8 +248,7 @@ static void xhci_link_segments(struct xhci_segment *prev,
*/
val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
val &= ~TRB_TYPE_BITMASK;
- val |= (TRB_LINK << TRB_TYPE_SHIFT);
-
+ val |= TRB_TYPE(TRB_LINK);
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
}
@@ -295,7 +293,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_ctrl *ctrl)
seg = xzalloc(sizeof(*seg));
- seg->trbs = xhci_malloc(ctrl, SEGMENT_SIZE);
+ seg->trbs = xhci_malloc(ctrl, SEGMENT_SIZE, &seg->dma);
return seg;
}
@@ -365,6 +363,7 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl)
struct xhci_hccr *hccr = ctrl->hccr;
struct xhci_hcor *hcor = ctrl->hcor;
struct xhci_scratchpad *scratchpad;
+ dma_addr_t val_64;
int num_sp;
uint32_t page_size;
void *buf;
@@ -379,11 +378,11 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl)
goto fail_sp;
ctrl->scratchpad = scratchpad;
- scratchpad->sp_array = xhci_malloc(ctrl, num_sp * sizeof(u64));
+ scratchpad->sp_array = xhci_malloc(ctrl, num_sp * sizeof(u64), &val_64);
if (!scratchpad->sp_array)
goto fail_sp2;
- ctrl->dcbaa->dev_context_ptrs[0] =
- cpu_to_le64((uintptr_t)scratchpad->sp_array);
+
+ ctrl->dcbaa->dev_context_ptrs[0] = cpu_to_le64(val_64);
xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[0],
sizeof(ctrl->dcbaa->dev_context_ptrs[0]));
@@ -397,17 +396,21 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl)
BUG_ON(i == 16);
page_size = 1 << (i + 12);
- buf = xhci_malloc(ctrl, num_sp * page_size);
+ buf = xhci_malloc(ctrl, num_sp * page_size, &val_64);
if (!buf)
goto fail_sp3;
xhci_flush_cache((uintptr_t)buf, num_sp * page_size);
+ scratchpad->scratchpad = buf;
for (i = 0; i < num_sp; i++) {
- uintptr_t ptr = (uintptr_t)buf + i * page_size;
- scratchpad->sp_array[i] = cpu_to_le64(ptr);
+ scratchpad->sp_array[i] = cpu_to_le64(val_64);
+ val_64 += page_size;
}
+ xhci_flush_cache((uintptr_t)scratchpad->sp_array,
+ sizeof(u64) * num_sp);
+
return 0;
fail_sp3:
@@ -438,11 +441,11 @@ static struct xhci_container_ctx
BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT));
ctx->type = type;
ctx->size = (MAX_EP_CTX_NUM + 1) *
- CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
+ CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams));
if (type == XHCI_CTX_TYPE_INPUT)
- ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
+ ctx->size += CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams));
- ctx->bytes = xhci_malloc(ctrl, ctx->size);
+ ctx->bytes = xhci_malloc(ctrl, ctx->size, &ctx->dma);
return ctx;
}
@@ -464,8 +467,7 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)
return -EEXIST;
}
- ctrl->devs[slot_id] = (struct xhci_virt_device *)
- malloc(sizeof(struct xhci_virt_device));
+ ctrl->devs[slot_id] = malloc(sizeof(struct xhci_virt_device));
if (!ctrl->devs[slot_id]) {
dev_err(ctrl->dev, "Failed to allocate virtual device\n");
@@ -494,10 +496,10 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)
/* Allocate endpoint 0 ring */
virt_dev->eps[0].ring = xhci_ring_alloc(ctrl, 1, true);
- byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes);
+ byte_64 = virt_dev->out_ctx->dma;
/* Point to output device context in dcbaa. */
- ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64;
+ ctrl->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(byte_64);
xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[slot_id],
sizeof(__le64));
@@ -516,29 +518,27 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)
int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
struct xhci_hcor *hcor)
{
+ dma_addr_t dma;
uint64_t val_64;
uint64_t trb_64;
uint32_t val;
- unsigned long deq;
+ uint64_t deq;
int i;
struct xhci_segment *seg;
/* DCBAA initialization */
- ctrl->dcbaa = xhci_malloc(ctrl, sizeof(*ctrl->dcbaa));
- if (!ctrl->dcbaa) {
- dev_err(ctrl->dev, "unable to allocate DCBA\n");
- return -ENOMEM;
- }
+ ctrl->dcbaa = xhci_malloc(ctrl, sizeof(struct xhci_device_context_array),
+ &dma);
+ ctrl->dcbaa->dma = dma;
- val_64 = (uintptr_t)ctrl->dcbaa;
/* Set the pointer in DCBAA register */
- xhci_writeq(&hcor->or_dcbaap, val_64);
+ xhci_writeq(&hcor->or_dcbaap, dma);
/* Command ring control pointer register initialization */
ctrl->cmd_ring = xhci_ring_alloc(ctrl, 1, true);
/* Set the address in the Command Ring Control register */
- trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs;
+ trb_64 = ctrl->cmd_ring->first_seg->dma;
val_64 = xhci_readq(&hcor->or_crcr);
val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
(trb_64 & (u64) ~CMD_RING_RSVD_BITS) |
@@ -560,8 +560,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
/* Event ring does not maintain link TRB */
ctrl->event_ring = xhci_ring_alloc(ctrl, ERST_NUM_SEGS, false);
- ctrl->erst.entries =
- xhci_malloc(ctrl, sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);
+ ctrl->erst.entries = xhci_malloc(ctrl, sizeof(struct xhci_erst_entry) *
+ ERST_NUM_SEGS, &ctrl->erst.erst_dma_addr);
ctrl->erst.num_entries = ERST_NUM_SEGS;
@@ -570,9 +570,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
val++) {
struct xhci_erst_entry *entry = &ctrl->erst.entries[val];
- trb_64 = 0;
- trb_64 = (uintptr_t)seg->trbs;
- xhci_writeq(&entry->seg_addr, trb_64);
+ trb_64 = seg->dma;
+ entry->seg_addr = cpu_to_le64(trb_64);
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
entry->rsvd = 0;
seg = seg->next;
@@ -580,7 +579,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
xhci_flush_cache((uintptr_t)ctrl->erst.entries,
ERST_NUM_SEGS * sizeof(struct xhci_erst_entry));
- deq = (unsigned long)ctrl->event_ring->dequeue;
+ deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg,
+ ctrl->event_ring->dequeue);
/* Update HC event ring dequeue pointer */
xhci_writeq(&ctrl->ir_set->erst_dequeue,
@@ -595,7 +595,7 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
/* this is the event ring segment table pointer */
val_64 = xhci_readq(&ctrl->ir_set->erst_base);
val_64 &= ERST_PTR_MASK;
- val_64 |= ((uintptr_t)(ctrl->erst.entries) & ~ERST_PTR_MASK);
+ val_64 |= ctrl->erst.erst_dma_addr & ~ERST_PTR_MASK;
xhci_writeq(&ctrl->ir_set->erst_base, val_64);
@@ -645,7 +645,7 @@ struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl,
return (struct xhci_slot_ctx *)ctx->bytes;
return (struct xhci_slot_ctx *)
- (ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)));
+ (ctx->bytes + CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)));
}
/**
@@ -667,7 +667,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl,
return (struct xhci_ep_ctx *)
(ctx->bytes +
- (ep_index * CTX_SIZE(readl(&ctrl->hccr->cr_hccparams))));
+ (ep_index * CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams))));
}
/**
@@ -776,7 +776,7 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
dev_dbg(&udev->dev, "route string 0x%x\n", route);
- slot_ctx->dev_info |= route;
+ slot_ctx->dev_info |= cpu_to_le32(route);
switch (speed) {
case USB_SPEED_SUPER:
@@ -824,25 +824,22 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
/* Step 4 - ring already allocated */
/* Step 5 */
- ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT);
+ ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP));
dev_dbg(&udev->dev, "SPEED = %d\n", speed);
switch (speed) {
case USB_SPEED_SUPER:
- ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) <<
- MAX_PACKET_SHIFT));
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(512));
dev_dbg(&udev->dev, "Setting Packet size = 512bytes\n");
break;
case USB_SPEED_HIGH:
/* USB core guesses at a 64-byte max packet first for FS devices */
case USB_SPEED_FULL:
- ep0_ctx->ep_info2 |= cpu_to_le32(((64 & MAX_PACKET_MASK) <<
- MAX_PACKET_SHIFT));
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(64));
dev_dbg(&udev->dev, "Setting Packet size = 64bytes\n");
break;
case USB_SPEED_LOW:
- ep0_ctx->ep_info2 |= cpu_to_le32(((8 & MAX_PACKET_MASK) <<
- MAX_PACKET_SHIFT));
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(8));
dev_dbg(&udev->dev, "Setting Packet size = 8bytes\n");
break;
default:
@@ -851,11 +848,9 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
}
/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
- ep0_ctx->ep_info2 |=
- cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) |
- ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT));
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3));
- trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs;
+ trb_64 = virt_dev->eps[0].ring->first_seg->dma;
ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state);
/*
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 60764222af..691d9c7463 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -19,12 +19,30 @@
#include <io.h>
#include <linux/err.h>
#include <linux/sizes.h>
-#include <usb/usb.h>
-#include <usb/xhci.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/xhci.h>
#include <asm/unaligned.h>
#include "xhci.h"
+/*
+ * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
+ * address of the TRB.
+ */
+dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
+ union xhci_trb *trb)
+{
+ unsigned long segment_offset;
+
+ BUG_ON(!seg || !trb || trb < seg->trbs);
+
+ /* offset in TRBs */
+ segment_offset = trb - seg->trbs;
+ BUG_ON(segment_offset >= TRBS_PER_SEGMENT);
+
+ return seg->dma + (segment_offset * sizeof(*trb));
+}
+
/**
* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
* segment? I.e. would the updated event TRB pointer step off the end of the
@@ -181,12 +199,11 @@ static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring)
* @param trb_fields pointer to trb field array containing TRB contents
* @return pointer to the enqueued trb
*/
-static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl,
- struct xhci_ring *ring,
- bool more_trbs_coming,
- unsigned int *trb_fields)
+static dma_addr_t queue_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
+ bool more_trbs_coming, unsigned int *trb_fields)
{
struct xhci_generic_trb *trb;
+ dma_addr_t addr;
int i;
trb = &ring->enqueue->generic;
@@ -196,9 +213,11 @@ static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl,
xhci_flush_cache((uintptr_t)trb, sizeof(struct xhci_generic_trb));
+ addr = xhci_trb_virt_to_dma(ring->enq_seg, (union xhci_trb *)trb);
+
inc_enq(ctrl, ring, more_trbs_coming);
- return trb;
+ return addr;
}
/**
@@ -273,16 +292,15 @@ static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring,
* @param cmd Command type to enqueue
* @return none
*/
-void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id,
+void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr, u32 slot_id,
u32 ep_index, trb_type cmd)
{
u32 fields[4];
- u64 val_64 = (uintptr_t)ptr;
BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING));
- fields[0] = lower_32_bits(val_64);
- fields[1] = upper_32_bits(val_64);
+ fields[0] = lower_32_bits(addr);
+ fields[1] = upper_32_bits(addr);
fields[2] = 0;
fields[3] = TRB_TYPE(cmd) | SLOT_ID_FOR_TRB(slot_id) |
ctrl->cmd_ring->cycle_state;
@@ -396,12 +414,15 @@ static void giveback_first_trb(struct usb_device *udev, int ep_index,
*/
void xhci_acknowledge_event(struct xhci_ctrl *ctrl)
{
+ dma_addr_t deq;
+
/* Advance our dequeue pointer to the next event */
inc_deq(ctrl, ctrl->event_ring);
/* Inform the hardware */
- xhci_writeq(&ctrl->ir_set->erst_dequeue,
- (uintptr_t)ctrl->event_ring->dequeue | ERST_EHB);
+ deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg,
+ ctrl->event_ring->dequeue);
+ xhci_writeq(&ctrl->ir_set->erst_dequeue, deq | ERST_EHB);
}
/**
@@ -449,7 +470,8 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected,
continue;
type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags));
- if (type == expected)
+ if (type == expected ||
+ (expected == TRB_NONE && type != TRB_PORT_STATUS))
return event;
if (type == TRB_PORT_STATUS)
@@ -461,14 +483,59 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected,
BUG_ON(GET_COMP_CODE(
le32_to_cpu(event->generic.field[2])) !=
COMP_SUCCESS);
+ else
+ dev_dbg(ctrl->dev, "Unexpected XHCI event TRB, skipping... "
+ "(%08x %08x %08x %08x)\n",
+ le32_to_cpu(event->generic.field[0]),
+ le32_to_cpu(event->generic.field[1]),
+ le32_to_cpu(event->generic.field[2]),
+ le32_to_cpu(event->generic.field[3]));
+
xhci_acknowledge_event(ctrl);
} while (!is_timeout_non_interruptible(start, timeout_ms * MSECOND));
if (expected == TRB_TRANSFER)
return NULL;
- dev_err(ctrl->dev, "XHCI timeout on event type %d... cannot recover.\n", expected);
- BUG();
+ dev_warn(ctrl->dev, "XHCI timeout on event type %d...\n", expected);
+
+ return NULL;
+}
+
+/*
+ * Send reset endpoint command for given endpoint. This recovers from a
+ * halted endpoint (e.g. due to a stall error).
+ */
+static void reset_ep(struct usb_device *udev, int ep_index, unsigned int timeout_ms)
+{
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
+ struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring;
+ union xhci_trb *event;
+ u64 addr;
+ u32 field;
+
+ dev_info(&udev->dev, "Resetting EP %d...\n", ep_index);
+
+ xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_RESET_EP);
+ event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms);
+ if (!event)
+ return;
+
+ field = le32_to_cpu(event->trans_event.flags);
+ BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
+ xhci_acknowledge_event(ctrl);
+
+ addr = xhci_trb_virt_to_dma(ring->enq_seg,
+ (void *)((uintptr_t)ring->enqueue | ring->cycle_state));
+ xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ);
+ event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms);
+ if (!event)
+ return;
+
+ BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
+ != udev->slot_id || GET_COMP_CODE(le32_to_cpu(
+ event->event_cmd.status)) != COMP_SUCCESS);
+ xhci_acknowledge_event(ctrl);
}
/*
@@ -484,27 +551,49 @@ static void abort_td(struct usb_device *udev, int ep_index)
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring;
union xhci_trb *event;
+ xhci_comp_code comp;
+ trb_type type;
+ dma_addr_t addr;
u32 field;
- xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_STOP_RING);
+ xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_STOP_RING);
- event = xhci_wait_for_event(ctrl, TRB_TRANSFER, XHCI_TIMEOUT_DEFAULT);
- field = le32_to_cpu(event->trans_event.flags);
- BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
- BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
- BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len
- != COMP_STOP)));
- xhci_acknowledge_event(ctrl);
+ event = xhci_wait_for_event(ctrl, TRB_NONE, XHCI_TIMEOUT_DEFAULT);
+ if (!event)
+ return;
- event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT);
- BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
- != udev->slot_id || GET_COMP_CODE(le32_to_cpu(
- event->event_cmd.status)) != COMP_SUCCESS);
+ type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags));
+ if (type == TRB_TRANSFER) {
+ field = le32_to_cpu(event->trans_event.flags);
+ BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
+ BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
+ BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len
+ != COMP_STOP)));
+ xhci_acknowledge_event(ctrl);
+
+ event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT);
+ if (!event)
+ return;
+ type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags));
+
+ } else {
+ dev_warn(ctrl->dev, "abort_td: Expected a TRB_TRANSFER TRB first\n");
+ }
+
+ comp = GET_COMP_CODE(le32_to_cpu(event->event_cmd.status));
+ BUG_ON(type != TRB_COMPLETION ||
+ TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
+ != udev->slot_id || (comp != COMP_SUCCESS && comp
+ != COMP_CTX_STATE));
xhci_acknowledge_event(ctrl);
- xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue |
- ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ);
+ addr = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
+ addr |= ring->cycle_state;
+ xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT);
+ if (!event)
+ return;
+
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
!= udev->slot_id || GET_COMP_CODE(le32_to_cpu(
event->event_cmd.status)) != COMP_SUCCESS);
@@ -594,7 +683,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
memcpy(bounce, buffer, length);
}
- map = addr = dma_map_single(ctrl->dev, bounce, length, direction);
+ map = addr = dma_map_single(ctrl->host.hw_dev, bounce, length, direction);
dev_dbg(&udev->dev, "pipe=0x%lx, buffer=%p, length=%d\n",
pipe, buffer, length);
@@ -607,6 +696,14 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index);
+ /*
+ * If the endpoint was halted due to a prior error, resume it before
+ * the next transfer. It is the responsibility of the upper layer to
+ * have dealt with whatever caused the error.
+ */
+ if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED)
+ reset_ep(udev, ep_index, timeout_ms);
+
ring = virt_dev->eps[ep_index].ring;
/*
* How much data is (potentially) left before the 64KB boundary?
@@ -639,8 +736,10 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
*/
ret = prepare_ring(ctrl, ring,
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
- if (ret < 0)
+ if (ret < 0) {
+ dma_unmap_single(ctrl->host.hw_dev, map, length, direction);
return ret;
+ }
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
@@ -660,6 +759,9 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
first_trb = true;
+ /* flush the buffer before use */
+ xhci_flush_cache((uintptr_t)buffer, length);
+
/* Queue the first TRB, even if it's zero-length */
do {
u32 remainder = 0;
@@ -696,15 +798,14 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
maxpacketsize,
num_trbs - 1);
- length_field = ((trb_buff_len & TRB_LEN_MASK) |
+ length_field = (TRB_LEN(trb_buff_len) |
remainder |
- ((0 & TRB_INTR_TARGET_MASK) <<
- TRB_INTR_TARGET_SHIFT));
+ TRB_INTR_TARGET(0));
trb_fields[0] = lower_32_bits(addr);
trb_fields[1] = upper_32_bits(addr);
trb_fields[2] = length_field;
- trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT);
+ trb_fields[3] = field | TRB_TYPE(TRB_NORMAL);
queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
@@ -725,6 +826,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
abort_td(udev, ep_index);
udev->status = USB_ST_NAK_REC; /* closest thing to a timeout */
udev->act_len = 0;
+ dma_unmap_single(ctrl->host.hw_dev, map, length, direction);
return -ETIMEDOUT;
}
field = le32_to_cpu(event->trans_event.flags);
@@ -740,7 +842,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
record_transfer_result(udev, event, length);
xhci_acknowledge_event(ctrl);
- dma_unmap_single(ctrl->dev, map, length, direction);
+ dma_unmap_single(ctrl->host.hw_dev, map, length, direction);
if (usb_pipein(pipe))
memcpy(buffer, bounce, length);
@@ -839,7 +941,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
/* Queue setup TRB - see section 6.4.1.2.1 */
/* FIXME better way to translate setup_packet into two u32 fields? */
field = 0;
- field |= TRB_IDT | (TRB_SETUP << TRB_TYPE_SHIFT);
+ field |= TRB_IDT | TRB_TYPE(TRB_SETUP);
if (start_cycle == 0)
field |= 0x1;
@@ -847,9 +949,9 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) >= 0x100) {
if (length > 0) {
if (req->requesttype & USB_DIR_IN)
- field |= (TRB_DATA_IN << TRB_TX_TYPE_SHIFT);
+ field |= TRB_TX_TYPE(TRB_DATA_IN);
else
- field |= (TRB_DATA_OUT << TRB_TX_TYPE_SHIFT);
+ field |= TRB_TX_TYPE(TRB_DATA_OUT);
}
}
@@ -865,8 +967,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
trb_fields[1] = le16_to_cpu(req->index) |
le16_to_cpu(req->length) << 16;
/* TRB_LEN | (TRB_INTR_TARGET) */
- trb_fields[2] = (8 | ((0 & TRB_INTR_TARGET_MASK) <<
- TRB_INTR_TARGET_SHIFT));
+ trb_fields[2] = (TRB_LEN(8) | TRB_INTR_TARGET(0));
/* Immediate data in pointer */
trb_fields[3] = field;
queue_trb(ctrl, ep_ring, true, trb_fields);
@@ -876,15 +977,15 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
/* If there's data, queue data TRBs */
/* Only set interrupt on short packet for IN endpoints */
if (usb_pipein(pipe))
- field = TRB_ISP | (TRB_DATA << TRB_TYPE_SHIFT);
+ field = TRB_ISP | TRB_TYPE(TRB_DATA);
else
- field = (TRB_DATA << TRB_TYPE_SHIFT);
+ field = TRB_TYPE(TRB_DATA);
- length_field = (length & TRB_LEN_MASK) | xhci_td_remainder(length) |
+ length_field = TRB_LEN(length) | xhci_td_remainder(length) |
((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT);
dev_dbg(&udev->dev, "length_field = %d, length = %d,"
"xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n",
- length_field, (length & TRB_LEN_MASK),
+ length_field, TRB_LEN(length),
xhci_td_remainder(length), 0);
if (req->requesttype & USB_DIR_IN)
@@ -895,13 +996,14 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
if (length > 0) {
if (req->requesttype & USB_DIR_IN)
field |= TRB_DIR_IN;
- map = buf_64 = dma_map_single(ctrl->dev, buffer, length, direction);
+ map = buf_64 = dma_map_single(ctrl->host.hw_dev, buffer, length, direction);
trb_fields[0] = lower_32_bits(buf_64);
trb_fields[1] = upper_32_bits(buf_64);
trb_fields[2] = length_field;
trb_fields[3] = field | ep_ring->cycle_state;
+ xhci_flush_cache((uintptr_t)buffer, length);
queue_trb(ctrl, ep_ring, true, trb_fields);
}
@@ -919,11 +1021,10 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
trb_fields[0] = 0;
trb_fields[1] = 0;
- trb_fields[2] = ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT);
+ trb_fields[2] = TRB_INTR_TARGET(0);
/* Event on completion */
trb_fields[3] = field | TRB_IOC |
- (TRB_STATUS << TRB_TYPE_SHIFT) |
- ep_ring->cycle_state;
+ TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state;
queue_trb(ctrl, ep_ring, false, trb_fields);
@@ -947,7 +1048,12 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
/* Invalidate buffer to make it available to usb-core */
if (length > 0)
- dma_unmap_single(ctrl->dev, map, length, direction);
+ dma_unmap_single(ctrl->host.hw_dev, map, length, direction);
+
+ if (udev->status == USB_ST_STALLED) {
+ reset_ep(udev, ep_index, timeout_ms);
+ return -EPIPE;
+ }
if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))
== COMP_SHORT_TX) {
@@ -967,5 +1073,6 @@ abort:
abort_td(udev, ep_index);
udev->status = USB_ST_NAK_REC;
udev->act_len = 0;
+ dma_unmap_single(ctrl->host.hw_dev, map, length, direction);
return -ETIMEDOUT;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index ce93d345f9..e7b8344181 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -24,13 +24,13 @@
#include <init.h>
#include <io.h>
#include <linux/err.h>
-#include <usb/usb.h>
-#include <usb/xhci.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/xhci.h>
#include <asm/unaligned.h>
#include "xhci.h"
-static struct descriptor {
+static const struct descriptor {
struct usb_hub_descriptor hub;
struct usb_device_descriptor device;
struct usb_config_descriptor config;
@@ -151,6 +151,8 @@ static int xhci_start(struct xhci_ctrl *ctrl)
u32 temp;
int ret;
+ dev_dbg(ctrl->dev, "Starting the controller\n");
+
temp = xhci_readl(&hcor->or_usbcmd);
temp |= (CMD_RUN);
xhci_writel(&hcor->or_usbcmd, temp);
@@ -441,9 +443,12 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change)
in_ctx = virt_dev->in_ctx;
xhci_flush_cache((uintptr_t)in_ctx->bytes, in_ctx->size);
- xhci_queue_command(ctrl, in_ctx->bytes, udev->slot_id, 0,
+ xhci_queue_command(ctrl, in_ctx->dma, udev->slot_id, 0,
ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT);
+ if (!event)
+ return -ETIMEDOUT;
+
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
!= udev->slot_id);
@@ -566,8 +571,7 @@ static int xhci_set_configuration(struct usb_device *udev)
cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
EP_INTERVAL(interval) | EP_MULT(mult));
- ep_ctx[ep_index]->ep_info2 =
- cpu_to_le32(ep_type << EP_TYPE_SHIFT);
+ ep_ctx[ep_index]->ep_info2 = cpu_to_le32(EP_TYPE(ep_type));
ep_ctx[ep_index]->ep_info2 |=
cpu_to_le32(MAX_PACKET
(get_unaligned(&endpt_desc->wMaxPacketSize)));
@@ -579,8 +583,8 @@ static int xhci_set_configuration(struct usb_device *udev)
cpu_to_le32(MAX_BURST(max_burst) |
ERROR_COUNT(err_count));
- trb_64 = (uintptr_t)
- virt_dev->eps[ep_index].ring->enqueue;
+ trb_64 = xhci_trb_virt_to_dma(virt_dev->eps[ep_index].ring->enq_seg,
+ virt_dev->eps[ep_index].ring->enqueue);
ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 |
virt_dev->eps[ep_index].ring->cycle_state);
@@ -628,8 +632,12 @@ static int xhci_address_device(struct usb_device *udev, int root_portnr)
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
ctrl_ctx->drop_flags = 0;
- xhci_queue_command(ctrl, (void *)ctrl_ctx, slot_id, 0, TRB_ADDR_DEV);
+ xhci_queue_command(ctrl, virt_dev->in_ctx->dma,
+ slot_id, 0, TRB_ADDR_DEV);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT);
+ if (!event)
+ return -ETIMEDOUT;
+
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != slot_id);
switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) {
@@ -703,8 +711,11 @@ static int _xhci_alloc_device(struct usb_device *udev)
return 0;
}
- xhci_queue_command(ctrl, NULL, 0, 0, TRB_ENABLE_SLOT);
+ xhci_queue_command(ctrl, 0, 0, 0, TRB_ENABLE_SLOT);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT);
+ if (!event)
+ return -ETIMEDOUT;
+
BUG_ON(GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))
!= COMP_SUCCESS);
@@ -764,8 +775,7 @@ int xhci_check_maxpacket(struct usb_device *udev)
ctrl->devs[slot_id]->out_ctx, ep_index);
in_ctx = ctrl->devs[slot_id]->in_ctx;
ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
- ep_ctx->ep_info2 &= cpu_to_le32(~((0xffff & MAX_PACKET_MASK)
- << MAX_PACKET_SHIFT));
+ ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET(MAX_PACKET_MASK));
ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size));
/*
@@ -860,7 +870,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe,
{
uint8_t tmpbuf[4];
u16 typeReq;
- void *srcptr = NULL;
+ const void *srcptr = NULL;
int len, srclen;
uint32_t reg;
volatile uint32_t *status_reg;
@@ -928,7 +938,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe,
case USB_DT_HUB:
case USB_DT_SS_HUB:
dev_dbg(&udev->dev, "USB_DT_HUB config\n");
- srcptr = &descriptor.hub;
+ srcptr = &ctrl->hub_desc;
srclen = 0x8;
break;
default:
@@ -1088,8 +1098,10 @@ unknown:
static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length, int interval)
{
- if (usb_pipetype(pipe) != PIPE_INTERRUPT)
+ if (usb_pipetype(pipe) != PIPE_INTERRUPT) {
+ dev_err(&udev->dev, "non-interrupt pipe (type=%lu)", usb_pipetype(pipe));
return -EINVAL;
+ }
/*
* xHCI uses normal TRBs for both bulk and interrupt. When the
@@ -1112,8 +1124,10 @@ static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length, int timeout_ms)
{
- if (usb_pipetype(pipe) != PIPE_BULK)
+ if (usb_pipetype(pipe) != PIPE_BULK) {
+ dev_err(&udev->dev, "non-bulk pipe (type=%lu)", usb_pipetype(pipe));
return -EINVAL;
+ }
return xhci_bulk_tx(udev, pipe, length, buffer, timeout_ms);
}
@@ -1137,8 +1151,10 @@ static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe,
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int ret = 0;
- if (usb_pipetype(pipe) != PIPE_CONTROL)
+ if (usb_pipetype(pipe) != PIPE_CONTROL) {
+ dev_err(&udev->dev, "non-control pipe (type=%lu)", usb_pipetype(pipe));
return -EINVAL;
+ }
if (usb_pipedevice(pipe) == ctrl->rootdev)
return xhci_submit_root(udev, pipe, buffer, setup);
@@ -1181,21 +1197,23 @@ static int xhci_lowlevel_init(struct xhci_ctrl *ctrl)
/* initializing xhci data structures */
if (xhci_mem_init(ctrl, hccr, hcor) < 0)
return -ENOMEM;
+ ctrl->hub_desc = descriptor.hub;
reg = xhci_readl(&hccr->cr_hcsparams1);
- descriptor.hub.bNbrPorts = ((reg & HCS_MAX_PORTS_MASK) >>
- HCS_MAX_PORTS_SHIFT);
+ ctrl->hub_desc.bNbrPorts = HCS_MAX_PORTS(reg);
+
+ dev_dbg(ctrl->dev, "Register 0x%x NbrPorts %d\n", reg, ctrl->hub_desc.bNbrPorts);
/* Port Indicators */
reg = xhci_readl(&hccr->cr_hccparams);
if (HCS_INDICATOR(reg))
- put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
- | 0x80, &descriptor.hub.wHubCharacteristics);
+ put_unaligned(get_unaligned(&ctrl->hub_desc.wHubCharacteristics)
+ | 0x80, &ctrl->hub_desc.wHubCharacteristics);
/* Port Power Control */
if (HCC_PPC(reg))
- put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
- | 0x01, &descriptor.hub.wHubCharacteristics);
+ put_unaligned(get_unaligned(&ctrl->hub_desc.wHubCharacteristics)
+ | 0x01, &ctrl->hub_desc.wHubCharacteristics);
if (xhci_start(ctrl)) {
xhci_reset(ctrl);
@@ -1218,6 +1236,8 @@ static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl)
xhci_reset(ctrl);
+ dev_dbg(ctrl->dev, "Disabling event ring interrupts\n");
+
temp = xhci_readl(&ctrl->hcor->or_usbsts);
xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT);
temp = xhci_readl(&ctrl->ir_set->irq_pending);
@@ -1344,7 +1364,7 @@ static __maybe_unused int xhci_get_max_xfer_size(size_t *size)
int xhci_register(struct xhci_ctrl *ctrl)
{
struct usb_host *host;
- struct device_d *dev = ctrl->dev;
+ struct device *dev = ctrl->dev;
int ret;
dev_dbg(dev, "%s: hccr=%p, hcor=%p\n", __func__, ctrl->hccr, ctrl->hcor);
@@ -1359,7 +1379,11 @@ int xhci_register(struct xhci_ctrl *ctrl)
*/
host->no_desc_before_addr = true;
- host->hw_dev = dev;
+ /*
+ * If xHCI doesn't have its own DT node, it'll be a child of a
+ * physical USB host controller device that should be used for DMA
+ */
+ host->hw_dev = dev_of_node(dev) ? dev : dev->parent;
host->submit_int_msg = xhci_submit_int_msg;
host->submit_control_msg = xhci_submit_control_msg;
host->submit_bulk_msg = xhci_submit_bulk_msg;
@@ -1394,7 +1418,7 @@ int xhci_deregister(struct xhci_ctrl *ctrl)
* xHCI platform driver
*/
-static int xhci_probe(struct device_d *dev)
+static int xhci_probe(struct device *dev)
{
struct resource *iores;
struct xhci_ctrl *ctrl;
@@ -1415,14 +1439,14 @@ static int xhci_probe(struct device_d *dev)
return xhci_register(ctrl);
}
-static void xhci_remove(struct device_d *dev)
+static void xhci_remove(struct device *dev)
{
struct xhci_ctrl *ctrl = dev->priv;
xhci_deregister(ctrl);
}
-static struct driver_d xhci_driver = {
+static struct driver xhci_driver = {
.name = "xHCI",
.probe = xhci_probe,
.remove = xhci_remove,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index e445438e81..37e8cee843 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -16,7 +16,7 @@
#ifndef HOST_XHCI_H_
#define HOST_XHCI_H_
-#include <asm/types.h>
+#include <linux/types.h>
#include <io.h>
#include <io-64-nonatomic-lo-hi.h>
#include <linux/list.h>
@@ -30,8 +30,8 @@
/* Section 5.3.3 - MaxPorts */
#define MAX_HC_PORTS 255
-/* Up to 16 ms to halt an HC */
-#define XHCI_MAX_HALT_USEC (16*1000)
+/* Up to 32 ms to halt an HC */
+#define XHCI_MAX_HALT_USEC (32*1000)
#define XHCI_MAX_RESET_USEC (250*1000)
@@ -490,6 +490,7 @@ struct xhci_container_ctx {
int size;
u8 *bytes;
+ dma_addr_t dma;
};
/**
@@ -691,6 +692,8 @@ struct xhci_input_control_ctx {
struct xhci_device_context_array {
/* 64-bit device addresses; we only write 32-bit addresses */
__le64 dev_context_ptrs[MAX_HC_SLOTS];
+ /* private xHCD pointers */
+ dma_addr_t dma;
};
/* TODO: write function to set the 64-bit device DMA address */
/*
@@ -903,6 +906,8 @@ union xhci_trb {
/* TRB type IDs */
typedef enum {
+ /* reserved, used as a software sentinel */
+ TRB_NONE = 0,
/* bulk, interrupt, isoc scatter/gather, and control data stage */
TRB_NORMAL = 1,
/* setup stage for control transfers */
@@ -1003,6 +1008,7 @@ struct xhci_segment {
union xhci_trb *trbs;
/* private to HCD */
struct xhci_segment *next;
+ dma_addr_t dma;
};
struct xhci_ring {
@@ -1031,11 +1037,14 @@ struct xhci_erst_entry {
struct xhci_erst {
struct xhci_erst_entry *entries;
unsigned int num_entries;
+ /* xhci->event_ring keeps track of segment dma addresses */
+ dma_addr_t erst_dma_addr;
/* Num entries the ERST can contain */
unsigned int erst_size;
};
struct xhci_scratchpad {
+ void *scratchpad;
u64 *sp_array;
};
@@ -1134,8 +1143,6 @@ void xhci_hcd_stop(int index);
/*************************************************************
EXTENDED CAPABILITY DEFINITIONS
*************************************************************/
-/* Up to 16 ms to halt an HC */
-#define XHCI_MAX_HALT_USEC (16*1000)
/* HC not running - set to 1 when run/stop bit is cleared. */
#define XHCI_STS_HALT (1 << 0)
@@ -1203,7 +1210,7 @@ void xhci_hcd_stop(int index);
struct xhci_ctrl {
struct usb_host host;
- struct device_d *dev;
+ struct device *dev;
struct xhci_hccr *hccr; /* R/O registers, not need for volatile */
struct xhci_hcor *hcor;
struct xhci_doorbell_array *dba;
@@ -1218,6 +1225,7 @@ struct xhci_ctrl {
struct xhci_erst_entry entry[ERST_NUM_SEGS];
struct xhci_scratchpad *scratchpad;
struct xhci_virt_device *devs[MAX_HC_SLOTS];
+ struct usb_hub_descriptor hub_desc;
void *bounce_buffer;
int rootdev;
};
@@ -1227,7 +1235,7 @@ static inline struct xhci_ctrl *to_xhci(struct usb_host *host)
return container_of(host, struct xhci_ctrl, host);
}
-unsigned long trb_addr(struct xhci_segment *seg, union xhci_trb *trb);
+dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
struct xhci_input_control_ctx
*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx);
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl,
@@ -1244,7 +1252,7 @@ void xhci_slot_copy(struct xhci_ctrl *ctrl,
struct xhci_container_ctx *out_ctx);
void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
struct usb_device *udev, int hop_portnr);
-void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr,
+void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr,
u32 slot_id, u32 ep_index, trb_type cmd);
void xhci_acknowledge_event(struct xhci_ctrl *ctrl);
#define XHCI_TIMEOUT_DEFAULT 5000
diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c
index f71cf80b7d..c5e6ce61e9 100644
--- a/drivers/usb/imx/chipidea-imx.c
+++ b/drivers/usb/imx/chipidea-imx.c
@@ -9,13 +9,13 @@
#include <of.h>
#include <errno.h>
#include <driver.h>
-#include <usb/usb.h>
-#include <usb/ehci.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/ehci.h>
#include <regulator.h>
-#include <usb/chipidea-imx.h>
-#include <usb/phy.h>
-#include <usb/ulpi.h>
-#include <usb/fsl_usb2.h>
+#include <linux/usb/chipidea-imx.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/ulpi.h>
+#include <linux/usb/fsl_usb2.h>
#include <linux/err.h>
#include <linux/phy/phy.h>
#include <linux/clk.h>
@@ -27,13 +27,13 @@ struct imx_chipidea_data {
};
struct imx_chipidea {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct ehci_data data;
unsigned long flags;
enum usb_dr_mode mode;
int portno;
- struct device_d *usbmisc;
+ struct device *usbmisc;
enum usb_phy_interface phymode;
struct param_d *param_mode;
struct regulator *vbus;
@@ -104,7 +104,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci)
struct of_phandle_args out_args;
if (ci->have_usb_misc) {
- if (of_parse_phandle_with_args(ci->dev->device_node, "fsl,usbmisc",
+ if (of_parse_phandle_with_args(ci->dev->of_node, "fsl,usbmisc",
"#index-cells", 0, &out_args))
return -ENODEV;
@@ -117,7 +117,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci)
ci->flags = MXC_EHCI_MODE_UTMI_8BIT;
- ci->mode = of_usb_get_dr_mode(ci->dev->device_node, NULL);
+ ci->mode = of_usb_get_dr_mode(ci->dev->of_node, NULL);
if (ci->mode == USB_DR_MODE_UNKNOWN) {
/*
@@ -130,7 +130,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci)
ci->mode = USB_DR_MODE_HOST;
}
- ci->phymode = of_usb_get_phy_mode(ci->dev->device_node, NULL);
+ ci->phymode = of_usb_get_phy_mode(ci->dev->of_node, NULL);
switch (ci->phymode) {
case USBPHY_INTERFACE_MODE_UTMI:
ci->flags = MXC_EHCI_MODE_UTMI_8BIT;
@@ -151,18 +151,18 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci)
dev_dbg(ci->dev, "no phy_type setting. Relying on reset default\n");
}
- if (of_find_property(ci->dev->device_node,
+ if (of_find_property(ci->dev->of_node,
"disable-over-current", NULL))
ci->flags |= MXC_EHCI_DISABLE_OVERCURRENT;
- else if (!of_find_property(ci->dev->device_node,
+ else if (!of_find_property(ci->dev->of_node,
"over-current-active-high", NULL))
ci->flags |= MXC_EHCI_OC_PIN_ACTIVE_LOW;
- if (of_find_property(ci->dev->device_node, "power-active-high", NULL))
+ if (of_find_property(ci->dev->of_node, "power-active-high", NULL))
ci->flags |= MXC_EHCI_PWR_PIN_ACTIVE_HIGH;
- if (of_usb_get_maximum_speed(ci->dev->device_node, NULL) ==
+ if (of_usb_get_maximum_speed(ci->dev->of_node, NULL) ==
USB_SPEED_FULL)
ci->flags |= MXC_EHCI_PFSC;
@@ -213,7 +213,7 @@ static int ci_set_mode(void *ctx, enum usb_dr_mode mode)
return 0;
}
-static int imx_chipidea_probe(struct device_d *dev)
+static int imx_chipidea_probe(struct device *dev)
{
struct resource *iores;
struct imx_chipidea_data *imx_data;
@@ -232,7 +232,7 @@ static int imx_chipidea_probe(struct device_d *dev)
if (!ret)
ci->have_usb_misc = imx_data->have_usb_misc;
- if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) {
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) {
ret = imx_chipidea_probe_dt(ci);
if (ret)
return ret;
@@ -265,9 +265,9 @@ static int imx_chipidea_probe(struct device_d *dev)
* more modern former one but fall back to the old one.
*
* Code should be removed when all devicetrees are using "phys" */
- if (of_property_read_bool(dev->device_node, "phys"))
+ if (of_property_read_bool(dev->of_node, "phys"))
phynode_name = "phys";
- else if (of_property_read_bool(dev->device_node, "fsl,usbphy"))
+ else if (of_property_read_bool(dev->of_node, "fsl,usbphy"))
phynode_name = "fsl,usbphy";
else
phynode_name = NULL;
@@ -330,7 +330,7 @@ static int imx_chipidea_probe(struct device_d *dev)
return ret;
};
-static void imx_chipidea_remove(struct device_d *dev)
+static void imx_chipidea_remove(struct device *dev)
{
struct imx_chipidea *ci = dev->priv;
@@ -363,8 +363,9 @@ static __maybe_unused struct of_device_id imx_chipidea_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, imx_chipidea_dt_ids);
-static struct driver_d imx_chipidea_driver = {
+static struct driver imx_chipidea_driver = {
.name = "imx-usb",
.probe = imx_chipidea_probe,
.of_compatible = DRV_OF_COMPAT(imx_chipidea_dt_ids),
diff --git a/drivers/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c
index 71164af3e8..bf9583e626 100644
--- a/drivers/usb/imx/imx-usb-misc.c
+++ b/drivers/usb/imx/imx-usb-misc.c
@@ -9,9 +9,9 @@
#include <io.h>
#include <of.h>
#include <errno.h>
-#include <usb/chipidea-imx.h>
-#include <mach/imx6-regs.h>
-#include <mach/iomux-mx6.h>
+#include <linux/usb/chipidea-imx.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/iomux-mx6.h>
#define MX25_OTG_SIC_SHIFT 29
#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
@@ -602,7 +602,7 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = {
.data = &mx7_data,
},
#endif
-#ifdef CONFIG_ARCH_IMX8M
+#if defined CONFIG_ARCH_IMX8M || defined CONFIG_ARCH_IMX93
{
.compatible = "fsl,imx8mm-usbmisc",
.data = &mx7_data,
@@ -622,8 +622,9 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, imx_usbmisc_dt_ids);
-int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags)
+int imx_usbmisc_port_init(struct device *dev, int port, unsigned flags)
{
struct imx_usb_misc_priv *usbmisc = dev->priv;
@@ -636,7 +637,7 @@ int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags)
return usbmisc->data->init(usbmisc->base, port, flags);
}
-int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags)
+int imx_usbmisc_port_post_init(struct device *dev, int port, unsigned flags)
{
struct imx_usb_misc_priv *usbmisc = dev->priv;
@@ -649,7 +650,7 @@ int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags)
return usbmisc->data->post_init(usbmisc->base, port, flags);
}
-static int imx_usbmisc_probe(struct device_d *dev)
+static int imx_usbmisc_probe(struct device *dev)
{
struct resource *iores;
struct imx_usb_misc_data *devtype;
@@ -673,7 +674,7 @@ static int imx_usbmisc_probe(struct device_d *dev)
return 0;
}
-static struct driver_d imx_usbmisc_driver = {
+static struct driver imx_usbmisc_driver = {
.name = "imx-usbmisc",
.probe = imx_usbmisc_probe,
.id_table = imx_usbmisc_ids,
diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c
index fc7f84d064..70bf292f80 100644
--- a/drivers/usb/imx/imx-usb-phy.c
+++ b/drivers/usb/imx/imx-usb-phy.c
@@ -10,7 +10,7 @@
#include <errno.h>
#include <driver.h>
#include <malloc.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
#include <linux/phy/phy.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -108,7 +108,7 @@ static int imx_usbphy_notify_disconnect(struct usb_phy *phy,
return 0;
}
-static struct phy *imx_usbphy_xlate(struct device_d *dev,
+static struct phy *imx_usbphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct imx_usbphy *imxphy = dev->priv;
@@ -143,10 +143,10 @@ static int imx_usbphy_get_vbus_state(struct param_d *p, void *priv)
return 0;
}
-static int imx_usbphy_probe(struct device_d *dev)
+static int imx_usbphy_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret;
struct imx_usbphy *imxphy;
@@ -229,8 +229,9 @@ static __maybe_unused struct of_device_id imx_usbphy_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, imx_usbphy_dt_ids);
-static struct driver_d imx_usbphy_driver = {
+static struct driver imx_usbphy_driver = {
.name = "imx-usb-phy",
.probe = imx_usbphy_probe,
.of_compatible = DRV_OF_COMPAT(imx_usbphy_dt_ids),
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 9799af4725..fde57fd743 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -14,3 +14,13 @@ config USB_HUB_USB251XB
Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration
parameters may be set in devicetree or platform data.
Say Y or M here if you need to configure such a device via SMBus.
+
+config USB_ONBOARD_HUB
+ bool "Onboard USB hub support"
+ depends on OFDEVICE || COMPILE_TEST
+ help
+ Say Y here if you want to support discrete onboard USB hubs that
+ don't require an additional control bus for initialization, but
+ need some non-trivial form of initialization, such as enabling a
+ power regulator. An example for such a hub is the Realtek
+ RTS5411.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index be5c044f5a..e00f66a5ed 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -4,3 +4,4 @@
# (the ones that don't fit into any other categories)
#
obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o
+obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
new file mode 100644
index 0000000000..9e94caaa84
--- /dev/null
+++ b/drivers/usb/misc/onboard_usb_hub.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for onboard USB hubs
+ *
+ * Copyright (c) 2022, Google LLC
+ */
+
+#include <driver.h>
+#include <linux/gpio/consumer.h>
+#include <init.h>
+#include <of.h>
+#include <linux/printk.h>
+#include <of_device.h>
+#include <regulator.h>
+#include <linux/usb/usb.h>
+
+#include "onboard_usb_hub.h"
+
+void of_usb_host_probe_hubs(struct usb_host *host)
+{
+ struct device_node *np;
+
+ np = dev_of_node(host->hw_dev);
+ if (!np)
+ return;
+
+ of_platform_populate(np, onboard_hub_match, host->hw_dev);
+}
+
+struct onboard_hub {
+ struct regulator *vdd;
+ struct device *dev;
+ const struct onboard_hub_pdata *pdata;
+ struct gpio_desc *reset_gpio;
+};
+
+static int onboard_hub_power_on(struct onboard_hub *hub)
+{
+ int err;
+
+ err = regulator_enable(hub->vdd);
+ if (err) {
+ dev_err(hub->dev, "failed to enable regulator: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ udelay(hub->pdata->reset_us);
+ gpiod_set_value(hub->reset_gpio, 0);
+
+ return 0;
+}
+
+static int onboard_hub_probe(struct device *dev)
+{
+ struct device_node *peer_node;
+ struct device *peer_dev;
+ struct onboard_hub *hub;
+
+ peer_node = of_parse_phandle(dev->of_node, "peer-hub", 0);
+ if (peer_node) {
+ peer_dev = of_find_device_by_node(peer_node);
+ if (peer_dev && peer_dev->priv)
+ return 0;
+ }
+
+ hub = xzalloc(sizeof(*hub));
+
+ hub->pdata = device_get_match_data(dev);
+ if (!hub->pdata)
+ return -EINVAL;
+
+ hub->vdd = regulator_get(dev, "vdd");
+ if (IS_ERR(hub->vdd))
+ return PTR_ERR(hub->vdd);
+
+ hub->reset_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(hub->reset_gpio))
+ return dev_errp_probe(dev, hub->reset_gpio,
+ "failed to get reset GPIO\n");
+
+ hub->dev = dev;
+ dev->priv = hub;
+
+ return onboard_hub_power_on(hub);
+}
+
+static struct driver onboard_hub_driver = {
+ .name = "onboard-usb-hub",
+ .probe = onboard_hub_probe,
+ .of_compatible = onboard_hub_match,
+};
+device_platform_driver(onboard_hub_driver);
+
+MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
+MODULE_DESCRIPTION("Driver for discrete onboard USB hubs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h
new file mode 100644
index 0000000000..e379ca811a
--- /dev/null
+++ b/drivers/usb/misc/onboard_usb_hub.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2022, Google LLC
+ */
+
+#ifndef _USB_MISC_ONBOARD_USB_HUB_H
+#define _USB_MISC_ONBOARD_USB_HUB_H
+
+struct onboard_hub_pdata {
+ unsigned long reset_us; /* reset pulse width in us */
+};
+
+static const struct onboard_hub_pdata microchip_usb424_data = {
+ .reset_us = 1,
+};
+
+static const struct onboard_hub_pdata realtek_rts5411_data = {
+ .reset_us = 0,
+};
+
+static const struct onboard_hub_pdata ti_tusb8041_data = {
+ .reset_us = 3000,
+};
+
+static const struct onboard_hub_pdata genesys_gl850g_data = {
+ .reset_us = 3,
+};
+
+static const struct onboard_hub_pdata genesys_gl852g_data = {
+ .reset_us = 50,
+};
+
+static const struct onboard_hub_pdata vialab_vl817_data = {
+ .reset_us = 10,
+};
+
+static const struct of_device_id onboard_hub_match[] = {
+ { .compatible = "usb424,2514", .data = &microchip_usb424_data, },
+ { .compatible = "usb424,2517", .data = &microchip_usb424_data, },
+ { .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
+ { .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
+ { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
+ { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
+ { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
+ { .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
+ { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
+ { .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
+ { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
+ { .compatible = "usb2109,817", .data = &vialab_vl817_data, },
+ { .compatible = "usb2109,2817", .data = &vialab_vl817_data, },
+ {}
+};
+MODULE_DEVICE_TABLE(of, onboard_hub_match);
+
+#endif /* _USB_MISC_ONBOARD_USB_HUB_H */
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 4871faf451..465d97e779 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -116,7 +116,7 @@
#define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller"
struct usb251xb {
- struct device_d *dev;
+ struct device *dev;
struct i2c_client *i2c;
u8 skip_config;
int gpio_reset;
@@ -241,7 +241,7 @@ static void usb251xb_reset(struct usb251xb *hub, int state)
static int usb251xb_connect(struct usb251xb *hub)
{
- struct device_d *dev = hub->dev;
+ struct device *dev = hub->dev;
int err, i;
char i2c_wb[USB251XB_I2C_REG_SZ];
@@ -337,8 +337,8 @@ out_err:
static int usb251xb_get_ofdata(struct usb251xb *hub,
struct usb251xb_data *data)
{
- struct device_d *dev = hub->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = hub->dev;
+ struct device_node *np = dev->of_node;
int len, i;
u32 port, property_u32 = 0;
const u32 *cproperty_u32;
@@ -608,6 +608,7 @@ static const struct of_device_id usb251xb_of_match[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, usb251xb_of_match);
#else /* CONFIG_OFDEVICE */
static int usb251xb_get_ofdata(struct usb251xb *hub,
struct usb251xb_data *data)
@@ -618,8 +619,8 @@ static int usb251xb_get_ofdata(struct usb251xb *hub,
static int usb251xb_probe(struct usb251xb *hub)
{
- struct device_d *dev = hub->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = hub->dev;
+ struct device_node *np = dev->of_node;
const struct of_device_id *of_id = of_match_device(usb251xb_of_match,
dev);
int err;
@@ -644,7 +645,7 @@ static int usb251xb_probe(struct usb251xb *hub)
return 0;
}
-static int usb251xb_i2c_probe(struct device_d *dev)
+static int usb251xb_i2c_probe(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct usb251xb *hub;
@@ -670,7 +671,7 @@ static const struct platform_device_id usb251xb_id[] = {
{ /* sentinel */ }
};
-static struct driver_d usb251xb_i2c_driver = {
+static struct driver usb251xb_i2c_driver = {
.name = DRIVER_NAME,
.probe = usb251xb_i2c_probe,
.id_table = usb251xb_id,
diff --git a/drivers/usb/musb/am35x-phy-control.h b/drivers/usb/musb/am35x-phy-control.h
index c10d595a9e..b4eb585e71 100644
--- a/drivers/usb/musb/am35x-phy-control.h
+++ b/drivers/usb/musb/am35x-phy-control.h
@@ -17,6 +17,6 @@ static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on)
phy_ctrl->phy_wkup(phy_ctrl, id, on);
}
-struct phy_control *am335x_get_phy_control(struct device_d *dev);
+struct phy_control *am335x_get_phy_control(struct device *dev);
#endif
diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c
index 03f30d92aa..19d780d15b 100644
--- a/drivers/usb/musb/musb_am335x.c
+++ b/drivers/usb/musb/musb_am335x.c
@@ -3,11 +3,11 @@
#include <init.h>
#include <linux/clk.h>
-static int am335x_child_probe(struct device_d *dev)
+static int am335x_child_probe(struct device *dev)
{
int ret;
- ret = of_platform_populate(dev->device_node, NULL, dev);
+ ret = of_platform_populate(dev->of_node, NULL, dev);
if (ret)
return ret;
@@ -21,8 +21,9 @@ static __maybe_unused struct of_device_id am335x_child_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, am335x_child_dt_ids);
-static struct driver_d am335x_child_driver = {
+static struct driver am335x_child_driver = {
.name = "am335x_child_probe",
.probe = am335x_child_probe,
.of_compatible = DRV_OF_COMPAT(am335x_child_dt_ids),
diff --git a/drivers/usb/musb/musb_barebox.c b/drivers/usb/musb/musb_barebox.c
index 357a8f4281..81fdd6338f 100644
--- a/drivers/usb/musb/musb_barebox.c
+++ b/drivers/usb/musb/musb_barebox.c
@@ -2,8 +2,8 @@
#include <common.h>
#include <init.h>
#include <clock.h>
-#include <usb/musb.h>
-#include <usb/usb.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/usb.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/barebox-wrapper.h>
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index ab825553b5..9c6c4e7bb4 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -67,8 +67,8 @@
#include <common.h>
#include <init.h>
#include <clock.h>
-#include <usb/musb.h>
-#include <usb/usb.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/usb.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/barebox-wrapper.h>
@@ -1108,9 +1108,7 @@ fail2:
musb_platform_exit(musb);
fail1:
- if (status != -EPROBE_DEFER)
- dev_err(musb->controller,
- "musb_init_controller failed with status %d\n", status);
+ dev_err_probe(musb->controller, status, "musb_init_controller failed\n");
musb_free(musb);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 999ea5519f..d954719161 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -12,8 +12,8 @@
#include <poller.h>
#include <notifier.h>
-#include <usb/usb.h>
-#include <usb/phy.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/phy.h>
#include <linux/spinlock.h>
struct musb;
@@ -308,7 +308,7 @@ struct musb {
struct dma_controller *dma_controller;
- struct device_d *controller;
+ struct device *controller;
void __iomem *ctrl_base;
void __iomem *mregs;
@@ -324,7 +324,6 @@ struct musb {
u16 int_rx;
u16 int_tx;
- //struct device_d *phydev;
struct usb_host host;
struct usb_phy *xceiv;
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index ee0215024a..97b64302ec 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -17,8 +17,8 @@
#include <common.h>
#include <init.h>
#include <clock.h>
-#include <usb/usb.h>
-#include <usb/musb.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/musb.h>
#include <malloc.h>
#include <linux/err.h>
#include <linux/barebox-wrapper.h>
@@ -99,7 +99,7 @@ struct dsps_musb_wrapper {
* DSPS glue structure.
*/
struct dsps_glue {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
unsigned long flags;
enum musb_mode mode;
@@ -108,7 +108,7 @@ struct dsps_glue {
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
struct poller_async timer; /* otg_workaround timer */
uint64_t last_timer; /* last timer data for each instance */
- struct device_d otg_dev;
+ struct device otg_dev;
uint32_t otgmode;
struct musb_hdrc_platform_data pdata;
};
@@ -260,11 +260,11 @@ static int get_int_prop(struct device_node *dn, const char *s)
return val;
}
-static int get_musb_port_mode(struct device_d *dev)
+static int get_musb_port_mode(struct device *dev)
{
enum usb_dr_mode mode;
- mode = of_usb_get_dr_mode(dev->device_node, NULL);
+ mode = of_usb_get_dr_mode(dev->of_node, NULL);
switch (mode) {
case USB_DR_MODE_HOST:
return MUSB_PORT_MODE_HOST;
@@ -297,15 +297,15 @@ static int dsps_set_mode(void *ctx, enum usb_dr_mode mode)
return musb_init_controller(&glue->musb, &glue->pdata);
}
-static int dsps_probe(struct device_d *dev)
+static int dsps_probe(struct device *dev)
{
struct resource *iores[2];
struct musb_hdrc_platform_data *pdata;
struct musb_hdrc_config *config;
- struct device_node *dn = dev->device_node;
+ struct device_node *dn = dev->of_node;
const struct dsps_musb_wrapper *wrp;
struct device_node *phy_node;
- struct device_d *phy_dev;
+ struct device *phy_dev;
struct dsps_glue *glue;
int ret;
@@ -428,8 +428,9 @@ static __maybe_unused struct of_device_id musb_dsps_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, musb_dsps_dt_ids);
-static struct driver_d dsps_usbss_driver = {
+static struct driver dsps_usbss_driver = {
.name = "musb-dsps",
.probe = dsps_probe,
.of_compatible = DRV_OF_COMPAT(musb_dsps_dt_ids),
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index c28049c062..87d6602f74 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -991,8 +991,7 @@ static void musb_gadget_poll(struct usb_gadget *gadget)
static int musb_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int musb_gadget_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int musb_gadget_stop(struct usb_gadget *g);
static const struct usb_gadget_ops musb_gadget_operations = {
.get_frame = musb_gadget_get_frame,
@@ -1167,16 +1166,9 @@ err:
return retval;
}
-static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
+static void stop_activity(struct musb *musb)
{
- int i;
- struct musb_hw_ep *hw_ep;
-
- /* don't disconnect if it's not connected */
- if (musb->g.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
- else
- musb->g.speed = USB_SPEED_UNKNOWN;
+ musb->g.speed = USB_SPEED_UNKNOWN;
/* deactivate the hardware */
if (musb->softconnect) {
@@ -1184,25 +1176,6 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
musb_pullup(musb, 0);
}
musb_stop(musb);
-
- /* killing any outstanding requests will quiesce the driver;
- * then report disconnect
- */
- if (driver) {
- for (i = 0, hw_ep = musb->endpoints;
- i < musb->nr_endpoints;
- i++, hw_ep++) {
- musb_ep_select(musb->mregs, i);
- if (hw_ep->is_shared_fifo /* || !epnum */) {
- nuke(&hw_ep->ep_in, -ESHUTDOWN);
- } else {
- if (hw_ep->max_packet_sz_tx)
- nuke(&hw_ep->ep_in, -ESHUTDOWN);
- if (hw_ep->max_packet_sz_rx)
- nuke(&hw_ep->ep_out, -ESHUTDOWN);
- }
- }
- }
}
/*
@@ -1211,8 +1184,7 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
*
* @param driver the gadget driver to unregister
*/
-static int musb_gadget_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int musb_gadget_stop(struct usb_gadget *g)
{
struct musb *musb = gadget_to_musb(g);
unsigned long flags;
@@ -1226,10 +1198,7 @@ static int musb_gadget_stop(struct usb_gadget *g,
(void) musb_gadget_vbus_draw(&musb->g, 0);
- stop_activity(musb, driver);
-
- dev_dbg(musb->controller, "unregistering driver %s\n",
- driver ? driver->function : "(removed)");
+ stop_activity(musb);
musb->is_active = 0;
musb->gadget_driver = NULL;
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index af81f062e6..bbd1a35880 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -11,7 +11,7 @@
#define __MUSB_GADGET_H
#include <linux/list.h>
-#include <usb/gadget.h>
+#include <linux/usb/gadget.h>
#if IS_ENABLED(CONFIG_USB_MUSB_GADGET)
extern int musb_g_ep0_irq(struct musb *);
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 7a7c366521..5585f2e3b5 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -12,7 +12,7 @@
//#include <linux/scatterlist.h>
#include <linux/list.h>
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
#include <asm/unaligned.h>
/*
diff --git a/drivers/usb/musb/phy-am335x-control.c b/drivers/usb/musb/phy-am335x-control.c
index bc0f6b9485..313c67ef7e 100644
--- a/drivers/usb/musb/phy-am335x-control.c
+++ b/drivers/usb/musb/phy-am335x-control.c
@@ -8,7 +8,7 @@
#include "am35x-phy-control.h"
struct am335x_control_usb {
- struct device_d *dev;
+ struct device *dev;
void __iomem *phy_reg;
void __iomem *wkup;
spinlock_t lock;
@@ -102,13 +102,14 @@ static __maybe_unused struct of_device_id omap_control_usb_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, omap_control_usb_dt_ids);
-struct phy_control *am335x_get_phy_control(struct device_d *dev)
+struct phy_control *am335x_get_phy_control(struct device *dev)
{
struct device_node *node;
struct am335x_control_usb *ctrl_usb;
- node = of_parse_phandle(dev->device_node, "ti,ctrl_mod", 0);
+ node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0);
if (!node)
return ERR_PTR(-ENOENT);
@@ -125,7 +126,7 @@ struct phy_control *am335x_get_phy_control(struct device_d *dev)
EXPORT_SYMBOL(am335x_get_phy_control);
-static int am335x_control_usb_probe(struct device_d *dev)
+static int am335x_control_usb_probe(struct device *dev)
{
struct resource *iores;
/*struct resource *res;*/
@@ -169,7 +170,7 @@ free_ctrl:
return 0;
};
-static struct driver_d am335x_control_driver = {
+static struct driver am335x_control_driver = {
.name = "am335x-control-usb",
.probe = am335x_control_usb_probe,
.of_compatible = DRV_OF_COMPAT(omap_control_usb_dt_ids),
diff --git a/drivers/usb/musb/phy-am335x.c b/drivers/usb/musb/phy-am335x.c
index 67787cfec3..f2a12182e0 100644
--- a/drivers/usb/musb/phy-am335x.c
+++ b/drivers/usb/musb/phy-am335x.c
@@ -22,7 +22,7 @@ static int am335x_init(struct usb_phy *phy)
return 0;
}
-static int am335x_phy_probe(struct device_d *dev)
+static int am335x_phy_probe(struct device *dev)
{
struct am335x_usbphy *am_usbphy;
struct resource *iores;
@@ -43,7 +43,7 @@ static int am335x_phy_probe(struct device_d *dev)
goto err_release;
}
- am_usbphy->id = of_alias_get_id(dev->device_node, "phy");
+ am_usbphy->id = of_alias_get_id(dev->of_node, "phy");
if (am_usbphy->id < 0) {
dev_err(dev, "Missing PHY id: %d\n", am_usbphy->id);
ret = am_usbphy->id;
@@ -72,8 +72,9 @@ static __maybe_unused struct of_device_id am335x_phy_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, am335x_phy_dt_ids);
-static struct driver_d am335x_phy_driver = {
+static struct driver am335x_phy_driver = {
.name = "am335x-phy-driver",
.probe = am335x_phy_probe,
.of_compatible = DRV_OF_COMPAT(am335x_phy_dt_ids),
diff --git a/drivers/usb/otg/otgdev.c b/drivers/usb/otg/otgdev.c
index 29cb0d362b..5a86263430 100644
--- a/drivers/usb/otg/otgdev.c
+++ b/drivers/usb/otg/otgdev.c
@@ -2,10 +2,10 @@
#include <common.h>
#include <driver.h>
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
struct otg_mode {
- struct device_d dev;
+ struct device dev;
unsigned int var_mode;
unsigned int cur_mode;
int (*set_mode_callback)(void *ctx, enum usb_dr_mode mode);
@@ -42,7 +42,7 @@ static const char *otg_mode_names[] = {
[USB_DR_MODE_OTG] = "otg",
};
-static int register_otg_device(struct device_d *dev, struct otg_mode *otg)
+static int register_otg_device(struct device *dev, struct otg_mode *otg)
{
struct param_d *param_mode;
int ret;
@@ -58,20 +58,33 @@ static int register_otg_device(struct device_d *dev, struct otg_mode *otg)
return PTR_ERR_OR_ZERO(param_mode);
}
-static struct device_d otg_device = {
- .name = "otg",
- .id = DEVICE_ID_SINGLE,
+struct bus_type otg_bus_type = {
+ .name = "usbotg" /* "otg" is already taken for the alias */
};
-int usb_register_otg_device(struct device_d *parent,
+int otg_device_get_mode(struct device *dev)
+{
+ struct otg_mode *otg;
+
+ if (dev->bus != &otg_bus_type)
+ return -ENODEV;
+
+ otg = dev->priv;
+
+ return otg->cur_mode;
+}
+
+int usb_register_otg_device(struct device *parent,
int (*set_mode)(void *ctx, enum usb_dr_mode mode), void *ctx)
{
+ bool first_otg = list_empty(&otg_bus_type.device_list);
int ret;
struct otg_mode *otg;
otg = xzalloc(sizeof(*otg));
otg->dev.priv = otg;
otg->dev.parent = parent;
+ otg->dev.bus = &otg_bus_type;
otg->dev.id = DEVICE_ID_DYNAMIC;
dev_set_name(&otg->dev, "otg");
@@ -80,13 +93,19 @@ int usb_register_otg_device(struct device_d *parent,
otg->set_mode_callback = set_mode;
otg->ctx = ctx;
+ ret = register_otg_device(&otg->dev, otg);
+ if (ret)
+ return ret;
+
/* register otg.mode as an alias of otg0.mode */
- if (otg_device.parent == NULL) {
- otg_device.parent = parent;
- ret = register_otg_device(&otg_device, otg);
- if (ret)
- return ret;
- }
-
- return register_otg_device(&otg->dev, otg);
+ if (first_otg)
+ dev_add_alias(&otg->dev, "otg");
+
+ return 0;
+}
+
+static int otg_bus_init(void)
+{
+ return bus_register(&otg_bus_type);
}
+pure_initcall(otg_bus_init);
diff --git a/drivers/usb/otg/twl4030.c b/drivers/usb/otg/twl4030.c
index cd82148b2e..5cbf734ded 100644
--- a/drivers/usb/otg/twl4030.c
+++ b/drivers/usb/otg/twl4030.c
@@ -24,7 +24,7 @@
*/
#include <mfd/twl4030.h>
-#include <usb/twl4030.h>
+#include <linux/usb/twl4030.h>
#include <clock.h>
static int twl4030_usb_write(u8 address, u8 data)
diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c
index aa9470b7ae..d231b49b08 100644
--- a/drivers/usb/otg/ulpi.c
+++ b/drivers/usb/otg/ulpi.c
@@ -6,7 +6,7 @@
#include <common.h>
#include <io.h>
#include <errno.h>
-#include <usb/ulpi.h>
+#include <linux/usb/ulpi.h>
/* ULPIVIEW register bits */
#define ULPIVW_WU (1 << 31) /* Wakeup */
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 82e0d21ae5..be3b18dc66 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -49,7 +49,7 @@ static int usb_stor_Bulk_clear_endpt_stall(struct us_data *us, unsigned int pipe
/* Determine what the maximum LUN supported is */
int usb_stor_Bulk_max_lun(struct us_data *us)
{
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
int len, ret = 0;
unsigned char *iobuf = dma_alloc(1);
@@ -85,40 +85,45 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev,
void *data, u32 datalen)
{
struct us_data *us = usb_blkdev->us;
- struct device_d *dev = &us->pusb_dev->dev;
- struct bulk_cb_wrap cbw;
- struct bulk_cs_wrap csw;
+ struct device *dev = &us->pusb_dev->dev;
+ struct bulk_cb_wrap *cbw;
+ struct bulk_cs_wrap *csw;
int actlen, data_actlen;
int result;
unsigned int residue;
unsigned int pipein = usb_rcvbulkpipe(us->pusb_dev, us->recv_bulk_ep);
unsigned int pipeout = usb_sndbulkpipe(us->pusb_dev, us->send_bulk_ep);
int dir_in = US_DIRECTION(cmd[0]);
+ int ret = 0;
+
+ cbw = dma_alloc(sizeof(*cbw));
+ csw = dma_alloc(sizeof(*csw));
/* set up the command wrapper */
- cbw.Signature = cpu_to_le32(US_BULK_CB_SIGN);
- cbw.DataTransferLength = cpu_to_le32(datalen);
- cbw.Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT);
- cbw.Tag = ++cbw_tag;
- cbw.Lun = usb_blkdev->lun;
- cbw.Length = cmdlen;
+ cbw->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+ cbw->DataTransferLength = cpu_to_le32(datalen);
+ cbw->Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT);
+ cbw->Tag = ++cbw_tag;
+ cbw->Lun = usb_blkdev->lun;
+ cbw->Length = cmdlen;
/* copy the command payload */
- memset(cbw.CDB, 0, sizeof(cbw.CDB));
- memcpy(cbw.CDB, cmd, cbw.Length);
+ memset(cbw->CDB, 0, sizeof(cbw->CDB));
+ memcpy(cbw->CDB, cmd, cbw->Length);
/* send it to out endpoint */
dev_dbg(dev, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n",
- le32_to_cpu(cbw.Signature), cbw.Tag,
- le32_to_cpu(cbw.DataTransferLength), cbw.Flags,
- (cbw.Lun >> 4), (cbw.Lun & 0x0F),
- cbw.Length);
- result = usb_bulk_msg(us->pusb_dev, pipeout, &cbw, US_BULK_CB_WRAP_LEN,
+ le32_to_cpu(cbw->Signature), cbw->Tag,
+ le32_to_cpu(cbw->DataTransferLength), cbw->Flags,
+ (cbw->Lun >> 4), (cbw->Lun & 0x0F),
+ cbw->Length);
+ result = usb_bulk_msg(us->pusb_dev, pipeout, cbw, US_BULK_CB_WRAP_LEN,
&actlen, USB_BULK_TO);
dev_dbg(dev, "Bulk command transfer result=%d\n", result);
if (result < 0) {
usb_stor_Bulk_reset(us);
- return USB_STOR_TRANSPORT_FAILED;
+ ret = USB_STOR_TRANSPORT_FAILED;
+ goto fail;
}
/* DATA STAGE */
@@ -141,13 +146,14 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev,
if (result < 0) {
dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status);
usb_stor_Bulk_reset(us);
- return USB_STOR_TRANSPORT_FAILED;
+ ret = USB_STOR_TRANSPORT_FAILED;
+ goto fail;
}
}
/* STATUS phase + error handling */
dev_dbg(dev, "Attempting to get CSW...\n");
- result = usb_bulk_msg(us->pusb_dev, pipein, &csw, US_BULK_CS_WRAP_LEN,
+ result = usb_bulk_msg(us->pusb_dev, pipein, csw, US_BULK_CS_WRAP_LEN,
&actlen, USB_BULK_TO);
/* did the endpoint stall? */
@@ -158,7 +164,7 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev,
if (result >= 0) {
dev_dbg(dev, "Attempting to get CSW...\n");
result = usb_bulk_msg(us->pusb_dev, pipein,
- &csw, US_BULK_CS_WRAP_LEN,
+ csw, US_BULK_CS_WRAP_LEN,
&actlen, USB_BULK_TO);
}
}
@@ -166,35 +172,39 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev,
if (result < 0) {
dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status);
usb_stor_Bulk_reset(us);
- return USB_STOR_TRANSPORT_FAILED;
+ ret = USB_STOR_TRANSPORT_FAILED;
+ goto fail;
}
/* check bulk status */
- residue = le32_to_cpu(csw.Residue);
+ residue = le32_to_cpu(csw->Residue);
dev_dbg(dev, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
- le32_to_cpu(csw.Signature), csw.Tag, residue, csw.Status);
- if (csw.Signature != cpu_to_le32(US_BULK_CS_SIGN)) {
+ le32_to_cpu(csw->Signature), csw->Tag, residue, csw->Status);
+ if (csw->Signature != cpu_to_le32(US_BULK_CS_SIGN)) {
dev_dbg(dev, "Bad CSW signature\n");
usb_stor_Bulk_reset(us);
- return USB_STOR_TRANSPORT_FAILED;
- } else if (csw.Tag != cbw_tag) {
+ ret = USB_STOR_TRANSPORT_FAILED;
+ } else if (csw->Tag != cbw_tag) {
dev_dbg(dev, "Mismatching tag\n");
usb_stor_Bulk_reset(us);
- return USB_STOR_TRANSPORT_FAILED;
- } else if (csw.Status >= US_BULK_STAT_PHASE) {
+ ret = USB_STOR_TRANSPORT_FAILED;
+ } else if (csw->Status >= US_BULK_STAT_PHASE) {
dev_dbg(dev, "Status >= phase\n");
usb_stor_Bulk_reset(us);
- return USB_STOR_TRANSPORT_ERROR;
+ ret = USB_STOR_TRANSPORT_ERROR;
} else if (residue > datalen) {
dev_dbg(dev, "residue (%uB) > req data (%uB)\n",
residue, datalen);
- return USB_STOR_TRANSPORT_FAILED;
- } else if (csw.Status == US_BULK_STAT_FAIL) {
+ ret = USB_STOR_TRANSPORT_FAILED;
+ } else if (csw->Status == US_BULK_STAT_FAIL) {
dev_dbg(dev, "FAILED\n");
- return USB_STOR_TRANSPORT_FAILED;
+ ret = USB_STOR_TRANSPORT_FAILED;
}
- return 0;
+fail:
+ dma_free(cbw);
+ dma_free(csw);
+ return ret;
}
@@ -203,7 +213,7 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev,
*/
int usb_stor_Bulk_reset(struct us_data *us)
{
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
int result;
int result2;
unsigned int pipe;
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 2962d08983..cc241e69be 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -10,10 +10,11 @@
#include <common.h>
#include <init.h>
#include <malloc.h>
+#include <dma.h>
#include <errno.h>
#include <scsi.h>
-#include <usb/usb.h>
-#include <usb/usb_defs.h>
+#include <linux/usb/usb.h>
+#include <linux/usb/usb_defs.h>
#include <asm/unaligned.h>
@@ -30,10 +31,10 @@
static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev)
{
struct us_data *us = usb_blkdev->us;
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
u8 cmd[6];
const u8 datalen = 18;
- u8 *data = xzalloc(datalen);
+ u8 *data = dma_alloc(datalen);
dev_dbg(dev, "SCSI_REQ_SENSE\n");
@@ -44,7 +45,7 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev)
dev_dbg(dev, "Request Sense returned %02X %02X %02X\n",
data[2], data[12], data[13]);
- free(data);
+ dma_free(data);
return 0;
}
@@ -73,7 +74,7 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev,
int retries, int request_sense_delay_ms)
{
struct us_data *us = usb_blkdev->us;
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
int i, ret;
for (i = 0; i <= retries; i++) {
@@ -100,11 +101,11 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev,
static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev)
{
- struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
+ struct device *dev = &usb_blkdev->us->pusb_dev->dev;
int ret;
u8 cmd[6];
const u16 datalen = 36;
- u8 *data = xzalloc(datalen);
+ u8 *data = dma_alloc(datalen);
memset(cmd, 0, sizeof(cmd));
cmd[0] = SCSI_INQUIRY;
@@ -126,7 +127,7 @@ static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev)
// TODO: process and store device info
exit:
- free(data);
+ dma_free(data);
return ret;
}
@@ -151,10 +152,10 @@ static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev, u64 timeout_n
static int read_capacity_16(struct us_blk_dev *usb_blkdev)
{
- struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
+ struct device *dev = &usb_blkdev->us->pusb_dev->dev;
unsigned char cmd[16];
const u8 datalen = 32;
- u8 *data = xzalloc(datalen);
+ u8 *data = dma_alloc(datalen);
int ret;
sector_t lba;
unsigned sector_size;
@@ -169,7 +170,7 @@ static int read_capacity_16(struct us_blk_dev *usb_blkdev)
if (ret < 0) {
dev_warn(dev, "Read Capacity(16) failed\n");
- return ret;
+ goto fail;
}
/* Note this is logical, not physical sector size */
@@ -181,21 +182,25 @@ static int read_capacity_16(struct us_blk_dev *usb_blkdev)
if ((data[12] & 1) == 1) {
dev_warn(dev, "Protection unsupported\n");
- return -ENOTSUPP;
+ ret = -ENOTSUPP;
+ goto fail;
}
usb_blkdev->blk.blockbits = SECTOR_SHIFT;
usb_blkdev->blk.num_blocks = lba + 1;
- return sector_size;
+ ret = sector_size;
+fail:
+ dma_free(data);
+ return ret;
}
static int read_capacity_10(struct us_blk_dev *usb_blkdev)
{
- struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
+ struct device *dev = &usb_blkdev->us->pusb_dev->dev;
unsigned char cmd[16];
const u32 datalen = 8;
- __be32 *data = xzalloc(datalen);
+ __be32 *data = dma_alloc(datalen);
int ret;
sector_t lba;
unsigned sector_size;
@@ -208,7 +213,7 @@ static int read_capacity_10(struct us_blk_dev *usb_blkdev)
if (ret < 0) {
dev_warn(dev, "Read Capacity(10) failed\n");
- return ret;
+ goto fail;
}
sector_size = be32_to_cpu(data[1]);
@@ -223,7 +228,10 @@ static int read_capacity_10(struct us_blk_dev *usb_blkdev)
usb_blkdev->blk.num_blocks = lba + 1;
usb_blkdev->blk.blockbits = SECTOR_SHIFT;
- return SECTOR_SIZE;
+ ret = SECTOR_SIZE;
+fail:
+ dma_free(data);
+ return ret;
}
static int usb_stor_io_16(struct us_blk_dev *usb_blkdev, u8 opcode,
@@ -269,7 +277,7 @@ static int usb_stor_blk_io(struct block_device *disk_dev,
struct us_blk_dev,
blk);
struct us_data *us = pblk_dev->us;
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
int result;
/* ensure unit ready */
@@ -342,7 +350,7 @@ static struct block_device_ops usb_mass_storage_ops = {
static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
{
struct us_data *us = pblk_dev->us;
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
int result;
/* get device info */
@@ -388,7 +396,7 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
/* Create and register a disk device for the specified LUN */
static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun)
{
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
struct us_blk_dev *pblk_dev;
int result;
@@ -413,6 +421,7 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun)
pblk_dev->blk.cdev.name = basprintf("disk%d", result);
pblk_dev->blk.blockbits = SECTOR_SHIFT;
+ pblk_dev->blk.type = BLK_TYPE_USB;
result = blockdevice_register(&pblk_dev->blk);
if (result != 0) {
@@ -420,11 +429,6 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun)
goto BadDevice;
}
- /* create partitions on demand */
- result = parse_partition_table(&pblk_dev->blk);
- if (result != 0)
- dev_warn(dev, "No partition table found\n");
-
list_add_tail(&pblk_dev->list, &us->blk_dev_list);
dev_dbg(dev, "USB disk device successfully added\n");
@@ -443,7 +447,7 @@ BadDevice:
/* Get the transport settings */
static void get_transport(struct us_data *us)
{
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
switch (us->protocol) {
case US_PR_BULK:
us->transport_name = "Bulk";
@@ -458,7 +462,7 @@ static void get_transport(struct us_data *us)
/* Get the endpoint settings */
static int get_pipes(struct us_data *us, struct usb_interface *intf)
{
- struct device_d *dev = &us->pusb_dev->dev;
+ struct device *dev = &us->pusb_dev->dev;
unsigned int i;
struct usb_endpoint_descriptor *ep;
struct usb_endpoint_descriptor *ep_in = NULL;
@@ -499,7 +503,7 @@ static int get_pipes(struct us_data *us, struct usb_interface *intf)
/* Scan device's LUNs, registering a disk device for each LUN */
static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us)
{
- struct device_d *dev = &usbdev->dev;
+ struct device *dev = &usbdev->dev;
unsigned char lun;
int num_devs = 0;
@@ -523,7 +527,7 @@ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us)
static int usb_stor_probe(struct usb_device *usbdev,
const struct usb_device_id *id)
{
- struct device_d *dev = &usbdev->dev;
+ struct device *dev = &usbdev->dev;
struct us_data *us;
int result;
int ifno;
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index b9626b303e..ae16d7b60b 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -10,7 +10,7 @@
#ifndef _STORAGE_USB_H_
#define _STORAGE_USB_H_
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
#include <block.h>
#include <disks.h>
#include <scsi.h>
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
new file mode 100644
index 0000000000..3b32a4e05a
--- /dev/null
+++ b/drivers/usb/typec/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+
+config TYPEC
+ prompt "Compile USB Type-C framework support" if COMPILE_TEST
+ bool
+
+config TYPEC_TUSB320
+ tristate "TI TUSB320 Type-C port controller"
+ depends on I2C
+ select REGMAP_I2C
+ select TYPEC
+ help
+ Say Y or here if your system has a TI TUSB320 Type-C port controller.
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
new file mode 100644
index 0000000000..456b94afbf
--- /dev/null
+++ b/drivers/usb/typec/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_TYPEC) += class.o
+obj-$(CONFIG_TYPEC_TUSB320) += tusb320.o
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
new file mode 100644
index 0000000000..7f498550f8
--- /dev/null
+++ b/drivers/usb/typec/class.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <module.h>
+#include <driver.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
+#include <param.h>
+
+enum typec_param_accessory {
+ TYPEC_PARAM_ACCESSORY_NONE,
+ TYPEC_PARAM_ACCESSORY_AUDIO,
+ TYPEC_PARAM_ACCESSORY_DEBUG,
+};
+
+struct typec_port {
+ struct device dev;
+ const struct typec_operations *ops;
+ int pwr_role; /* enum typec_role */
+ int usb_role; /* enum usb_role role */
+ int accessory; /* enum typec_param_accessory */
+};
+
+/**
+ * typec_set_pwr_role - Report power role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report power role changes.
+ */
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
+{
+ port->pwr_role = role;
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_role);
+
+static inline enum typec_param_accessory typec_mode_to_accessory(int mode)
+{
+ switch (mode) {
+ case TYPEC_MODE_AUDIO:
+ return TYPEC_PARAM_ACCESSORY_AUDIO;
+ case TYPEC_MODE_DEBUG:
+ return TYPEC_PARAM_ACCESSORY_DEBUG;
+ default:
+ return TYPEC_PARAM_ACCESSORY_NONE;
+ }
+}
+
+/**
+ * typec_set_mode - Set mode of operation for USB Type-C connector
+ * @port: USB Type-C connector
+ * @mode: Accessory Mode, USB Operation or Safe State
+ *
+ * Configure @port for Accessory Mode @mode. This function will configure the
+ * muxes needed for @mode.
+ */
+int typec_set_mode(struct typec_port *port, int mode)
+{
+ port->accessory = typec_mode_to_accessory(mode);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_mode);
+
+/**
+ * typec_set_role - Set USB role for a Type-C connector
+ * @port: USB Type-C connector
+ * @role: USB role to be switched to
+ *
+ * Set USB role @role for @sw. This is equivalent to Linux
+ * usb_role_switch_set_role();
+ */
+int typec_set_role(struct typec_port *port, enum usb_role role)
+{
+ port->usb_role = role;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_role);
+
+/**
+ * typec_get_drvdata - Return private driver data pointer
+ * @port: USB Type-C port
+ */
+void *typec_get_drvdata(struct typec_port *port)
+{
+ return port->dev.priv;
+}
+EXPORT_SYMBOL_GPL(typec_get_drvdata);
+
+static int typec_register_port_dev(struct typec_port *port, const char *name, int id)
+{
+ port->dev.id = id;
+ dev_set_name(&port->dev, name);
+
+ return register_device(&port->dev);
+}
+
+static const char * const usb_role_names[] = {
+ [USB_ROLE_NONE] = "none",
+ [USB_ROLE_HOST] = "host",
+ [USB_ROLE_DEVICE] = "device",
+};
+
+static const char * const pwr_role_names[] = {
+ [TYPEC_SINK] = "sink",
+ [TYPEC_SOURCE] = "source",
+};
+
+static const char * const accessory_names[] = {
+ [TYPEC_PARAM_ACCESSORY_NONE] = "none",
+ [TYPEC_PARAM_ACCESSORY_AUDIO] = "audio", /* analog */
+ [TYPEC_PARAM_ACCESSORY_DEBUG] = "debug",
+};
+
+static int typec_param_update(struct param_d *p, void *priv)
+{
+ struct typec_port *port = priv;
+
+ return port->ops->poll(port);
+}
+
+/**
+ * typec_register_port - Register a USB Type-C Port
+ * @parent: Parent device
+ * @cap: Description of the port
+ *
+ * Registers a device for USB Type-C Port described in @cap.
+ *
+ * Returns handle to the port on success or ERR_PTR on failure.
+ */
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap)
+{
+ struct typec_port *port;
+ struct device *dev;
+ const char *alias;
+ int ret;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ port->ops = cap->ops;
+ dev = &port->dev;
+ dev->parent = parent;
+ dev->of_node = cap->of_node;
+ dev->priv = cap->driver_data;
+
+ alias = dev->of_node ? of_alias_get(dev->of_node) : NULL;
+ if (alias)
+ ret = typec_register_port_dev(port, alias, DEVICE_ID_SINGLE);
+ if (!alias || ret)
+ ret = typec_register_port_dev(port, "typec", DEVICE_ID_DYNAMIC);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ of_platform_device_dummy_drv(dev);
+ if (dev->of_node)
+ dev->of_node->dev = dev;
+
+ dev_add_param_enum(dev, "usb_role", param_set_readonly, typec_param_update,
+ &port->usb_role, usb_role_names,
+ ARRAY_SIZE(usb_role_names), port);
+ dev_add_param_enum(dev, "pwr_role", param_set_readonly, typec_param_update,
+ &port->pwr_role, pwr_role_names,
+ ARRAY_SIZE(pwr_role_names), port);
+ dev_add_param_enum(dev, "accessory", param_set_readonly, typec_param_update,
+ &port->accessory, accessory_names,
+ ARRAY_SIZE(accessory_names), port);
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/tusb320.c b/drivers/usb/typec/tusb320.c
new file mode 100644
index 0000000000..90a846b0fb
--- /dev/null
+++ b/drivers/usb/typec/tusb320.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Based on the Linux driver:
+ * drivers/typec/typec-tusb320.c - TUSB320 typec driver
+ *
+ * Copyright (C) 2020 National Instruments Corporation
+ * Author: Michael Auchter <michael.auchter@ni.com>
+ */
+
+#include <linux/bitfield.h>
+#include <i2c/i2c.h>
+#include <init.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <module.h>
+#include <linux/regmap.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
+
+#define TUSB320_REG8 0x8
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6)
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x0
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x1
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x2
+#define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4)
+#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x0
+#define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1
+#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2
+#define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3
+#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 1)
+#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0
+#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4
+#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG 0x5
+#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP 0x6
+#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP 0x7
+#define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0)
+
+#define TUSB320_REG9 0x9
+#define TUSB320_REG9_ATTACHED_STATE GENMASK(7, 6)
+#define TUSB320_REG9_CABLE_DIRECTION BIT(5)
+#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)
+
+enum tusb320_attached_state {
+ TUSB320_ATTACHED_STATE_NONE,
+ TUSB320_ATTACHED_STATE_DFP,
+ TUSB320_ATTACHED_STATE_UFP,
+ TUSB320_ATTACHED_STATE_ACC,
+};
+
+struct tusb320_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct typec_port *port;
+ struct typec_capability cap;
+};
+
+static int tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9)
+{
+ struct typec_port *port = priv->port;
+ int typec_mode;
+ enum typec_role pwr_role;
+ enum usb_role usb_role;
+ u8 state, accessory;
+ int ret, reg8;
+
+ ret = regmap_read(priv->regmap, TUSB320_REG8, &reg8);
+ if (ret)
+ return ret;
+
+ state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9);
+ accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8);
+
+ switch (state) {
+ case TUSB320_ATTACHED_STATE_DFP:
+ typec_mode = TYPEC_MODE_USB2;
+ usb_role = USB_ROLE_HOST;
+ pwr_role = TYPEC_SOURCE;
+ break;
+ case TUSB320_ATTACHED_STATE_UFP:
+ typec_mode = TYPEC_MODE_USB2;
+ usb_role = USB_ROLE_DEVICE;
+ pwr_role = TYPEC_SINK;
+ break;
+ case TUSB320_ATTACHED_STATE_ACC:
+ /*
+ * Accessory detected. For debug accessories, just make some
+ * qualified guesses as to the role for lack of a better option.
+ */
+ if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO ||
+ accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) {
+ typec_mode = TYPEC_MODE_AUDIO;
+ usb_role = USB_ROLE_NONE;
+ pwr_role = TYPEC_SINK;
+ break;
+ } else if (accessory ==
+ TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) {
+ typec_mode = TYPEC_MODE_DEBUG;
+ pwr_role = TYPEC_SOURCE;
+ usb_role = USB_ROLE_HOST;
+ break;
+ } else if (accessory ==
+ TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) {
+ typec_mode = TYPEC_MODE_DEBUG;
+ pwr_role = TYPEC_SINK;
+ usb_role = USB_ROLE_DEVICE;
+ break;
+ }
+
+ dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n",
+ accessory);
+
+ fallthrough;
+ default:
+ typec_mode = TYPEC_MODE_USB2;
+ usb_role = USB_ROLE_NONE;
+ pwr_role = TYPEC_SINK;
+ break;
+ }
+
+ typec_set_pwr_role(port, pwr_role);
+ typec_set_mode(port, typec_mode);
+ typec_set_role(port, usb_role);
+
+ return 0;
+}
+
+static int tusb320_state_update_handler(struct tusb320_priv *priv,
+ bool force_update)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(priv->regmap, TUSB320_REG9, &reg);
+ if (ret)
+ return ret;
+
+ if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS))
+ return 0;
+
+ ret = tusb320_typec_irq_handler(priv, reg);
+
+ regmap_write(priv->regmap, TUSB320_REG9, reg);
+
+ return ret;
+}
+
+static irqreturn_t tusb320_irq_handler(struct typec_port *port)
+{
+ struct tusb320_priv *priv = typec_get_drvdata(port);
+
+ return tusb320_state_update_handler(priv, false);
+}
+
+static const struct typec_operations tusb320_typec_ops = {
+ .poll = tusb320_irq_handler,
+};
+
+static const struct regmap_config tusb320_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int tusb320_typec_probe(struct i2c_client *client,
+ struct tusb320_priv *priv)
+{
+ struct device_node *connector;
+
+ connector = of_get_child_by_name(client->dev.of_node, "connector");
+
+ priv->cap.driver_data = priv;
+ priv->cap.ops = &tusb320_typec_ops;
+ priv->cap.of_node = connector;
+
+ priv->port = typec_register_port(&client->dev, &priv->cap);
+
+ return PTR_ERR_OR_ZERO(priv->port);
+}
+
+static int tusb320_probe(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tusb320_priv *priv;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->dev = &client->dev;
+ i2c_set_clientdata(client, priv);
+
+ priv->regmap = regmap_init_i2c(client, &tusb320_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ ret = tusb320_typec_probe(client, priv);
+ if (ret)
+ return ret;
+
+ /* update initial state */
+ tusb320_state_update_handler(priv, true);
+
+ return ret;
+}
+
+static const struct of_device_id tusb320_typec_dt_match[] = {
+ { .compatible = "ti,tusb320" },
+ { .compatible = "ti,tusb320l" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tusb320_typec_dt_match);
+
+static struct driver tusb320_typec_driver = {
+ .name = "typec-tusb320",
+ .of_match_table = tusb320_typec_dt_match,
+ .probe = tusb320_probe,
+};
+device_i2c_driver(tusb320_typec_driver);
+
+MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");
+MODULE_DESCRIPTION("TI TUSB320 Type-C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a20b7bbee9..9e176d3198 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -31,7 +31,8 @@ config DRIVER_VIDEO_ATMEL_HLCD
config DRIVER_VIDEO_EFI_GOP
bool "EFI Graphics Output Protocol (GOP)"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
+ depends on X86
config DRIVER_VIDEO_IMX
bool "i.MX framebuffer driver"
@@ -67,12 +68,6 @@ config DRIVER_VIDEO_STM32_LTDC
Say 'Y' here to enable framebuffer and splash screen support for
STM32 and STM32MP1.
-config DRIVER_VIDEO_S3C24XX
- bool "S3C244x framebuffer driver"
- depends on ARCH_S3C24xx
- help
- Add support for the S3C244x LCD controller.
-
config DRIVER_VIDEO_OMAP
bool "OMAP framebuffer driver"
depends on ARCH_OMAP4 || COMPILE_TEST
@@ -81,13 +76,6 @@ config DRIVER_VIDEO_OMAP
driver only supports OMAP4 SoCs in DISPC parallel mode on
LCD2 (MIPI DPI).
-if DRIVER_VIDEO_S3C24XX
-
-config DRIVER_VIDEO_S3C_VERBOSE
- bool "S3C244x verbose framebuffer info"
-
-endif
-
config DRIVER_VIDEO_SDL
bool "SDL framebuffer driver"
depends on SANDBOX
@@ -123,6 +111,12 @@ config DRIVER_VIDEO_SIMPLEFB
Add support for setting up the kernel's simple framebuffer driver
based on the active barebox framebuffer.
+config DRIVER_VIDEO_RAMFB
+ bool "QEMU RamFB support"
+ select QEMU_FW_CFG
+ help
+ Add support for setting up a QEMU RamFB driver.
+
config DRIVER_VIDEO_EDID
bool "Add EDID support"
help
@@ -192,4 +186,16 @@ config DRIVER_VIDEO_PANEL_ILITEK_ILI9341
QVGA (240x320) RGB panels. support serial & parallel rgb
interface.
+config DRIVER_VIDEO_PANEL_MIPI_DBI
+ tristate "DRM support for MIPI DBI compatible panels"
+ depends on OFTREE && SPI
+ select DRIVER_VIDEO_MIPI_DBI
+ select FIRMWARE
+ select VIDEO_VPL
+ help
+ Say Y here if you want to enable support for MIPI DBI compatible
+ panels. The controller command setup can be provided using a
+ firmware file. For more information see
+ https://github.com/notro/panel-mipi-dbi/wiki.
+
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9ec0420cca..85cffb5a33 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
+obj-$(CONFIG_DRIVER_VIDEO_PANEL_MIPI_DBI) += panel-mipi-dbi.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
@@ -18,13 +19,13 @@ obj-$(CONFIG_DRIVER_VIDEO_STM) += stm.o
obj-$(CONFIG_DRIVER_VIDEO_STM32_LTDC) += stm32_ltdc.o
obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPU) += imx-ipu-fb.o
-obj-$(CONFIG_DRIVER_VIDEO_S3C24XX) += s3c24xx.o
obj-$(CONFIG_DRIVER_VIDEO_PXA) += pxa.o
obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o
obj-$(CONFIG_DRIVER_VIDEO_OMAP) += omap.o
obj-$(CONFIG_DRIVER_VIDEO_BCM283X) += bcm2835.o
obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB_CLIENT) += simplefb-client.o
obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB) += simplefb-fixup.o
+obj-$(CONFIG_DRIVER_VIDEO_RAMFB) += ramfb.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += imx-ipu-v3/
obj-$(CONFIG_DRIVER_VIDEO_EFI_GOP) += efi_gop.o
obj-$(CONFIG_DRIVER_VIDEO_FB_SSD1307) += ssd1307fb.o
diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c
index 4c05ac5821..0a24493907 100644
--- a/drivers/video/atmel_hlcdfb.c
+++ b/drivers/video/atmel_hlcdfb.c
@@ -9,9 +9,9 @@
#include <io.h>
#include <init.h>
#include <linux/clk.h>
-#include <mach/hardware.h>
-#include <mach/atmel_hlcdc.h>
-#include <mach/cpu.h>
+#include <mach/at91/hardware.h>
+#include <mach/at91/atmel_hlcdc.h>
+#include <mach/at91/cpu.h>
#include <errno.h>
#include "atmel_lcdfb.h"
@@ -260,12 +260,12 @@ struct atmel_lcdfb_devdata atmel_hlcdfb_data = {
.dma_desc_size = sizeof(struct atmel_hlcd_dma_desc),
};
-static int atmel_hlcdc_probe(struct device_d *dev)
+static int atmel_hlcdc_probe(struct device *dev)
{
return atmel_lcdc_register(dev, &atmel_hlcdfb_data);
}
-static struct driver_d atmel_hlcdc_driver = {
+static struct driver atmel_hlcdc_driver = {
.name = "atmel_hlcdfb",
.probe = atmel_hlcdc_probe,
};
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 38b4b8df39..5d8dc8f8b9 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -8,7 +8,7 @@
#include <common.h>
#include <io.h>
#include <init.h>
-#include <mach/hardware.h>
+#include <mach/at91/hardware.h>
#include <errno.h>
#include <linux/clk.h>
@@ -223,7 +223,7 @@ struct atmel_lcdfb_devdata atmel_lcdfb_data = {
.limit_screeninfo = atmel_lcdfb_limit_screeninfo,
};
-static int atmel_lcdc_probe(struct device_d *dev)
+static int atmel_lcdc_probe(struct device *dev)
{
return atmel_lcdc_register(dev, &atmel_lcdfb_data);
}
@@ -263,8 +263,9 @@ static __maybe_unused struct of_device_id atmel_lcdfb_compatible[] = {
{ .compatible = "atmel,at32ap-lcdc", .data = &at32ap_config, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, atmel_lcdfb_compatible);
-static struct driver_d atmel_lcdc_driver = {
+static struct driver atmel_lcdc_driver = {
.name = "atmel_lcdfb",
.probe = atmel_lcdc_probe,
.of_compatible = DRV_OF_COMPAT(atmel_lcdfb_compatible),
diff --git a/drivers/video/atmel_lcdfb.h b/drivers/video/atmel_lcdfb.h
index 110c71bdb2..7aa058e198 100644
--- a/drivers/video/atmel_lcdfb.h
+++ b/drivers/video/atmel_lcdfb.h
@@ -24,7 +24,7 @@ struct atmel_lcdfb_devdata {
struct atmel_lcdfb_info {
struct fb_info info;
void __iomem *mmio;
- struct device_d *device;
+ struct device *device;
unsigned int guard_time;
unsigned int smem_len;
@@ -49,4 +49,4 @@ struct atmel_lcdfb_info {
#define ATMEL_LCDC_STOP_NOWAIT (1 << 0)
-int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data);
+int atmel_lcdc_register(struct device *dev, struct atmel_lcdfb_devdata *data);
diff --git a/drivers/video/atmel_lcdfb_core.c b/drivers/video/atmel_lcdfb_core.c
index 17f754b566..9d3e6682b6 100644
--- a/drivers/video/atmel_lcdfb_core.c
+++ b/drivers/video/atmel_lcdfb_core.c
@@ -14,7 +14,7 @@
#include <linux/clk.h>
#include <malloc.h>
-#include <mach/cpu.h>
+#include <mach/at91/cpu.h>
#include "atmel_lcdfb.h"
@@ -62,7 +62,7 @@ static void atmel_lcdc_disable_controller(struct fb_info *fb_info)
static int atmel_lcdfb_check_var(struct fb_info *info)
{
- struct device_d *dev = &info->dev;
+ struct device *dev = &info->dev;
struct atmel_lcdfb_info *sinfo = info->priv;
struct fb_videomode *mode = info->mode;
unsigned long clk_value_khz;
@@ -237,7 +237,7 @@ static struct fb_ops atmel_lcdc_ops = {
.fb_disable = atmel_lcdc_disable_controller,
};
-static int power_control_init(struct device_d *dev,
+static int power_control_init(struct device *dev,
struct atmel_lcdfb_info *sinfo,
int gpio,
bool active_low)
@@ -294,7 +294,7 @@ static int of_get_wiring_mode(struct device_node *np,
return 0;
}
-static int of_get_power_control(struct device_d *dev,
+static int of_get_power_control(struct device *dev,
struct device_node *np,
struct atmel_lcdfb_info *sinfo)
{
@@ -311,7 +311,7 @@ static int of_get_power_control(struct device_d *dev,
return power_control_init(dev, sinfo, gpio, active_low);
}
-static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo)
+static int lcdfb_of_init(struct device *dev, struct atmel_lcdfb_info *sinfo)
{
struct fb_info *info = &sinfo->info;
struct display_timings *modes;
@@ -328,7 +328,7 @@ static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo)
}
/* Required properties */
- display = of_parse_phandle(dev->device_node, "display", 0);
+ display = of_parse_phandle(dev->of_node, "display", 0);
if (!display) {
dev_err(dev, "no display phandle\n");
return -ENOENT;
@@ -378,7 +378,8 @@ err:
return ret;
}
-static int lcdfb_pdata_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo)
+static int lcdfb_pdata_init(struct device *dev,
+ struct atmel_lcdfb_info *sinfo)
{
struct atmel_lcdfb_platform_data *pdata;
struct fb_info *info;
@@ -422,7 +423,7 @@ err:
return ret;
}
-int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data)
+int atmel_lcdc_register(struct device *dev, struct atmel_lcdfb_devdata *data)
{
struct atmel_lcdfb_info *sinfo;
const char *bus_clk_name;
@@ -450,7 +451,7 @@ int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data)
}
bus_clk_name = "hck1";
} else {
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node)
return -EINVAL;
ret = lcdfb_of_init(dev, sinfo);
diff --git a/drivers/video/backlight-pwm.c b/drivers/video/backlight-pwm.c
index ffa58c2b2d..87358ca778 100644
--- a/drivers/video/backlight-pwm.c
+++ b/drivers/video/backlight-pwm.c
@@ -101,10 +101,10 @@ static int backlight_pwm_set(struct backlight_device *backlight,
return backlight_pwm_disable(pwm_backlight);
}
-static int pwm_backlight_parse_dt(struct device_d *dev,
- struct pwm_backlight *pwm_backlight)
+static int pwm_backlight_parse_dt(struct device *dev,
+ struct pwm_backlight *pwm_backlight)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
struct property *prop;
int length;
u32 value;
@@ -164,13 +164,13 @@ static int pwm_backlight_parse_dt(struct device_d *dev,
return 0;
}
-static int backlight_pwm_of_probe(struct device_d *dev)
+static int backlight_pwm_of_probe(struct device *dev)
{
int ret;
struct pwm_backlight *pwm_backlight;
struct pwm_device *pwm;
- pwm = of_pwm_request(dev->device_node, NULL);
+ pwm = of_pwm_request(dev->of_node, NULL);
if (IS_ERR(pwm)) {
dev_err(dev, "Cannot find PWM device\n");
return PTR_ERR(pwm);
@@ -193,7 +193,7 @@ static int backlight_pwm_of_probe(struct device_d *dev)
pwm_backlight->backlight.slew_time_ms = 100;
pwm_backlight->backlight.brightness_set = backlight_pwm_set;
pwm_backlight->backlight.dev.parent = dev;
- pwm_backlight->backlight.node = dev->device_node;
+ pwm_backlight->backlight.node = dev->of_node;
ret = backlight_register(&pwm_backlight->backlight);
if (ret)
@@ -209,8 +209,9 @@ static struct of_device_id backlight_pwm_of_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, backlight_pwm_of_ids);
-static struct driver_d backlight_pwm_of_driver = {
+static struct driver backlight_pwm_of_driver = {
.name = "pwm-backlight",
.probe = backlight_pwm_of_probe,
.of_compatible = DRV_OF_COMPAT(backlight_pwm_of_ids),
diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c
index 14735fe465..070d1b4902 100644
--- a/drivers/video/bcm2835.c
+++ b/drivers/video/bcm2835.c
@@ -15,7 +15,7 @@
#include <xfuncs.h>
#include <of_address.h>
-#include <mach/mbox.h>
+#include <mach/bcm283x/mbox.h>
struct bcm2835fb_info {
struct fb_info fbi;
@@ -54,7 +54,7 @@ static struct fb_ops bcm2835fb_ops = {
.fb_disable = bcm2835fb_disable,
};
-static int bcm2835fb_probe(struct device_d *dev)
+static int bcm2835fb_probe(struct device *dev)
{
BCM2835_MBOX_STACK_ALIGN(struct msg_fb_query, msg_query);
BCM2835_MBOX_STACK_ALIGN(struct msg_fb_setup, msg_setup);
@@ -147,7 +147,7 @@ static int bcm2835fb_probe(struct device_d *dev)
return 0;
}
-static struct driver_d bcm2835fb_driver = {
+static struct driver bcm2835fb_driver = {
.name = "bcm2835_fb",
.probe = bcm2835fb_probe,
};
diff --git a/drivers/video/bochs/bochs_hw.c b/drivers/video/bochs/bochs_hw.c
index debdd36941..60439ddee5 100644
--- a/drivers/video/bochs/bochs_hw.c
+++ b/drivers/video/bochs/bochs_hw.c
@@ -8,7 +8,6 @@
#include <common.h>
#include <driver.h>
-#include <linux/pci.h>
#include <fb.h>
#include "../edid.h"
#include "bochs_hw.h"
@@ -149,7 +148,8 @@ static int bochs_hw_read_version(struct bochs *bochs)
return ver & 0xF;
}
-int bochs_hw_probe(struct device_d *dev, void __iomem *fb_map, void __iomem *mmio)
+int bochs_hw_probe(struct device *dev, void __iomem *fb_map,
+ void __iomem *mmio)
{
struct bochs *bochs;
struct fb_info *fb;
@@ -157,8 +157,8 @@ int bochs_hw_probe(struct device_d *dev, void __iomem *fb_map, void __iomem *mmi
bochs = xzalloc(sizeof(*bochs));
- bochs->fb_map = IOMEM(fb_map);
- bochs->mmio = IOMEM(mmio);
+ bochs->fb_map = fb_map;
+ bochs->mmio = mmio;
ret = bochs_hw_read_version(bochs);
if (ret < 0) {
diff --git a/drivers/video/bochs/bochs_hw.h b/drivers/video/bochs/bochs_hw.h
index 420b58f4da..c721113656 100644
--- a/drivers/video/bochs/bochs_hw.h
+++ b/drivers/video/bochs/bochs_hw.h
@@ -8,8 +8,8 @@
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
-struct device_d;
+struct device;
-int bochs_hw_probe(struct device_d *dev, void *fb_map, void __iomem *mmio);
+int bochs_hw_probe(struct device *dev, void __iomem *fb_map, void __iomem *mmio);
#endif
diff --git a/drivers/video/bochs/bochs_isa.c b/drivers/video/bochs/bochs_isa.c
index f273ac5f74..50fdecd9c8 100644
--- a/drivers/video/bochs/bochs_isa.c
+++ b/drivers/video/bochs/bochs_isa.c
@@ -11,7 +11,7 @@
static int bochs_isa_detect(void)
{
- struct device_d *dev;
+ struct device *dev;
int ret;
outw(0, VBE_DISPI_IOPORT_INDEX);
@@ -26,6 +26,6 @@ static int bochs_isa_detect(void)
if (ret)
return ret;
- return bochs_hw_probe(dev, (void *)0xe0000000, NULL);
+ return bochs_hw_probe(dev, IOMEM(0xe0000000), NULL);
}
device_initcall(bochs_isa_detect);
diff --git a/drivers/video/edid.c b/drivers/video/edid.c
index 96489f2a37..7e6747ccd5 100644
--- a/drivers/video/edid.c
+++ b/drivers/video/edid.c
@@ -847,20 +847,27 @@ edid_do_read_i2c(struct i2c_adapter *adapter, unsigned char *buf,
ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
} while (ret != xfers && --retries);
- return ret == xfers ? 0 : -1;
+ if (ret == 0)
+ ret = -EPROTO;
+
+ return ret == xfers ? 0 : ret;
}
void *edid_read_i2c(struct i2c_adapter *adapter)
{
u8 *block;
+ int ret;
if (!IS_ENABLED(CONFIG_I2C))
return NULL;
block = xmalloc(EDID_LENGTH);
- if (edid_do_read_i2c(adapter, block, 0, EDID_LENGTH))
+ ret = edid_do_read_i2c(adapter, block, 0, EDID_LENGTH);
+ if (ret) {
+ dev_dbg(&adapter->dev, "EDID readout failed: %pe\n", ERR_PTR(ret));
goto out;
+ }
return block;
out:
diff --git a/drivers/video/efi_gop.c b/drivers/video/efi_gop.c
index 5e9bc406e1..cd2506c04b 100644
--- a/drivers/video/efi_gop.c
+++ b/drivers/video/efi_gop.c
@@ -63,7 +63,7 @@ struct efi_graphics_output_protocol {
};
struct efi_gop_priv {
- struct device_d *dev;
+ struct device *dev;
struct fb_info fb;
uint32_t mode;
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
index 1c93dafbc9..6f412d62c4 100644
--- a/drivers/video/fb.c
+++ b/drivers/video/fb.c
@@ -43,6 +43,12 @@ static int fb_close(struct cdev *cdev)
return 0;
}
+void fb_damage(struct fb_info *info, struct fb_rect *rect)
+{
+ if (info->fbops->fb_damage)
+ info->fbops->fb_damage(info, rect);
+}
+
static int fb_op_flush(struct cdev *cdev)
{
struct fb_info *info = cdev->priv;
@@ -158,7 +164,7 @@ static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num)
static int fb_setup_mode(struct fb_info *info)
{
- struct device_d *dev = &info->dev;
+ struct device *dev = &info->dev;
int ret;
struct fb_videomode *mode;
@@ -251,7 +257,7 @@ static void fb_print_modes(struct display_timings *modes)
fb_print_mode(&modes->modes[i]);
}
-static void fb_info(struct device_d *dev)
+static void fb_info(struct device *dev)
{
struct fb_info *info = dev->priv;
@@ -279,7 +285,7 @@ static int fb_set_shadowfb(struct param_d *p, void *priv)
int register_framebuffer(struct fb_info *info)
{
int id = get_free_deviceid("fb");
- struct device_d *dev;
+ struct device *dev;
int ret, num_modes, i;
const char **names;
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 070378aa23..6c85e8e06a 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -18,6 +18,12 @@ enum state_t {
struct fbc_priv {
struct console_device cdev;
struct fb_info *fb;
+ struct {
+ u32 top;
+ u32 left;
+ u32 bottom;
+ u32 right;
+ } margin;
struct screen *sc;
@@ -60,9 +66,26 @@ static int fbc_tstc(struct console_device *cdev)
static void cls(struct fbc_priv *priv)
{
void *buf = gui_screen_render_buffer(priv->sc);
+ struct fb_info *fb = priv->fb;
+ int width = fb->xres - priv->margin.left - priv->margin.right;
+ int height = fb->yres - priv->margin.top - priv->margin.bottom;
+ void *adr;
+
+ adr = buf + priv->fb->line_length * priv->margin.top;
+
+ if (!priv->margin.left && !priv->margin.right) {
+ memset(adr, 0, priv->fb->line_length * height);
+ } else {
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ int y;
- memset(buf, 0, priv->fb->line_length * priv->fb->yres);
- gu_screen_blit(priv->sc);
+ for (y = 0; y < height; y++) {
+ memset(adr + priv->margin.left * bpp, 0, width * bpp);
+ adr += priv->fb->line_length;
+ }
+ }
+ gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top,
+ width, height);
}
struct rgb {
@@ -122,7 +145,8 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
uint8_t t = inbuf[i];
int j;
- adr = buf + line_length * (y * priv->font->height + i) + x * priv->font->width * bpp;
+ adr = buf + line_length * (priv->margin.top + y * priv->font->height + i) +
+ (priv->margin.left + x * priv->font->width) * bpp;
for (j = 0; j < priv->font->width; j++) {
if (t & 0x80)
@@ -142,9 +166,11 @@ static void video_invertchar(struct fbc_priv *priv, int x, int y)
buf = gui_screen_render_buffer(priv->sc);
- gu_invert_area(priv->fb, buf, x * priv->font->width, y * priv->font->height,
+ gu_invert_area(priv->fb, buf, priv->margin.left + x * priv->font->width,
+ priv->margin.top + y * priv->font->height,
priv->font->width, priv->font->height);
- gu_screen_blit_area(priv->sc, x * priv->font->width, y * priv->font->height,
+ gu_screen_blit_area(priv->sc, priv->margin.left + x * priv->font->width,
+ priv->margin.top + y * priv->font->height,
priv->font->width, priv->font->height);
}
@@ -185,8 +211,9 @@ static void printchar(struct fbc_priv *priv, int c)
default:
drawchar(priv, priv->x, priv->y, c);
- gu_screen_blit_area(priv->sc, priv->x * priv->font->width,
- priv->y * priv->font->height,
+ gu_screen_blit_area(priv->sc,
+ priv->margin.left + priv->x * priv->font->width,
+ priv->margin.top + priv->y * priv->font->height,
priv->font->width, priv->font->height);
priv->x++;
@@ -198,15 +225,36 @@ static void printchar(struct fbc_priv *priv, int c)
if (priv->y > priv->rows) {
void *buf;
+ void *adr;
u32 line_length = priv->fb->line_length;
int line_height = line_length * priv->font->height;
+ int width = priv->fb->xres - priv->margin.left - priv->margin.right;
+ int height = (priv->rows + 1) * priv->font->height;
buf = gui_screen_render_buffer(priv->sc);
+ adr = buf + priv->margin.top * line_length;
+
+ if (!priv->margin.left && !priv->margin.right) {
+ memcpy(adr, adr + line_height, line_height * priv->rows);
+ memset(adr + line_height * priv->rows, 0, line_height);
+ } else {
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ int y;
+
+ adr += priv->margin.left * bpp;
+
+ for (y = 0; y < height - priv->font->height; y++) {
+ memcpy(adr, adr + line_height, width * bpp);
+ adr += line_length;
+ }
+ for (y = height - priv->font->height; y < height; y++) {
+ memset(adr, 0, width * bpp);
+ adr += line_length;
+ }
+ }
- memcpy(buf, buf + line_height, line_height * priv->rows);
- memset(buf + line_height * priv->rows, 0, line_height);
-
- gu_screen_blit(priv->sc);
+ gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top,
+ width, height);
priv->y = priv->rows;
}
@@ -401,8 +449,9 @@ static void fbc_putc(struct console_device *cdev, char c)
static int setup_font(struct fbc_priv *priv)
{
- struct fb_info *fb = priv->fb;
const struct font_desc *font;
+ unsigned int height = priv->fb->yres - priv->margin.top - priv->margin.bottom;
+ unsigned int width = priv->fb->xres - priv->margin.left - priv->margin.right;
font = find_font_enum(priv->par_font_val);
if (!font) {
@@ -411,8 +460,8 @@ static int setup_font(struct fbc_priv *priv)
priv->font = font;
- priv->rows = fb->yres / priv->font->height - 1;
- priv->cols = fb->xres / priv->font->width - 1;
+ priv->rows = height / priv->font->height - 1;
+ priv->cols = width / priv->font->width - 1;
return 0;
}
@@ -472,6 +521,35 @@ static int set_font(struct param_d *p, void *vpriv)
return 0;
}
+static int set_margin(struct param_d *p, void *vpriv)
+{
+ struct fbc_priv *priv = vpriv;
+ struct console_device *cdev = &priv->cdev;
+ int ret;
+
+ if (!priv->font) {
+ ret = setup_font(priv);
+ if (ret)
+ return ret;
+ }
+
+ priv->margin.left = min(priv->margin.left,
+ priv->fb->xres - priv->margin.right - priv->font->width);
+ priv->margin.top = min(priv->margin.top,
+ priv->fb->yres - priv->margin.bottom - priv->font->height);
+ priv->margin.right = min(priv->margin.right,
+ priv->fb->xres - priv->margin.left - priv->font->width);
+ priv->margin.bottom = min(priv->margin.bottom,
+ priv->fb->yres - priv->margin.top - priv->font->height);
+
+ if (cdev->f_active & (CONSOLE_STDOUT | CONSOLE_STDERR)) {
+ cls(priv);
+ setup_font(priv);
+ }
+
+ return 0;
+}
+
int register_fbconsole(struct fb_info *fb)
{
struct fbc_priv *priv;
@@ -508,6 +586,15 @@ int register_fbconsole(struct fb_info *fb)
set_font, NULL,
&priv->par_font_val, priv);
+ dev_add_param_uint32(&cdev->class_dev, "margin.top", set_margin,
+ NULL, &priv->margin.top, "%u", priv);
+ dev_add_param_uint32(&cdev->class_dev, "margin.left", set_margin,
+ NULL, &priv->margin.left, "%u", priv);
+ dev_add_param_uint32(&cdev->class_dev, "margin.bottom", set_margin,
+ NULL, &priv->margin.bottom, "%u", priv);
+ dev_add_param_uint32(&cdev->class_dev, "margin.right", set_margin,
+ NULL, &priv->margin.right, "%u", priv);
+
pr_info("registered as %s%d\n", cdev->class_dev.name, cdev->class_dev.id);
return 0;
diff --git a/drivers/video/imx-ipu-fb.c b/drivers/video/imx-ipu-fb.c
index d158c68c25..e2ff01929b 100644
--- a/drivers/video/imx-ipu-fb.c
+++ b/drivers/video/imx-ipu-fb.c
@@ -8,14 +8,14 @@
#include <dma.h>
#include <init.h>
#include <io.h>
-#include <mach/imx35-regs.h>
+#include <mach/imx/imx35-regs.h>
#include <fb.h>
-#include <mach/imxfb.h>
+#include <platform_data/imxfb.h>
#include <malloc.h>
#include <errno.h>
#include <linux/math64.h>
#include <mmu.h>
-#include <mach/imx-ipu-fb.h>
+#include <mach/imx/imx-ipu-fb.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -29,7 +29,7 @@ struct ipu_fb_info {
struct fb_info info;
struct fb_info overlay;
- struct device_d *dev;
+ struct device *dev;
unsigned int alpha;
int disable_fractional_divider;
@@ -974,7 +974,7 @@ static int sdc_fb_register_overlay(struct ipu_fb_info *fbi, void *fb)
#endif
-static int imxfb_probe(struct device_d *dev)
+static int imxfb_probe(struct device *dev)
{
struct resource *iores;
struct ipu_fb_info *fbi;
@@ -1043,7 +1043,7 @@ static int imxfb_probe(struct device_d *dev)
return ret;
}
-static struct driver_d imx3fb_driver = {
+static struct driver imx3fb_driver = {
.name = "imx-ipu-fb",
.probe = imxfb_probe,
};
diff --git a/drivers/video/imx-ipu-v3/imx-hdmi.c b/drivers/video/imx-ipu-v3/imx-hdmi.c
index d63f2c2111..2d5fd98666 100644
--- a/drivers/video/imx-ipu-v3/imx-hdmi.c
+++ b/drivers/video/imx-ipu-v3/imx-hdmi.c
@@ -18,8 +18,8 @@
#include <i2c/i2c.h>
#include <video/media-bus-format.h>
#include <video/vpl.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx53-regs.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx53-regs.h>
#include "imx-ipu-v3.h"
#include "ipuv3-plane.h"
@@ -113,7 +113,7 @@ struct hdmi_data_info {
struct dw_hdmi {
enum dw_hdmi_devtype dev_type;
- struct device_d *dev;
+ struct device *dev;
struct clk *isfr_clk;
struct clk *iahb_clk;
@@ -1197,6 +1197,7 @@ static struct of_device_id dw_hdmi_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, dw_hdmi_dt_ids);
static int dw_hdmi_get_modes(struct dw_hdmi *hdmi, struct display_timings *timings)
{
@@ -1248,10 +1249,10 @@ static int dw_hdmi_ioctl(struct vpl *vpl, unsigned int port,
return 0;
}
-static int dw_hdmi_probe(struct device_d *dev)
+static int dw_hdmi_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct dw_hdmi *hdmi;
int ret;
@@ -1347,7 +1348,7 @@ err_isfr:
return ret;
}
-static struct driver_d dw_hdmi_driver = {
+static struct driver dw_hdmi_driver = {
.probe = dw_hdmi_probe,
.of_compatible = dw_hdmi_dt_ids,
.name = "imx-hdmi",
diff --git a/drivers/video/imx-ipu-v3/imx-ldb.c b/drivers/video/imx-ipu-v3/imx-ldb.c
index 4c934bc72e..3ed6d44f5b 100644
--- a/drivers/video/imx-ipu-v3/imx-ldb.c
+++ b/drivers/video/imx-ipu-v3/imx-ldb.c
@@ -19,8 +19,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/math64.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx53-regs.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx53-regs.h>
#include "imx-ipu-v3.h"
#include "ipuv3-plane.h"
@@ -61,7 +61,7 @@ struct imx_ldb_data {
};
struct imx_ldb {
- struct device_d *dev;
+ struct device *dev;
u32 bus_format;
int mode_valid;
struct imx_ldb_channel channel[2];
@@ -302,9 +302,9 @@ static int imx_ldb_ioctl(struct vpl *vpl, unsigned int port,
return 0;
}
-static int imx_ldb_probe(struct device_d *dev)
+static int imx_ldb_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct device_node *child;
struct imx_ldb *imx_ldb;
int ret, i;
@@ -403,8 +403,9 @@ static struct of_device_id imx_ldb_dt_ids[] = {
{ .compatible = "fsl,imx53-ldb", &imx_ldb_data_imx53},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
-static struct driver_d imx_ldb_driver = {
+static struct driver imx_ldb_driver = {
.probe = imx_ldb_probe,
.of_compatible = imx_ldb_dt_ids,
.name = "imx-ldb",
diff --git a/drivers/video/imx-ipu-v3/imx-pd.c b/drivers/video/imx-ipu-v3/imx-pd.c
index 92fa4ed4b4..d8b5f90a6c 100644
--- a/drivers/video/imx-ipu-v3/imx-pd.c
+++ b/drivers/video/imx-ipu-v3/imx-pd.c
@@ -22,7 +22,7 @@
#define IMX_PD_OUTPUT_PORT 1
struct imx_pd {
- struct device_d *dev;
+ struct device *dev;
struct display_timings *timings;
u32 bus_format;
struct vpl vpl;
@@ -61,9 +61,9 @@ static int imx_pd_ioctl(struct vpl *vpl, unsigned int port,
return 0;
}
-static int imx_pd_probe(struct device_d *dev)
+static int imx_pd_probe(struct device *dev)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
struct imx_pd *imx_pd;
struct device_node *port;
const char *fmt;
@@ -108,8 +108,9 @@ static struct of_device_id imx_pd_dt_ids[] = {
{ .compatible = "fsl,imx-parallel-display", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_pd_dt_ids);
-static struct driver_d imx_pd_driver = {
+static struct driver imx_pd_driver = {
.probe = imx_pd_probe,
.of_compatible = imx_pd_dt_ids,
.name = "imx-parallel-display",
diff --git a/drivers/video/imx-ipu-v3/ipu-common.c b/drivers/video/imx-ipu-v3/ipu-common.c
index 3d7a19e9c0..4909119d87 100644
--- a/drivers/video/imx-ipu-v3/ipu-common.c
+++ b/drivers/video/imx-ipu-v3/ipu-common.c
@@ -11,10 +11,10 @@
#include <driver.h>
#include <init.h>
#include <linux/mutex.h>
-#include <mach/generic.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx53-regs.h>
-#include <mach/imx51-regs.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx53-regs.h>
+#include <mach/imx/imx51-regs.h>
#include "imx-ipu-v3.h"
#include "ipu-prv.h"
@@ -609,9 +609,10 @@ static struct of_device_id imx_ipu_dt_ids[] = {
{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
static int ipu_submodules_init(struct ipu_soc *ipu,
- struct device_d *dev, void __iomem *ipu_base,
+ struct device *dev, void __iomem *ipu_base,
struct clk *ipu_clk)
{
char *unit;
@@ -705,16 +706,16 @@ static struct ipu_platform_reg client_reg[] = {
static int ipu_client_id;
-static int ipu_add_subdevice_pdata(struct device_d *ipu_dev,
- struct ipu_platform_reg *reg)
+static int ipu_add_subdevice_pdata(struct device *ipu_dev,
+ struct ipu_platform_reg *reg)
{
- struct device_d *dev;
+ struct device *dev;
int ret;
dev = device_alloc(reg->name, ipu_client_id++);
dev->parent = ipu_dev;
device_add_data(dev, &reg->pdata, sizeof(reg->pdata));
- ((struct ipu_client_platformdata *)dev->platform_data)->device_node = ipu_dev->device_node;
+ ((struct ipu_client_platformdata *)dev->platform_data)->device_node = ipu_dev->of_node;
ret = platform_device_register(dev);
@@ -740,7 +741,7 @@ err_register:
return ret;
}
-static int ipu_probe(struct device_d *dev)
+static int ipu_probe(struct device *dev)
{
struct resource *iores;
struct ipu_soc *ipu;
@@ -844,7 +845,7 @@ out_failed_reset:
return ret;
}
-static struct driver_d imx_ipu_driver = {
+static struct driver imx_ipu_driver = {
.name = "imx-ipuv3",
.of_compatible = imx_ipu_dt_ids,
.probe = ipu_probe,
diff --git a/drivers/video/imx-ipu-v3/ipu-dc.c b/drivers/video/imx-ipu-v3/ipu-dc.c
index d069d78ab3..a0292fc4a1 100644
--- a/drivers/video/imx-ipu-v3/ipu-dc.c
+++ b/drivers/video/imx-ipu-v3/ipu-dc.c
@@ -96,7 +96,7 @@ struct ipu_dc_priv {
void __iomem *dc_reg;
void __iomem *dc_tmpl_reg;
struct ipu_soc *ipu;
- struct device_d *dev;
+ struct device *dev;
struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
};
@@ -312,7 +312,7 @@ void ipu_dc_put(struct ipu_dc *dc)
}
EXPORT_SYMBOL_GPL(ipu_dc_put);
-int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev,
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
void __iomem *base, void __iomem *template_base)
{
struct ipu_dc_priv *priv;
diff --git a/drivers/video/imx-ipu-v3/ipu-di.c b/drivers/video/imx-ipu-v3/ipu-di.c
index d63613c842..6156911bfc 100644
--- a/drivers/video/imx-ipu-v3/ipu-di.c
+++ b/drivers/video/imx-ipu-v3/ipu-di.c
@@ -700,7 +700,7 @@ void ipu_di_put(struct ipu_di *di)
}
EXPORT_SYMBOL_GPL(ipu_di_put);
-int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id,
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
void __iomem *base,
u32 module, struct clk *clk_ipu)
{
diff --git a/drivers/video/imx-ipu-v3/ipu-dmfc.c b/drivers/video/imx-ipu-v3/ipu-dmfc.c
index e879f97a33..4be6a15eef 100644
--- a/drivers/video/imx-ipu-v3/ipu-dmfc.c
+++ b/drivers/video/imx-ipu-v3/ipu-dmfc.c
@@ -100,7 +100,7 @@ struct dmfc_channel {
struct ipu_dmfc_priv {
struct ipu_soc *ipu;
- struct device_d *dev;
+ struct device *dev;
struct dmfc_channel channels[DMFC_NUM_CHANNELS];
unsigned long bandwidth_per_slot;
void __iomem *base;
@@ -340,8 +340,8 @@ void ipu_dmfc_put(struct dmfc_channel *dmfc)
}
EXPORT_SYMBOL_GPL(ipu_dmfc_put);
-int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
- struct clk *ipu_clk)
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base,
+ struct clk *ipu_clk)
{
struct ipu_dmfc_priv *priv;
int i;
diff --git a/drivers/video/imx-ipu-v3/ipu-dp.c b/drivers/video/imx-ipu-v3/ipu-dp.c
index 67f93c0d55..68b45c11c7 100644
--- a/drivers/video/imx-ipu-v3/ipu-dp.c
+++ b/drivers/video/imx-ipu-v3/ipu-dp.c
@@ -56,7 +56,7 @@ struct ipu_flow {
struct ipu_dp_priv {
struct ipu_soc *ipu;
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct ipu_flow flow[IPUV3_NUM_FLOWS];
int use_count;
@@ -277,7 +277,7 @@ void ipu_dp_put(struct ipu_dp *dp)
}
EXPORT_SYMBOL_GPL(ipu_dp_put);
-int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base)
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base)
{
struct ipu_dp_priv *priv;
int i;
diff --git a/drivers/video/imx-ipu-v3/ipu-prv.h b/drivers/video/imx-ipu-v3/ipu-prv.h
index 15aed7a943..4465711ee4 100644
--- a/drivers/video/imx-ipu-v3/ipu-prv.h
+++ b/drivers/video/imx-ipu-v3/ipu-prv.h
@@ -140,7 +140,7 @@ struct ipu_di;
struct ipu_devtype;
struct ipu_soc {
- struct device_d *dev;
+ struct device *dev;
const struct ipu_devtype *devtype;
enum ipuv3_type ipu_type;
spinlock_t lock;
@@ -172,22 +172,23 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
-int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id,
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
void __iomem *base, u32 module, struct clk *ipu_clk);
void ipu_di_exit(struct ipu_soc *ipu, int id);
-int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
- struct clk *ipu_clk);
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base,
+ struct clk *ipu_clk);
void ipu_dmfc_exit(struct ipu_soc *ipu);
-int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base);
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base);
void ipu_dp_exit(struct ipu_soc *ipu);
-int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base,
void __iomem *template_base);
void ipu_dc_exit(struct ipu_soc *ipu);
-int ipu_cpmem_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base);
+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev,
+ void __iomem *base);
void ipu_cpmem_exit(struct ipu_soc *ipu);
#endif /* __IPU_PRV_H__ */
diff --git a/drivers/video/imx-ipu-v3/ipufb.c b/drivers/video/imx-ipu-v3/ipufb.c
index f8bb69da06..e4ac988053 100644
--- a/drivers/video/imx-ipu-v3/ipufb.c
+++ b/drivers/video/imx-ipu-v3/ipufb.c
@@ -45,7 +45,7 @@ struct ipufb_info {
struct fb_videomode *mode;
struct fb_info info;
- struct device_d *dev;
+ struct device *dev;
/* plane[0] is the full plane, plane[1] is the partial plane */
struct ipu_plane *plane[2];
@@ -269,7 +269,7 @@ err_out:
return ret;
}
-static int ipufb_probe(struct device_d *dev)
+static int ipufb_probe(struct device *dev)
{
struct ipufb_info *fbi;
struct fb_info *info;
@@ -282,7 +282,7 @@ static int ipufb_probe(struct device_d *dev)
fbi = xzalloc(sizeof(*fbi));
info = &fbi->info;
- ipuid = of_alias_get_id(dev->parent->device_node, "ipu");
+ ipuid = of_alias_get_id(dev->parent->of_node, "ipu");
fbi->name = basprintf("ipu%d-di%d", ipuid + 1, pdata->di);
fbi->id = ipuid * 2 + pdata->di;
fbi->dino = pdata->di;
@@ -305,11 +305,12 @@ static int ipufb_probe(struct device_d *dev)
if (ret)
return ret;
- node = of_graph_get_port_by_id(dev->parent->device_node, 2 + pdata->di);
+ node = of_graph_get_port_by_id(dev->parent->of_node, 2 + pdata->di);
if (node && of_graph_port_is_available(node)) {
- dev_dbg(fbi->dev, "register vpl for %s\n", dev->parent->device_node->full_name);
+ dev_dbg(fbi->dev, "register vpl for %pOF\n",
+ dev->parent->of_node);
- fbi->vpl.node = dev->parent->device_node;
+ fbi->vpl.node = dev->parent->of_node;
ret = vpl_register(&fbi->vpl);
if (ret)
return ret;
@@ -341,11 +342,11 @@ static int ipufb_probe(struct device_d *dev)
return ret;
}
-static void ipufb_remove(struct device_d *dev)
+static void ipufb_remove(struct device *dev)
{
}
-static struct driver_d ipufb_driver = {
+static struct driver ipufb_driver = {
.name = "imx-ipuv3-crtc",
.probe = ipufb_probe,
.remove = ipufb_remove,
diff --git a/drivers/video/imx-ipu-v3/ipuv3-plane.c b/drivers/video/imx-ipu-v3/ipuv3-plane.c
index a54bdab78b..aed7a46963 100644
--- a/drivers/video/imx-ipu-v3/ipuv3-plane.c
+++ b/drivers/video/imx-ipu-v3/ipuv3-plane.c
@@ -55,7 +55,7 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane,
uint32_t src_w, uint32_t src_h)
{
struct ipu_ch_param __iomem *cpmem;
- struct device_d *dev = &info->dev;
+ struct device *dev = &info->dev;
int ret;
/* no scaling */
diff --git a/drivers/video/imx.c b/drivers/video/imx.c
index 615836d47e..cb1c11b4cb 100644
--- a/drivers/video/imx.c
+++ b/drivers/video/imx.c
@@ -14,7 +14,7 @@
#include <common.h>
#include <fb.h>
#include <io.h>
-#include <mach/imxfb.h>
+#include <platform_data/imxfb.h>
#include <driver.h>
#include <malloc.h>
#include <errno.h>
@@ -150,7 +150,7 @@ struct imxfb_info {
unused:30;
struct fb_info info;
- struct device_d *dev;
+ struct device *dev;
void (*enable)(int enable);
@@ -378,7 +378,7 @@ static struct fb_ops imxfb_ops = {
.fb_activate_var = imxfb_activate_var,
};
-static int imxfb_allocate_fbbuffer(const struct device_d *dev,
+static int imxfb_allocate_fbbuffer(const struct device *dev,
struct fb_info *info, void *forcefb)
{
size_t fbsize = info->xres * info->yres * (info->bits_per_pixel >> 3);
@@ -525,7 +525,7 @@ static int imxfb_register_overlay(struct imxfb_info *fbi, void *fb)
}
#endif
-static int imxfb_probe(struct device_d *dev)
+static int imxfb_probe(struct device *dev)
{
struct resource *iores;
struct imxfb_info *fbi;
@@ -602,7 +602,7 @@ static int imxfb_probe(struct device_d *dev)
return 0;
}
-static struct driver_d imxfb_driver = {
+static struct driver imxfb_driver = {
.name = "imxfb",
.probe = imxfb_probe,
};
diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
index 48b1110f72..9611a402d1 100644
--- a/drivers/video/mipi_dbi.c
+++ b/drivers/video/mipi_dbi.c
@@ -8,11 +8,13 @@
#define pr_fmt(fmt) "mipi-dbi: " fmt
#include <common.h>
+#include <dma.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#include <regulator.h>
#include <spi/spi.h>
+#include <video/backlight.h>
#include <video/mipi_dbi.h>
#include <video/vpl.h>
@@ -197,6 +199,220 @@ int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
/**
+ * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
+ * @dst: The destination buffer
+ * @info: The source framebuffer info
+ * @clip: Clipping rectangle of the area to be copied
+ * @swap: When true, swap MSB/LSB of 16-bit values
+ */
+static void mipi_dbi_buf_copy(u16 *dst, struct fb_info *info,
+ struct fb_rect *clip, bool swap)
+{
+ u16 *src = (u16 *)info->screen_base;
+ unsigned int height = clip->y2 - clip->y1;
+ unsigned int width = clip->x2 - clip->x1;
+ int x, y;
+
+ src += clip->y1 * info->xres + clip->x1;
+ if (swap) {
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++)
+ *dst++ = src[x] << 8 | src[x] >> 8;
+ src += info->xres;
+ }
+ } else {
+ for (y = 0; y < height; y++) {
+ memcpy(dst, src, 2 * width);
+ dst += width;
+ src += info->xres;
+ }
+ }
+}
+
+static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev,
+ unsigned int xs, unsigned int xe,
+ unsigned int ys, unsigned int ye)
+{
+ struct mipi_dbi *dbi = &dbidev->dbi;
+
+ xs += dbidev->mode.left_margin;
+ xe += dbidev->mode.left_margin;
+ ys += dbidev->mode.upper_margin;
+ ye += dbidev->mode.upper_margin;
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff,
+ xs & 0xff, (xe >> 8) & 0xff, xe & 0xff);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff,
+ ys & 0xff, (ye >> 8) & 0xff, ye & 0xff);
+}
+
+static void mipi_dbi_fb_dirty(struct mipi_dbi_dev *dbidev, struct fb_info *info,
+ struct fb_rect *rect)
+{
+ unsigned int height = rect->y2 - rect->y1;
+ unsigned int width = rect->x2 - rect->x1;
+ struct mipi_dbi *dbi = &dbidev->dbi;
+ bool swap = dbi->swap_bytes;
+ int ret;
+ bool full;
+ void *tr;
+
+ full = width == info->xres && height == info->yres;
+
+ if (!full || swap) {
+ tr = dbidev->tx_buf;
+ mipi_dbi_buf_copy(tr, info, rect, swap);
+ } else {
+ tr = info->screen_base;
+ }
+
+ mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1,
+ rect->y2 - 1);
+
+ ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr,
+ width * height * 2);
+ if (ret)
+ pr_err_once("Failed to update display %d\n", ret);
+
+ dbidev->damage.x1 = 0;
+ dbidev->damage.y1 = 0;
+ dbidev->damage.x2 = 0;
+ dbidev->damage.y2 = 0;
+}
+
+/**
+ * mipi_dbi_enable_flush - MIPI DBI enable helper
+ * @dbidev: MIPI DBI device structure
+ * @info: Framebuffer info
+ *
+ * Flushes the whole framebuffer and enables the backlight. Drivers can use this
+ * in their &fb_ops->fb_enable callback.
+ */
+void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
+ struct fb_info *info)
+{
+ struct fb_rect rect = {
+ .x1 = 0,
+ .y1 = 0,
+ .x2 = info->xres,
+ .y2 = info->yres
+ };
+
+ mipi_dbi_fb_dirty(dbidev, info, &rect);
+
+ if (dbidev->backlight)
+ backlight_set_brightness_default(dbidev->backlight);
+}
+EXPORT_SYMBOL(mipi_dbi_enable_flush);
+
+static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev)
+{
+ u16 height = dbidev->mode.xres;
+ u16 width = dbidev->mode.yres;
+ struct mipi_dbi *dbi = &dbidev->dbi;
+ size_t len = width * height * 2;
+
+ memset(dbidev->tx_buf, 0, len);
+
+ mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1);
+ mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, dbidev->tx_buf, len);
+}
+
+/**
+ * mipi_dbi_fb_disable - MIPI DBI framebuffer disable helper
+ * @info: Framebuffer info
+ *
+ * This function disables backlight if present, if not the display memory is
+ * blanked. The regulator is disabled if in use. Drivers can use this as their
+ * &fb_ops->fb_disable callback.
+ */
+void mipi_dbi_fb_disable(struct fb_info *info)
+{
+ struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info);
+
+ if (dbidev->backlight)
+ backlight_set_brightness(dbidev->backlight, 0);
+ else
+ mipi_dbi_blank(dbidev);
+
+ regulator_disable(dbidev->regulator);
+ regulator_disable(dbidev->io_regulator);
+}
+EXPORT_SYMBOL(mipi_dbi_fb_disable);
+
+void mipi_dbi_fb_damage(struct fb_info *info, const struct fb_rect *rect)
+{
+ struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info);
+
+ if (dbidev->damage.x2 && dbidev->damage.y2) {
+ dbidev->damage.x1 = min(dbidev->damage.x1, rect->x1);
+ dbidev->damage.y1 = min(dbidev->damage.y1, rect->y1);
+ dbidev->damage.x2 = max(dbidev->damage.x2, rect->x2);
+ dbidev->damage.y2 = max(dbidev->damage.y2, rect->y2);
+ } else {
+ dbidev->damage = *rect;
+ }
+}
+EXPORT_SYMBOL(mipi_dbi_fb_damage);
+
+void mipi_dbi_fb_flush(struct fb_info *info)
+{
+ struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info);
+
+ if (!dbidev->damage.x2 || !dbidev->damage.y2) {
+ dbidev->damage.x1 = 0;
+ dbidev->damage.y1 = 0;
+ dbidev->damage.x2 = info->xres;
+ dbidev->damage.y2 = info->yres;
+ }
+
+ mipi_dbi_fb_dirty(dbidev, info, &dbidev->damage);
+}
+EXPORT_SYMBOL(mipi_dbi_fb_flush);
+
+/**
+ * mipi_dbi_dev_init - MIPI DBI device initialization
+ * @dbidev: MIPI DBI device structure to initialize
+ * @ops: Framebuffer operations
+ * @mode: Display mode
+ *
+ * This function sets up a &fb_info with one fixed &fb_videomode.
+ * Additionally &mipi_dbi.tx_buf is allocated.
+ *
+ * Supported format: RGB565.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, struct fb_ops *ops,
+ struct fb_videomode *mode)
+{
+ struct fb_info *info = &dbidev->info;
+
+ info->mode = mode;
+ info->fbops = ops;
+ info->dev.parent = dbidev->dev;
+
+ info->xres = mode->xres;
+ info->yres = mode->yres;
+ info->bits_per_pixel = 16;
+ info->line_length = info->xres * 2;
+ info->screen_size = info->line_length * info->yres;
+ info->screen_base = dma_zalloc(info->screen_size);
+
+ info->red.length = 5;
+ info->red.offset = 11;
+ info->green.length = 6;
+ info->green.offset = 5;
+ info->blue.length = 5;
+ info->blue.offset = 0;
+
+ dbidev->tx_buf = dma_alloc(info->screen_size);
+
+ return 0;
+}
+
+/**
* mipi_dbi_hw_reset - Hardware reset of controller
* @dbi: MIPI DBI structure
*
@@ -204,7 +420,7 @@ EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
*/
void mipi_dbi_hw_reset(struct mipi_dbi *dbi)
{
- if (!gpio_is_valid(dbi->reset))
+ if (!dbi->reset)
return;
gpiod_set_value(dbi->reset, 0);
@@ -246,6 +462,68 @@ bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
}
EXPORT_SYMBOL(mipi_dbi_display_is_on);
+static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool cond)
+{
+ struct device *dev = dbidev->dev;
+ struct mipi_dbi *dbi = &dbidev->dbi;
+ int ret;
+
+ ret = regulator_enable(dbidev->regulator);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(dbidev->io_regulator);
+ if (ret) {
+ dev_err(dev, "Failed to enable I/O regulator (%d)\n", ret);
+ regulator_disable(dbidev->regulator);
+ return ret;
+ }
+
+ if (cond && mipi_dbi_display_is_on(dbi))
+ return 1;
+
+ mipi_dbi_hw_reset(dbi);
+ ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET);
+ if (ret) {
+ dev_err(dev, "Failed to send reset command (%d)\n", ret);
+ regulator_disable(dbidev->io_regulator);
+ regulator_disable(dbidev->regulator);
+ return ret;
+ }
+
+ /*
+ * If we did a hw reset, we know the controller is in Sleep mode and
+ * per MIPI DSC spec should wait 5ms after soft reset. If we didn't,
+ * we assume worst case and wait 120ms.
+ */
+ if (dbi->reset)
+ mdelay(5);
+ else
+ mdelay(120);
+
+ return 0;
+}
+
+/**
+ * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset
+ * @dbidev: MIPI DBI device structure
+ *
+ * This function enables the regulator if used and if the display is off, it
+ * does a hardware and software reset. If mipi_dbi_display_is_on() determines
+ * that the display is on, no reset is performed.
+ *
+ * Returns:
+ * Zero if the controller was reset, 1 if the display was already on, or a
+ * negative error code.
+ */
+int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev)
+{
+ return mipi_dbi_poweron_reset_conditional(dbidev, true);
+}
+EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset);
+
#if IS_ENABLED(CONFIG_SPI)
/**
@@ -392,22 +670,21 @@ static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
* Zero on success, negative error code on failure.
*/
int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
- int dc)
+ struct gpio_desc *dc)
{
- struct device_d *dev = &spi->dev;
+ struct device *dev = &spi->dev;
dbi->spi = spi;
dbi->read_commands = mipi_dbi_dcs_read_commands;
- if (!gpio_is_valid(dc)) {
+ if (!dc) {
dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n");
return -ENOSYS;
}
dbi->command = mipi_dbi_typec3_command;
dbi->dc = dc;
- // TODO: can we just force 16 bit?
- if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16)
+ if (mipi_dbi_machine_little_endian() && !spi_is_bpw_supported(spi, 16))
dbi->swap_bytes = true;
dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
diff --git a/drivers/video/mtl017.c b/drivers/video/mtl017.c
index 46c826be21..ba214b47ae 100644
--- a/drivers/video/mtl017.c
+++ b/drivers/video/mtl017.c
@@ -16,7 +16,7 @@
struct mtl017 {
struct vpl vpl;
- struct device_d *dev;
+ struct device *dev;
struct i2c_client *client;
u8 *regs;
int enable_gpio;
@@ -218,14 +218,14 @@ forward:
return vpl_ioctl(&mtl017->vpl, 1, cmd, ptr);
}
-static int mtl017_probe(struct device_d *dev)
+static int mtl017_probe(struct device *dev)
{
struct mtl017 *mtl017;
int ret;
enum of_gpio_flags flags;
mtl017 = xzalloc(sizeof(struct mtl017));
- mtl017->vpl.node = dev->device_node;
+ mtl017->vpl.node = dev->of_node;
mtl017->vpl.ioctl = mtl017_ioctl;
mtl017->dev = dev;
mtl017->client = to_i2c_client(dev);
@@ -234,15 +234,16 @@ static int mtl017_probe(struct device_d *dev)
if (IS_ERR(mtl017->regulator))
mtl017->regulator = NULL;
- mtl017->enable_gpio = of_get_named_gpio_flags(dev->device_node,
- "enable-gpios", 0, &flags);
+ mtl017->enable_gpio = of_get_named_gpio_flags(dev->of_node,
+ "enable-gpios", 0,
+ &flags);
if (gpio_is_valid(mtl017->enable_gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
mtl017->enable_active_high = 1;
}
- mtl017->reset_gpio = of_get_named_gpio_flags(dev->device_node,
- "reset-gpios", 0, &flags);
+ mtl017->reset_gpio = of_get_named_gpio_flags(dev->of_node,
+ "reset-gpios", 0, &flags);
if (gpio_is_valid(mtl017->reset_gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
mtl017->reset_active_high = 1;
@@ -255,7 +256,7 @@ static int mtl017_probe(struct device_d *dev)
return 0;
}
-static struct driver_d mtl_driver = {
+static struct driver mtl_driver = {
.name = "mtl017",
.probe = mtl017_probe,
};
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
index 6fe1e1b08b..74b01239cb 100644
--- a/drivers/video/of_display_timing.c
+++ b/drivers/video/of_display_timing.c
@@ -37,8 +37,8 @@ static int parse_timing_property(const struct device_node *np, const char *name,
prop = of_find_property(np, name, &length);
if (!prop) {
- pr_err("%s: could not find property %s\n",
- np->full_name, name);
+ pr_err("%pOF: could not find property %s\n",
+ np, name);
return -EINVAL;
}
@@ -46,8 +46,8 @@ static int parse_timing_property(const struct device_node *np, const char *name,
if ((cells == 1) || (cells == 3)) {
ret = of_property_read_u32(np, name, res);
} else {
- pr_err("%s: illegal timing specification in %s\n",
- np->full_name, name);
+ pr_err("%pOF: illegal timing specification in %s\n",
+ np, name);
return -EINVAL;
}
@@ -90,8 +90,7 @@ static int of_parse_display_timing(const struct device_node *np,
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
if (ret) {
- pr_err("%s: error reading timing properties\n",
- np->full_name);
+ pr_err("%pOF: error reading timing properties\n", np);
return -EINVAL;
}
@@ -99,6 +98,28 @@ static int of_parse_display_timing(const struct device_node *np,
}
/**
+ * of_get_display_timing - parse a display_timing entry
+ * @np: device_node with the timing subnode
+ * @name: name of the timing node
+ * @mode: fb_videomode struct to fill
+ **/
+int of_get_display_timing(const struct device_node *np, const char *name,
+ struct fb_videomode *mode)
+{
+ struct device_node *timing_np;
+
+ if (!np)
+ return -EINVAL;
+
+ timing_np = of_get_child_by_name(np, name);
+ if (!timing_np)
+ return -ENOENT;
+
+ return of_parse_display_timing(timing_np, mode);
+}
+EXPORT_SYMBOL_GPL(of_get_display_timing);
+
+/**
* of_get_display_timings - parse all display_timing entries from a device_node
* @np: device_node with the subnodes
**/
@@ -114,8 +135,7 @@ struct display_timings *of_get_display_timings(struct device_node *np)
timings_np = of_get_child_by_name(np, "display-timings");
if (!timings_np) {
- pr_debug("%s: could not find display-timings node\n",
- np->full_name);
+ pr_debug("%pOF: could not find display-timings node\n", np);
return NULL;
}
@@ -127,20 +147,19 @@ struct display_timings *of_get_display_timings(struct device_node *np)
entry = of_get_next_available_child(np, NULL);
/* if there is no child, it is useless to go on */
if (!entry) {
- pr_err("%s: no timing specifications given\n",
- np->full_name);
+ pr_err("%pOF: no timing specifications given\n", np);
goto fail;
}
- pr_debug("%s: using %s as default timing\n",
- np->full_name, entry->name);
+ pr_debug("%pOF: using %s as default timing\n",
+ np, entry->name);
native_mode = entry;
disp->num_modes = of_get_child_count(timings_np);
if (disp->num_modes == 0) {
/* should never happen, as entry was already found above */
- pr_err("%s: no timings specified\n", np->full_name);
+ pr_err("%pOF: no timings specified\n", np);
goto fail;
}
@@ -161,8 +180,8 @@ struct display_timings *of_get_display_timings(struct device_node *np)
* to not encourage wrong devicetrees, fail in case of
* an error
*/
- pr_err("%s: error in timing %d\n",
- np->full_name, disp->num_modes + 1);
+ pr_err("%pOF: error in timing %d\n",
+ np, disp->num_modes + 1);
goto fail;
}
@@ -174,8 +193,8 @@ struct display_timings *of_get_display_timings(struct device_node *np)
disp->num_modes++;
}
- pr_debug("%s: got %d timings. Using timing #%d as default\n",
- np->full_name, disp->num_modes,
+ pr_debug("%pOF: got %d timings. Using timing #%d as default\n",
+ np, disp->num_modes,
disp->native_mode + 1);
return disp;
diff --git a/drivers/video/omap.c b/drivers/video/omap.c
index 189b95d241..3b1ec89c38 100644
--- a/drivers/video/omap.c
+++ b/drivers/video/omap.c
@@ -27,7 +27,7 @@
struct omapfb_device {
struct fb_info info;
- struct device_d *dev;
+ struct device *dev;
struct omapfb_display const *cur_display;
@@ -410,7 +410,7 @@ static struct fb_ops omapfb_ops = {
.fb_activate_var = omapfb_activate_var,
};
-static int omapfb_probe(struct device_d *dev)
+static int omapfb_probe(struct device *dev)
{
struct omapfb_platform_data const *pdata = dev->platform_data;
struct omapfb_device *fbi;
@@ -499,7 +499,7 @@ out:
return rc;
}
-static struct driver_d omapfb_driver = {
+static struct driver omapfb_driver = {
.name = "omap_fb",
.probe = omapfb_probe,
};
diff --git a/drivers/video/panel-ilitek-ili9341.c b/drivers/video/panel-ilitek-ili9341.c
index d877442022..4d03a8513e 100644
--- a/drivers/video/panel-ilitek-ili9341.c
+++ b/drivers/video/panel-ilitek-ili9341.c
@@ -12,10 +12,10 @@
*
* Derived from Linux drivers/drm/gpu/panel/panel-ilitek-ili9341.c
*/
-#define DEBUG 1
+
#include <common.h>
#include <linux/bitops.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
#include <of.h>
#include <regulator.h>
#include <spi/spi.h>
@@ -159,11 +159,11 @@ struct ili9341_config {
};
struct ili9341 {
- struct device_d *dev;
+ struct device *dev;
struct vpl vpl;
const struct ili9341_config *conf;
- int reset_gpio;
- int dc_gpio;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *dc_gpio;
struct mipi_dbi *dbi;
u32 max_spi_speed;
struct regulator_bulk_data supplies[3];
@@ -280,7 +280,7 @@ static inline struct ili9341 *vpl_to_ili9341(struct vpl *vpl)
static void ili9341_dpi_init(struct ili9341 *ili)
{
- struct device_d *dev = ili->dev;
+ struct device *dev = ili->dev;
struct mipi_dbi *dbi = ili->dbi;
struct ili9341_config *cfg = (struct ili9341_config *)ili->conf;
@@ -344,7 +344,7 @@ static void ili9341_dpi_init(struct ili9341 *ili)
static int ili9341_dpi_power_on(struct ili9341 *ili)
{
- struct device_d *dev = ili->dev;
+ struct device *dev = ili->dev;
int ret = 0;
/* Assert RESET */
@@ -458,9 +458,10 @@ static int ili9341_ioctl(struct vpl *vpl, unsigned int port,
}
}
-static int ili9341_dpi_probe(struct spi_device *spi, int dc, int reset)
+static int ili9341_dpi_probe(struct spi_device *spi,
+ struct gpio_desc *dc, struct gpio_desc *reset)
{
- struct device_d *dev = &spi->dev;
+ struct device *dev = &spi->dev;
struct ili9341 *ili;
int ret;
@@ -499,23 +500,23 @@ static int ili9341_dpi_probe(struct spi_device *spi, int dc, int reset)
ili->dev = dev;
ili->max_spi_speed = ili->conf->max_spi_speed;
- ili->vpl.node = dev->device_node;
+ ili->vpl.node = dev->of_node;
ili->vpl.ioctl = ili9341_ioctl;
return vpl_register(&ili->vpl);
}
-static int ili9341_probe(struct device_d *dev)
+static int ili9341_probe(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
- int dc, reset;
+ struct gpio_desc *dc, *reset;
- reset = gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (!gpio_is_valid(reset) && reset != -ENOENT)
+ reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset))
dev_err(dev, "Failed to get gpio 'reset'\n");
dc = gpiod_get(dev, "dc", GPIOD_OUT_LOW);
- if (!gpio_is_valid(dc) && dc != -ENOENT)
+ if (IS_ERR(dc))
dev_err(dev, "Failed to get gpio 'dc'\n");
return ili9341_dpi_probe(spi, dc, reset);
@@ -528,8 +529,9 @@ static const struct of_device_id ili9341_of_match[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(of, ili9341_of_match);
-static struct driver_d ili9341_driver = {
+static struct driver ili9341_driver = {
.name = "panel-ilitek-ili9341",
.of_compatible = ili9341_of_match,
.probe = ili9341_probe,
diff --git a/drivers/video/panel-mipi-dbi.c b/drivers/video/panel-mipi-dbi.c
new file mode 100644
index 0000000000..fecb232796
--- /dev/null
+++ b/drivers/video/panel-mipi-dbi.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DRM driver for MIPI DBI compatible display panels
+ *
+ * Copyright 2022 Noralf Trønnes
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <fb.h>
+#include <firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/printk.h>
+#include <of.h>
+#include <regulator.h>
+#include <spi/spi.h>
+
+#include <video/backlight.h>
+#include <video/mipi_dbi.h>
+#include <video/mipi_display.h>
+
+static const u8 panel_mipi_dbi_magic[15] = { 'M', 'I', 'P', 'I', ' ', 'D', 'B', 'I',
+ 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * The display controller configuration is stored in a firmware file.
+ * The Device Tree 'compatible' property value with a '.bin' suffix is passed
+ * to request_firmware() to fetch this file.
+ */
+struct panel_mipi_dbi_config {
+ /* Magic string: panel_mipi_dbi_magic */
+ u8 magic[15];
+
+ /* Config file format version */
+ u8 file_format_version;
+
+ /*
+ * MIPI commands to execute when the display pipeline is enabled.
+ * This is used to configure the display controller.
+ *
+ * The commands are stored in a byte array with the format:
+ * command, num_parameters, [ parameter, ...], command, ...
+ *
+ * Some commands require a pause before the next command can be received.
+ * Inserting a delay in the command sequence is done by using the NOP command with one
+ * parameter: delay in miliseconds (the No Operation command is part of the MIPI Display
+ * Command Set where it has no parameters).
+ *
+ * Example:
+ * command 0x11
+ * sleep 120ms
+ * command 0xb1 parameters 0x01, 0x2c, 0x2d
+ * command 0x29
+ *
+ * Byte sequence:
+ * 0x11 0x00
+ * 0x00 0x01 0x78
+ * 0xb1 0x03 0x01 0x2c 0x2d
+ * 0x29 0x00
+ */
+ u8 commands[];
+};
+
+struct panel_mipi_dbi_commands {
+ const u8 *buf;
+ size_t len;
+};
+
+static struct panel_mipi_dbi_commands *
+panel_mipi_dbi_check_commands(struct device *dev, const struct firmware *fw)
+{
+ const struct panel_mipi_dbi_config *config = (struct panel_mipi_dbi_config *)fw->data;
+ struct panel_mipi_dbi_commands *commands;
+ size_t size = fw->size, commands_len;
+ unsigned int i = 0;
+
+ if (size < sizeof(*config) + 2) { /* At least 1 command */
+ dev_err(dev, "config: file size=%zu is too small\n", size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (memcmp(config->magic, panel_mipi_dbi_magic, sizeof(config->magic))) {
+ dev_err(dev, "config: Bad magic: %15ph\n", config->magic);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (config->file_format_version != 1) {
+ dev_err(dev, "config: version=%u is not supported\n", config->file_format_version);
+ return ERR_PTR(-EINVAL);
+ }
+
+ dev_dbg(dev, "size=%zu version=%u\n", size, config->file_format_version);
+
+ commands_len = size - sizeof(*config);
+
+ while ((i + 1) < commands_len) {
+ u8 command = config->commands[i++];
+ u8 num_parameters = config->commands[i++];
+ const u8 *parameters = &config->commands[i];
+
+ i += num_parameters;
+ if (i > commands_len) {
+ dev_err(dev, "config: command=0x%02x num_parameters=%u overflows\n",
+ command, num_parameters);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (command == 0x00 && num_parameters == 1)
+ dev_dbg(dev, "sleep %ums\n", parameters[0]);
+ else
+ dev_dbg(dev, "command %02x %*ph\n",
+ command, num_parameters, parameters);
+ }
+
+ if (i != commands_len) {
+ dev_err(dev, "config: malformed command array\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ commands = kzalloc(sizeof(*commands), GFP_KERNEL);
+ if (!commands)
+ return ERR_PTR(-ENOMEM);
+
+ commands->len = commands_len;
+ commands->buf = kmemdup(config->commands, commands->len, GFP_KERNEL);
+ if (!commands->buf)
+ return ERR_PTR(-ENOMEM);
+
+ return commands;
+}
+
+static struct panel_mipi_dbi_commands *panel_mipi_dbi_commands_from_fw(struct device *dev)
+{
+ struct panel_mipi_dbi_commands *commands;
+ const struct firmware *fw;
+ const char *compatible;
+ char fw_name[40];
+ int ret;
+
+ ret = of_property_read_string_index(dev->of_node, "compatible", 0, &compatible);
+ if (ret)
+ return ERR_PTR(ret);
+
+ snprintf(fw_name, sizeof(fw_name), "%s.bin", compatible);
+ ret = request_firmware(&fw, fw_name, dev);
+ if (ret) {
+ dev_err(dev, "No config file found for compatible '%s' (error=%d)\n",
+ compatible, ret);
+
+ return ERR_PTR(ret);
+ }
+
+ commands = panel_mipi_dbi_check_commands(dev, fw);
+ release_firmware(fw);
+
+ return commands;
+}
+
+static void panel_mipi_dbi_commands_execute(struct mipi_dbi *dbi,
+ struct panel_mipi_dbi_commands *commands)
+{
+ unsigned int i = 0;
+
+ if (!commands)
+ return;
+
+ while (i < commands->len) {
+ u8 command = commands->buf[i++];
+ u8 num_parameters = commands->buf[i++];
+ const u8 *parameters = &commands->buf[i];
+
+ if (command == 0x00 && num_parameters == 1)
+ mdelay(parameters[0]);
+ else if (num_parameters)
+ mipi_dbi_command_stackbuf(dbi, command, parameters, num_parameters);
+ else
+ mipi_dbi_command(dbi, command);
+
+ i += num_parameters;
+ }
+}
+
+static void panel_mipi_dbi_enable(struct fb_info *info)
+{
+ struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info);
+ struct mipi_dbi *dbi = &dbidev->dbi;
+ int ret;
+
+ if (!info->mode) {
+ dev_err(dbidev->dev, "No valid mode found\n");
+ return;
+ }
+
+ if (dbidev->backlight_node && !dbidev->backlight) {
+ dbidev->backlight = of_backlight_find(dbidev->backlight_node);
+ if (!dbidev->backlight)
+ dev_err(dbidev->dev, "No backlight found\n");
+ }
+
+ if (!dbidev->driver_private) {
+ dbidev->driver_private = panel_mipi_dbi_commands_from_fw(dbidev->dev);
+ if (IS_ERR(dbidev->driver_private)) {
+ dbidev->driver_private = NULL;
+ return;
+ }
+ }
+
+ ret = mipi_dbi_poweron_conditional_reset(dbidev);
+ if (ret < 0)
+ return;
+ if (!ret)
+ panel_mipi_dbi_commands_execute(dbi, dbidev->driver_private);
+
+ mipi_dbi_enable_flush(dbidev, info);
+}
+
+
+static struct fb_ops panel_mipi_dbi_ops = {
+ .fb_enable = panel_mipi_dbi_enable,
+ .fb_disable = mipi_dbi_fb_disable,
+ .fb_damage = mipi_dbi_fb_damage,
+ .fb_flush = mipi_dbi_fb_flush,
+};
+
+
+static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev, struct fb_videomode *mode)
+{
+ struct device *dev = dbidev->dev;
+ int ret;
+
+ ret = of_get_display_timing(dev->of_node, "panel-timing", mode);
+ if (ret) {
+ dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n", dev->of_node, ret);
+ return ret;
+ }
+
+ /*
+ * Make sure width and height are set and that only back porch and
+ * pixelclock are set in the other timing values. Also check that
+ * width and height don't exceed the 16-bit value specified by MIPI DCS.
+ */
+ if (!mode->xres || !mode->yres || mode->display_flags ||
+ mode->right_margin || mode->hsync_len || (mode->left_margin + mode->xres) > 0xffff ||
+ mode->lower_margin || mode->vsync_len || (mode->upper_margin + mode->yres) > 0xffff) {
+ dev_err(dev, "%pOF: panel-timing out of bounds\n", dev->of_node);
+ return -EINVAL;
+ }
+
+ /* The driver doesn't use the pixel clock but it is mandatory so fake one if not set */
+ if (!mode->pixclock) {
+ mode->pixclock =
+ (mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len) *
+ (mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len) *
+ 60 / 1000;
+ }
+
+ return 0;
+}
+
+static int panel_mipi_dbi_spi_probe(struct device *dev)
+{
+ struct mipi_dbi_dev *dbidev;
+ struct spi_device *spi = to_spi_device(dev);
+ struct mipi_dbi *dbi;
+ struct fb_info *info;
+ struct gpio_desc *dc;
+ int ret;
+
+ dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL);
+ if (!dbidev)
+ return -ENOMEM;
+
+ dbidev->dev = dev;
+ dbi = &dbidev->dbi;
+ info = &dbidev->info;
+
+ ret = panel_mipi_dbi_get_mode(dbidev, &dbidev->mode);
+ if (ret)
+ return ret;
+
+ dbidev->regulator = regulator_get(dev, "power");
+ if (IS_ERR(dbidev->regulator))
+ return dev_err_probe(dev, PTR_ERR(dbidev->regulator),
+ "Failed to get regulator 'power'\n");
+
+ dbidev->io_regulator = regulator_get(dev, "io");
+ if (IS_ERR(dbidev->io_regulator))
+ return dev_err_probe(dev, PTR_ERR(dbidev->io_regulator),
+ "Failed to get regulator 'io'\n");
+
+ dbidev->backlight_node = of_parse_phandle(dev->of_node, "backlight", 0);
+
+ dbi->reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(dbi->reset))
+ return dev_errp_probe(dev, dbi->reset,
+ "Failed to get GPIO 'reset'\n");
+
+ dc = gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
+ if (IS_ERR(dc))
+ return dev_errp_probe(dev, dc, "Failed to get GPIO 'dc'\n");
+
+ ret = mipi_dbi_spi_init(spi, dbi, dc);
+ if (ret)
+ return ret;
+
+ ret = mipi_dbi_dev_init(dbidev, &panel_mipi_dbi_ops, &dbidev->mode);
+ if (ret)
+ return ret;
+
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to register framebuffer\n");
+
+ return 0;
+}
+
+static const struct of_device_id panel_mipi_dbi_spi_of_match[] = {
+ { .compatible = "panel-mipi-dbi-spi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, panel_mipi_dbi_spi_of_match);
+
+static struct driver panel_mipi_dbi_spi_driver = {
+ .name = "panel-mipi-dbi-spi",
+ .probe = panel_mipi_dbi_spi_probe,
+ .of_compatible = DRV_OF_COMPAT(panel_mipi_dbi_spi_of_match),
+};
+device_spi_driver(panel_mipi_dbi_spi_driver);
+
+MODULE_DESCRIPTION("MIPI DBI compatible display panel driver");
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/pxa.c b/drivers/video/pxa.c
index 7e2905f279..561a73fb32 100644
--- a/drivers/video/pxa.c
+++ b/drivers/video/pxa.c
@@ -23,10 +23,10 @@
#include <malloc.h>
#include <linux/err.h>
-#include <mach/clock.h>
-#include <mach/pxa-regs.h>
-#include <mach/regs-lcd.h>
-#include <mach/pxafb.h>
+#include <mach/pxa/clock.h>
+#include <mach/pxa/pxa-regs.h>
+#include <mach/pxa/regs-lcd.h>
+#include <mach/pxa/pxafb.h>
#include <asm/io.h>
#include <linux/math64.h>
@@ -74,7 +74,7 @@ struct pxafb_info {
struct pxafb_videomode *mode;
struct fb_info info;
- struct device_d *dev;
+ struct device *dev;
void (*lcd_power)(int);
void (*backlight_power)(int);
@@ -476,7 +476,7 @@ static struct fb_ops pxafb_ops = {
.fb_disable = pxafb_disable_controller,
};
-static int pxafb_probe(struct device_d *dev)
+static int pxafb_probe(struct device *dev)
{
struct resource *iores;
struct pxafb_platform_data *pdata = dev->platform_data;
@@ -534,7 +534,7 @@ static int pxafb_probe(struct device_d *dev)
return 0;
}
-static struct driver_d pxafb_driver = {
+static struct driver pxafb_driver = {
.name = "pxafb",
.probe = pxafb_probe,
};
diff --git a/drivers/video/ramfb.c b/drivers/video/ramfb.c
new file mode 100644
index 0000000000..3442e81b9e
--- /dev/null
+++ b/drivers/video/ramfb.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: (C) 2022 Adrian Negreanu
+
+#define pr_fmt(fmt) "ramfb: " fmt
+
+#include <common.h>
+#include <fb.h>
+#include <fcntl.h>
+#include <dma.h>
+#include <init.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fs.h>
+#include <linux/qemu_fw_cfg.h>
+#include <video/fourcc.h>
+
+struct ramfb {
+ int fd;
+ struct fb_info info;
+ dma_addr_t screen_dma;
+ struct fb_videomode mode;
+ u16 etcfb_select;
+};
+
+struct fw_cfg_etc_ramfb {
+ u64 addr;
+ u32 fourcc;
+ u32 flags;
+ u32 width;
+ u32 height;
+ u32 stride;
+} __packed;
+
+static int fw_cfg_find_file(struct device *dev, int fd, const char *filename)
+{
+ size_t filename_len = strlen(filename);
+ ssize_t ret;
+ __be32 count;
+ int i;
+
+ ioctl(fd, FW_CFG_SELECT, &(u16) { FW_CFG_FILE_DIR });
+
+ lseek(fd, 0, SEEK_SET);
+
+ ret = read(fd, &count, sizeof(count));
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < be32_to_cpu(count); i++) {
+ struct fw_cfg_file qfile;
+
+ read(fd, &qfile, sizeof(qfile));
+
+ dev_dbg(dev, "enumerating file %s\n", qfile.name);
+
+ if (memcmp(qfile.name, filename, filename_len))
+ continue;
+
+ return be16_to_cpu(qfile.select);
+ }
+
+ return -ENOENT;
+}
+
+static void ramfb_populate_modes(struct ramfb *ramfb)
+{
+ struct fb_info *info = &ramfb->info;
+
+ ramfb->mode.name = "x8r8g8b8";
+ info->xres = ramfb->mode.xres = 640;
+ info->yres = ramfb->mode.yres = 480;
+
+ info->mode = &ramfb->mode;
+ info->bits_per_pixel = 32;
+ info->red = (struct fb_bitfield) {16, 8};
+ info->green = (struct fb_bitfield) {8, 8};
+ info->blue = (struct fb_bitfield) {0, 8};
+ info->transp = (struct fb_bitfield) {0, 0};
+}
+
+static int ramfb_activate_var(struct fb_info *fbi)
+{
+ struct ramfb *ramfb = fbi->priv;
+
+ if (fbi->screen_base)
+ dma_free_coherent(fbi->screen_base, ramfb->screen_dma, fbi->screen_size);
+
+ fbi->screen_size = fbi->xres * fbi->yres * fbi->bits_per_pixel;
+ fbi->screen_base = dma_alloc_coherent(fbi->screen_size, &ramfb->screen_dma);
+
+ return 0;
+}
+
+static void ramfb_enable(struct fb_info *fbi)
+{
+ struct ramfb *ramfb = fbi->priv;
+ struct fw_cfg_etc_ramfb *etc_ramfb;
+
+ etc_ramfb = dma_alloc(sizeof(*etc_ramfb));
+
+ etc_ramfb->addr = cpu_to_be64(ramfb->screen_dma);
+ etc_ramfb->fourcc = cpu_to_be32(DRM_FORMAT_XRGB8888);
+ etc_ramfb->flags = cpu_to_be32(0);
+ etc_ramfb->width = cpu_to_be32(fbi->xres);
+ etc_ramfb->height = cpu_to_be32(fbi->yres);
+ etc_ramfb->stride = cpu_to_be32(fbi->line_length);
+
+ ioctl(ramfb->fd, FW_CFG_SELECT, &ramfb->etcfb_select);
+
+ pwrite(ramfb->fd, etc_ramfb, sizeof(*etc_ramfb), 0);
+
+ dma_free(etc_ramfb);
+}
+
+static struct fb_ops ramfb_ops = {
+ .fb_activate_var = ramfb_activate_var,
+ .fb_enable = ramfb_enable,
+};
+
+static int ramfb_probe(struct device *parent_dev, int fd)
+{
+ int ret;
+ struct ramfb *ramfb;
+ struct fb_info *fbi;
+
+ ret = -ENODEV;
+
+ ramfb = xzalloc(sizeof(*ramfb));
+
+ ramfb->fd = fd;
+
+ ret = fw_cfg_find_file(parent_dev, fd, "etc/ramfb");
+ if (ret < 0) {
+ dev_dbg(parent_dev, "ramfb: fw_cfg (etc/ramfb) file not found\n");
+ return -ENODEV;
+ }
+
+ ramfb->etcfb_select = ret;
+ dev_dbg(parent_dev, "etc/ramfb file at slot 0x%x\n", ramfb->etcfb_select);
+
+ fbi = &ramfb->info;
+ fbi->priv = ramfb;
+ fbi->fbops = &ramfb_ops;
+ fbi->dev.parent = parent_dev;
+
+ ramfb_populate_modes(ramfb);
+
+ ret = register_framebuffer(fbi);
+ if (ret < 0) {
+ dev_err(parent_dev, "Unable to register ramfb: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(parent_dev, "ramfb registered\n");
+
+ return 0;
+}
+
+static int ramfb_driver_init(void)
+{
+ struct cdev *cdev;
+ int err = 0;
+
+ for_each_cdev(cdev) {
+ int fd, ret;
+
+ if (!strstarts(cdev->name, "fw_cfg"))
+ continue;
+
+ fd = cdev_fdopen(cdev, O_RDWR);
+ if (fd < 0) {
+ err = fd;
+ continue;
+ }
+
+ ret = ramfb_probe(cdev->dev, fd);
+ if (ret == 0)
+ continue;
+ if (ret != -ENODEV && ret != -ENXIO)
+ err = ret;
+
+ close(fd);
+ }
+
+ return err;
+}
+device_initcall(ramfb_driver_init);
+
+MODULE_AUTHOR("Adrian Negreanu <adrian.negreanu@nxp.com>");
+MODULE_DESCRIPTION("QEMU RamFB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/rave-sp-backlight.c b/drivers/video/rave-sp-backlight.c
index ca6c5aa3e3..360b27eebb 100644
--- a/drivers/video/rave-sp-backlight.c
+++ b/drivers/video/rave-sp-backlight.c
@@ -27,7 +27,7 @@ static int rave_sp_backlight_set(struct backlight_device *bd, int brightness)
return rave_sp_exec(sp, cmd, sizeof(cmd), NULL, 0);
}
-static int rave_sp_backlight_probe(struct device_d *dev)
+static int rave_sp_backlight_probe(struct device *dev)
{
struct backlight_device *bd;
int ret;
@@ -52,8 +52,9 @@ static const struct of_device_id rave_sp_backlight_of_match[] = {
{ .compatible = "zii,rave-sp-backlight" },
{}
};
+MODULE_DEVICE_TABLE(of, rave_sp_backlight_of_match);
-static struct driver_d rave_sp_backlight_driver = {
+static struct driver rave_sp_backlight_driver = {
.name = "rave-sp-backlight",
.probe = rave_sp_backlight_probe,
.of_compatible = DRV_OF_COMPAT(rave_sp_backlight_of_match),
diff --git a/drivers/video/s3c24xx.c b/drivers/video/s3c24xx.c
deleted file mode 100644
index 6de2d7be98..0000000000
--- a/drivers/video/s3c24xx.c
+++ /dev/null
@@ -1,411 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2010 Juergen Beisert
- * Copyright (C) 2011 Alexey Galakhov
- *
- * This driver is based on a patch found in the web:
- * (C) Copyright 2006 by OpenMoko, Inc.
- * Author: Harald Welte <laforge at openmoko.org>
- */
-
-#include <common.h>
-#include <init.h>
-#include <fb.h>
-#include <driver.h>
-#include <malloc.h>
-#include <errno.h>
-#include <io.h>
-#include <mach/s3c-generic.h>
-#include <mach/s3c24xx-fb.h>
-
-#define LCDCON1 0x00
-# define PNRMODE(x) (((x) & 3) << 5)
-# define BPPMODE(x) (((x) & 0xf) << 1)
-# define SET_CLKVAL(x) (((x) & 0x3ff) << 8)
-# define GET_CLKVAL(x) (((x) >> 8) & 0x3ff)
-# define ENVID (1 << 0)
-
-#define LCDCON2 0x04
-# define SET_VBPD(x) (((x) & 0xff) << 24)
-# define SET_LINEVAL(x) (((x) & 0x3ff) << 14)
-# define SET_VFPD(x) (((x) & 0xff) << 6)
-# define SET_VSPW(x) ((x) & 0x3f)
-
-#define LCDCON3 0x08
-# define SET_HBPD(x) (((x) & 0x7f) << 19)
-# define SET_HOZVAL(x) (((x) & 0x7ff) << 8)
-# define SET_HFPD(x) ((x) & 0xff)
-
-#define LCDCON4 0x0c
-# define SET_HSPW(x) ((x) & 0xff)
-
-#define LCDCON5 0x10
-# define BPP24BL (1 << 12)
-# define FRM565 (1 << 11)
-# define INV_CLK (1 << 10)
-# define INV_HS (1 << 9)
-# define INV_VS (1 << 8)
-# define INV_DTA (1 << 7)
-# define INV_DE (1 << 6)
-# define INV_PWREN (1 << 5)
-# define INV_LEND (1 << 4)
-# define ENA_PWREN (1 << 3)
-# define ENA_LEND (1 << 2)
-# define BSWP (1 << 1)
-# define HWSWP (1 << 0)
-
-#define LCDSADDR1 0x14
-# define SET_LCDBANK(x) (((x) & 0x1ff) << 21)
-# define GET_LCDBANK(x) (((x) >> 21) & 0x1ff)
-# define SET_LCDBASEU(x) ((x) & 0x1fffff)
-# define GET_LCDBASEU(x) ((x) & 0x1fffff)
-
-#define LCDSADDR2 0x18
-# define SET_LCDBASEL(x) ((x) & 0x1fffff)
-# define GET_LCDBASEL(x) ((x) & 0x1fffff)
-
-#define LCDSADDR3 0x1c
-# define SET_OFFSIZE(x) (((x) & 0x7ff) << 11)
-# define GET_OFFSIZE(x) (((x) >> 11) & 0x7ff)
-# define SET_PAGE_WIDTH(x) ((x) & 0x3ff)
-# define GET_PAGE_WIDTH(x) ((x) & 0x3ff)
-
-#define RED_LUT 0x20
-#define GREEN_LUT 0x24
-#define BLUE_LUT 0x28
-
-#define DITHMODE 0x4c
-
-#define TPAL 0x50
-
-#define LCDINTPND 0x54
-#define LCDSRCPND 0x58
-#define LCDINTMSK 0x5c
-# define FIWSEL (1 << 2)
-# define INT_FrSyn (1 << 1)
-# define INT_FiCnt (1 << 0)
-
-#define TCONSEL 0x60
-
-#define RED 0
-#define GREEN 1
-#define BLUE 2
-#define TRANSP 3
-
-struct s3cfb_info {
- void __iomem *base;
- unsigned memory_size;
- struct fb_info info;
- struct device_d *hw_dev;
- int passive_display;
- void (*enable)(int enable);
-};
-
-/* the RGB565 true colour mode */
-static const struct fb_bitfield def_rgb565[] = {
- [RED] = {
- .offset = 11,
- .length = 5,
- },
- [GREEN] = {
- .offset = 5,
- .length = 6,
- },
- [BLUE] = {
- .offset = 0,
- .length = 5,
- },
- [TRANSP] = { /* no support for transparency */
- .length = 0,
- }
-};
-
-/* the RGB888 true colour mode */
-static const struct fb_bitfield def_rgb888[] = {
- [RED] = {
- .offset = 16,
- .length = 8,
- },
- [GREEN] = {
- .offset = 8,
- .length = 8,
- },
- [BLUE] = {
- .offset = 0,
- .length = 8,
- },
- [TRANSP] = { /* no support for transparency */
- .length = 0,
- }
-};
-
-/**
- * @param fb_info Framebuffer information
- */
-static void s3cfb_enable_controller(struct fb_info *fb_info)
-{
- struct s3cfb_info *fbi = fb_info->priv;
- uint32_t con1;
-
- con1 = readl(fbi->base + LCDCON1);
-
- con1 |= ENVID;
-
- writel(con1, fbi->base + LCDCON1);
-
- if (fbi->enable)
- fbi->enable(1);
-}
-
-/**
- * @param fb_info Framebuffer information
- */
-static void s3cfb_disable_controller(struct fb_info *fb_info)
-{
- struct s3cfb_info *fbi = fb_info->priv;
- uint32_t con1;
-
- if (fbi->enable)
- fbi->enable(0);
-
- con1 = readl(fbi->base + LCDCON1);
-
- con1 &= ~ENVID;
-
- writel(con1, fbi->base + LCDCON1);
-}
-
-/**
- * Prepare the video hardware for a specified video mode
- * @param fb_info Framebuffer information
- * @return 0 on success
- */
-static int s3cfb_activate_var(struct fb_info *fb_info)
-{
- struct s3cfb_info *fbi = fb_info->priv;
- struct fb_videomode *mode = fb_info->mode;
- unsigned size, hclk, div;
- uint32_t con1, con2, con3, con4, con5 = 0;
-
- if (fbi->passive_display != 0) {
- dev_err(fbi->hw_dev, "Passive displays are currently not supported\n");
- return -EINVAL;
- }
-
- /*
- * we need at least this amount of memory for the framebuffer
- */
- size = mode->xres * mode->yres * (fb_info->bits_per_pixel >> 3);
- if (fbi->memory_size != size || fb_info->screen_base == NULL) {
- if (fb_info->screen_base)
- free(fb_info->screen_base);
- fbi->memory_size = 0;
- fb_info->screen_base = malloc(size);
- if (! fb_info->screen_base)
- return -ENOMEM;
- memset(fb_info->screen_base, 0, size);
- fbi->memory_size = size;
- }
-
- /* ensure video output is _off_ */
- writel(0x00000000, fbi->base + LCDCON1);
-
- hclk = s3c_get_hclk() / 1000U; /* hclk in kHz */
- div = hclk / PICOS2KHZ(mode->pixclock);
- if (div < 3)
- div = 3;
- /* pixel clock is: (hclk) / ((div + 1) * 2) */
- div += 1;
- div >>= 1;
- div -= 1;
-
- con1 = PNRMODE(3) | SET_CLKVAL(div); /* PNRMODE=3 is TFT */
-
- switch (fb_info->bits_per_pixel) {
- case 16:
- con1 |= BPPMODE(12);
- con5 |= FRM565;
- con5 |= HWSWP;
- fb_info->red = def_rgb565[RED];
- fb_info->green = def_rgb565[GREEN];
- fb_info->blue = def_rgb565[BLUE];
- fb_info->transp = def_rgb565[TRANSP];
- break;
- case 24:
- con1 |= BPPMODE(13);
- /* con5 |= BPP24BL; */ /* FIXME maybe needed, check alignment */
- fb_info->red = def_rgb888[RED];
- fb_info->green = def_rgb888[GREEN];
- fb_info->blue = def_rgb888[BLUE];
- fb_info->transp = def_rgb888[TRANSP];
- break;
- default:
- dev_err(fbi->hw_dev, "Invalid bits per pixel value: %u\n", fb_info->bits_per_pixel);
- return -EINVAL;
- }
-
- /* 'normal' in register description means positive logic */
- if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
- con5 |= INV_HS;
- if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
- con5 |= INV_VS;
- if (!(mode->sync & FB_SYNC_DE_HIGH_ACT))
- con5 |= INV_DE;
- if (mode->sync & FB_SYNC_CLK_INVERT)
- con5 |= INV_CLK; /* display should latch at the rising edge */
- if (mode->sync & FB_SYNC_DATA_INVERT)
- con5 |= INV_DTA;
- if (mode->sync & FB_SYNC_INVERT_PWREN)
- con5 |= INV_PWREN;
- if (mode->sync & FB_SYNC_INVERT_LEND)
- con5 |= INV_LEND;
- if (mode->sync & FB_SYNC_USE_PWREN)
- con5 |= ENA_PWREN; /* FIXME should this be done conditionally/later? */
- if (mode->sync & FB_SYNC_USE_LEND)
- con5 |= ENA_LEND;
- if (mode->sync & FB_SYNC_SWAP_BYTES)
- con5 ^= BSWP;
- if (mode->sync & FB_SYNC_SWAP_HW)
- con5 ^= HWSWP;
-
- /* vertical timing */
- con2 = SET_VBPD(mode->upper_margin - 1) |
- SET_LINEVAL(mode->yres - 1) |
- SET_VFPD(mode->lower_margin - 1) |
- SET_VSPW(mode->vsync_len - 1);
-
- /* horizontal timing */
- con3 = SET_HBPD(mode->left_margin - 1) |
- SET_HOZVAL(mode->xres - 1) |
- SET_HFPD(mode->right_margin - 1);
- con4 = SET_HSPW(mode->hsync_len - 1);
-
- /* basic timing setup */
- writel(con1, fbi->base + LCDCON1);
- dev_dbg(fbi->hw_dev, "writing %08X into %p (con1)\n", con1, fbi->base + LCDCON1);
- writel(con2, fbi->base + LCDCON2);
- dev_dbg(fbi->hw_dev, "writing %08X into %p (con2)\n", con2, fbi->base + LCDCON2);
- writel(con3, fbi->base + LCDCON3);
- dev_dbg(fbi->hw_dev, "writing %08X into %p (con3)\n", con3, fbi->base + LCDCON3);
- writel(con4, fbi->base + LCDCON4);
- dev_dbg(fbi->hw_dev, "writing %08X into %p (con4)\n", con4, fbi->base + LCDCON4);
- writel(con5, fbi->base + LCDCON5);
- dev_dbg(fbi->hw_dev, "writing %08X into %p (con5)\n", con5, fbi->base + LCDCON5);
-
- dev_dbg(fbi->hw_dev, "setting up the fb baseadress to %p\n", fb_info->screen_base);
-
- /* framebuffer memory setup */
- writel((unsigned)fb_info->screen_base >> 1, fbi->base + LCDSADDR1);
- size = mode->xres * (fb_info->bits_per_pixel >> 3) * (mode->yres);
- writel(SET_LCDBASEL(((unsigned)fb_info->screen_base + size) >> 1), fbi->base + LCDSADDR2);
- writel(SET_OFFSIZE(0) |
- SET_PAGE_WIDTH((mode->xres * fb_info->bits_per_pixel) >> 4),
- fbi->base + LCDSADDR3);
- writel(FIWSEL | INT_FrSyn | INT_FiCnt, fbi->base + LCDINTMSK);
-
- return 0;
-}
-
-/**
- * Print some information about the current hardware state
- * @param hw_dev S3C video device
- */
-static void s3cfb_info(struct device_d *hw_dev)
-{
- uint32_t con1, addr1, addr2, addr3;
- struct s3cfb_info *fbi = hw_dev->priv;
-
- con1 = readl(fbi->base + LCDCON1);
- addr1 = readl(fbi->base + LCDSADDR1);
- addr2 = readl(fbi->base + LCDSADDR2);
- addr3 = readl(fbi->base + LCDSADDR3);
-
- printf(" Video hardware info:\n");
- printf(" Video clock is running at %u Hz\n", s3c_get_hclk() / ((GET_CLKVAL(con1) + 1) * 2));
- printf(" Video memory bank starts at 0x%08X\n", GET_LCDBANK(addr1) << 22);
- printf(" Video memory bank offset: 0x%08X\n", GET_LCDBASEU(addr1));
- printf(" Video memory end: 0x%08X\n", GET_LCDBASEU(addr2));
- printf(" Virtual screen offset size: %u half words\n", GET_OFFSIZE(addr3));
- printf(" Virtual screen page width: %u half words\n", GET_PAGE_WIDTH(addr3));
-}
-
-/*
- * There is only one video hardware instance available.
- * It makes no sense to dynamically allocate this data
- */
-static struct fb_ops s3cfb_ops = {
- .fb_activate_var = s3cfb_activate_var,
- .fb_enable = s3cfb_enable_controller,
- .fb_disable = s3cfb_disable_controller,
-};
-
-static struct s3cfb_info fbi = {
- .info = {
- .fbops = &s3cfb_ops,
- },
-};
-
-static int s3cfb_probe(struct device_d *hw_dev)
-{
- struct resource *iores;
- struct s3c_fb_platform_data *pdata = hw_dev->platform_data;
- int ret;
-
- if (! pdata)
- return -ENODEV;
-
- iores = dev_request_mem_resource(hw_dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- fbi.base = IOMEM(iores->start);
- writel(0, fbi.base + LCDCON1);
- writel(0, fbi.base + LCDCON5); /* FIXME not 0 for some displays */
-
- /* just init */
- fbi.info.priv = &fbi;
-
- /* add runtime hardware info */
- fbi.hw_dev = hw_dev;
- hw_dev->priv = &fbi;
-
- /* add runtime video info */
- fbi.info.modes.modes = pdata->mode_list;
- fbi.info.modes.num_modes = pdata->mode_cnt;
- fbi.info.mode = &fbi.info.modes.modes[0];
- fbi.info.xres = fbi.info.mode->xres;
- fbi.info.yres = fbi.info.mode->yres;
- if (pdata->bits_per_pixel)
- fbi.info.bits_per_pixel = pdata->bits_per_pixel;
- else
- fbi.info.bits_per_pixel = 16;
- fbi.passive_display = pdata->passive_display;
- fbi.enable = pdata->enable;
-
- if (IS_ENABLED(CONFIG_DRIVER_VIDEO_S3C_VERBOSE))
- hw_dev->info = s3cfb_info;
-
- fbi.info.dev.parent = hw_dev;
- ret = register_framebuffer(&fbi.info);
- if (ret != 0) {
- dev_err(hw_dev, "Failed to register framebuffer\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static struct driver_d s3cfb_driver = {
- .name = "s3c_fb",
- .probe = s3cfb_probe,
-};
-device_platform_driver(s3cfb_driver);
-
-/**
- * The S3C244x LCD controller supports passive (CSTN/STN) and active (TFT) LC displays
- *
- * The driver itself currently supports only active TFT LC displays in the follwing manner:
- *
- * * True colours
- * - 16 bpp
- * - 24 bpp (untested)
- */
diff --git a/drivers/video/sdl.c b/drivers/video/sdl.c
index 28a41916eb..06e13b7735 100644
--- a/drivers/video/sdl.c
+++ b/drivers/video/sdl.c
@@ -38,7 +38,7 @@ static struct fb_ops sdlfb_ops = {
.fb_disable = sdlfb_disable,
};
-static int sdlfb_probe(struct device_d *dev)
+static int sdlfb_probe(struct device *dev)
{
struct fb_info *fb;
int ret = -EIO;
@@ -81,7 +81,7 @@ static int sdlfb_probe(struct device_d *dev)
return ret;
}
-static void sdlfb_remove(struct device_d *dev)
+static void sdlfb_remove(struct device *dev)
{
struct fb_info *fb = dev->priv;
@@ -89,7 +89,7 @@ static void sdlfb_remove(struct device_d *dev)
kfree(fb);
}
-static struct driver_d sdlfb_driver = {
+static struct driver sdlfb_driver = {
.name = "sdlfb",
.probe = sdlfb_probe,
.remove = sdlfb_remove,
diff --git a/drivers/video/simple-panel.c b/drivers/video/simple-panel.c
index a87adcca64..7048e2f51b 100644
--- a/drivers/video/simple-panel.c
+++ b/drivers/video/simple-panel.c
@@ -17,7 +17,7 @@
#include <i2c/i2c.h>
struct simple_panel {
- struct device_d *dev;
+ struct device *dev;
struct vpl vpl;
int enable_gpio;
int enable_active_high;
@@ -99,7 +99,7 @@ static int simple_panel_get_modes(struct simple_panel *panel, struct display_tim
}
}
- modes = of_get_display_timings(panel->dev->device_node);
+ modes = of_get_display_timings(panel->dev->of_node);
if (modes) {
timings->modes = modes->modes;
timings->num_modes = modes->num_modes;
@@ -129,10 +129,10 @@ static int simple_panel_ioctl(struct vpl *vpl, unsigned int port,
}
}
-static int simple_panel_probe(struct device_d *dev)
+static int simple_panel_probe(struct device *dev)
{
struct simple_panel *panel;
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
enum of_gpio_flags flags;
int ret;
@@ -165,8 +165,9 @@ static struct of_device_id simple_panel_of_ids[] = {
{ .compatible = "simple-panel", },
{ }
};
+MODULE_DEVICE_TABLE(of, simple_panel_of_ids);
-static struct driver_d simple_panel_driver = {
+static struct driver simple_panel_driver = {
.name = "simple-panel",
.probe = simple_panel_probe,
.of_compatible = DRV_OF_COMPAT(simple_panel_of_ids),
diff --git a/drivers/video/simplefb-client.c b/drivers/video/simplefb-client.c
index 2367713ba4..dafec6178f 100644
--- a/drivers/video/simplefb-client.c
+++ b/drivers/video/simplefb-client.c
@@ -35,10 +35,10 @@ struct simplefb {
struct fb_videomode mode;
};
-static int simplefb_parse_dt(struct device_d *dev,
- struct simplefb_params *params)
+static int simplefb_parse_dt(struct device *dev,
+ struct simplefb_params *params)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int ret;
const char *format;
int i;
@@ -81,7 +81,7 @@ static int simplefb_parse_dt(struct device_d *dev,
return 0;
}
-static int simplefb_probe(struct device_d *dev)
+static int simplefb_probe(struct device *dev)
{
int ret;
struct simplefb_params params;
@@ -90,7 +90,7 @@ static int simplefb_probe(struct device_d *dev)
struct resource *mem;
ret = -ENODEV;
- if (dev->device_node)
+ if (dev->of_node)
ret = simplefb_parse_dt(dev, &params);
if (ret)
@@ -138,8 +138,9 @@ static const struct of_device_id simplefb_of_match[] = {
{ .compatible = "simple-framebuffer", },
{ },
};
+MODULE_DEVICE_TABLE(of, simplefb_of_match);
-static struct driver_d simplefb_driver = {
+static struct driver simplefb_driver = {
.name = "simple-framebuffer",
.of_compatible = simplefb_of_match,
.probe = simplefb_probe,
diff --git a/drivers/video/simplefb-fixup.c b/drivers/video/simplefb-fixup.c
index a2c59de364..65e0281a18 100644
--- a/drivers/video/simplefb-fixup.c
+++ b/drivers/video/simplefb-fixup.c
@@ -90,6 +90,7 @@ static int simplefb_create_node(struct device_node *root,
const struct fb_info *fbi, const char *format)
{
struct device_node *node;
+ phys_addr_t screen_base;
u32 cells[2];
int ret;
@@ -105,7 +106,11 @@ static int simplefb_create_node(struct device_node *root,
if (ret)
return ret;
- cells[0] = cpu_to_be32((u32)fbi->screen_base);
+ screen_base = virt_to_phys(fbi->screen_base);
+ if (upper_32_bits(screen_base))
+ return -ENOSYS;
+
+ cells[0] = cpu_to_be32(lower_32_bits(screen_base));
cells[1] = cpu_to_be32(fbi->line_length * fbi->yres);
ret = of_set_property(node, "reg", cells, sizeof(cells[0]) * 2, 1);
if (ret < 0)
@@ -130,8 +135,7 @@ static int simplefb_create_node(struct device_node *root,
if (ret < 0)
return ret;
- of_add_reserve_entry((u32)fbi->screen_base,
- (u32)fbi->screen_base + fbi->screen_size);
+ of_add_reserve_entry(screen_base, screen_base + fbi->screen_size);
return of_property_write_string(node, "status", "okay");
}
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 9e141a851f..fb50e895c5 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -423,13 +423,14 @@ static const struct of_device_id ssd1307fb_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
-static int ssd1307fb_probe(struct device_d *dev)
+static int ssd1307fb_probe(struct device *dev)
{
struct fb_info *info;
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
const struct of_device_id *match =
- of_match_node(ssd1307fb_of_match, dev->device_node);
+ of_match_node(ssd1307fb_of_match, dev->of_node);
u32 vmem_size;
struct ssd1307fb_par *par;
struct ssd1307fb_array *array;
@@ -451,12 +452,12 @@ static int ssd1307fb_probe(struct device_d *dev)
par->device_info = (struct ssd1307fb_deviceinfo *)match->data;
- if (IS_ENABLED(CONFIG_I2C) && dev->bus == &i2c_bus) {
+ if (dev_bus_is_i2c(dev)) {
par->client = to_i2c_client(dev);
i2c_set_clientdata(par->client, par);
par->write_array = ssd1307fb_i2c_write_array;
}
- if (IS_ENABLED(CONFIG_SPI) && dev->bus == &spi_bus) {
+ if (dev_bus_is_spi(dev)) {
par->spi = to_spi_device(dev);
par->dc = of_get_named_gpio(node, "dc-gpios", 0);
if (!gpio_is_valid(par->dc)) {
@@ -634,14 +635,14 @@ fb_alloc_error:
return ret;
}
-static __maybe_unused struct driver_d ssd1307fb_i2c_driver = {
+static __maybe_unused struct driver ssd1307fb_i2c_driver = {
.name = "ssd1307fb-i2c",
.probe = ssd1307fb_probe,
.of_compatible = DRV_OF_COMPAT(ssd1307fb_of_match),
};
device_i2c_driver(ssd1307fb_i2c_driver);
-static __maybe_unused struct driver_d ssd1307fb_spi_driver = {
+static __maybe_unused struct driver ssd1307fb_spi_driver = {
.name = "ssd1307fb-spi",
.probe = ssd1307fb_probe,
.of_compatible = DRV_OF_COMPAT(ssd1307fb_of_match),
diff --git a/drivers/video/stm.c b/drivers/video/stm.c
index 14182f8c46..917405ea80 100644
--- a/drivers/video/stm.c
+++ b/drivers/video/stm.c
@@ -20,7 +20,7 @@
#include <stmp-device.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <mach/fb.h>
+#include <mach/mxs/fb.h>
#define HW_LCDIF_CTRL 0x00
# define CTRL_SFTRST (1 << 31)
@@ -139,7 +139,7 @@ struct imxfb_info {
void __iomem *base;
unsigned memory_size;
struct fb_info info;
- struct device_d *hw_dev;
+ struct device *hw_dev;
struct clk *clk;
void *fixed_screen;
unsigned fixed_screen_size;
@@ -494,7 +494,7 @@ static struct imxfb_info fbi = {
},
};
-static int stmfb_probe(struct device_d *hw_dev)
+static int stmfb_probe(struct device *hw_dev)
{
struct resource *iores;
struct imx_fb_platformdata *pdata = hw_dev->platform_data;
@@ -532,10 +532,10 @@ static int stmfb_probe(struct device_d *hw_dev)
struct display_timings *modes;
struct device_node *display;
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !hw_dev->device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !hw_dev->of_node)
return -EINVAL;
- display = of_parse_phandle(hw_dev->device_node, "display", 0);
+ display = of_parse_phandle(hw_dev->of_node, "display", 0);
if (!display) {
dev_err(hw_dev, "no display phandle\n");
return -EINVAL;
@@ -581,8 +581,9 @@ static __maybe_unused struct of_device_id stmfb_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, stmfb_compatible);
-static struct driver_d stmfb_driver = {
+static struct driver stmfb_driver = {
.name = "stmfb",
.probe = stmfb_probe,
.of_compatible = DRV_OF_COMPAT(stmfb_compatible),
diff --git a/drivers/video/stm32_ltdc.c b/drivers/video/stm32_ltdc.c
index 9dc35ade61..d1c36b1f45 100644
--- a/drivers/video/stm32_ltdc.c
+++ b/drivers/video/stm32_ltdc.c
@@ -21,7 +21,7 @@
struct ltdc_hw {
void __iomem *regs;
- struct device_d *dev;
+ struct device *dev;
struct clk *pclk;
bool claimed;
};
@@ -252,7 +252,7 @@ static struct fb_ops ltdc_ops = {
.fb_disable = ltdc_disable,
};
-static int ltdc_probe(struct device_d *dev)
+static int ltdc_probe(struct device *dev)
{
struct device_node *np;
struct resource *iores;
@@ -273,7 +273,7 @@ static int ltdc_probe(struct device_d *dev)
return PTR_ERR(hw->pclk);
}
- for_each_available_child_of_node(dev->device_node, np) {
+ for_each_available_child_of_node(dev->of_node, np) {
struct ltdc_fb *priv;
struct of_endpoint ep;
struct fb_info *info;
@@ -285,12 +285,12 @@ static int ltdc_probe(struct device_d *dev)
if (ret)
return ret;
- dev_dbg(hw->dev, "register vpl for %s\n", np->full_name);
+ dev_dbg(hw->dev, "register vpl for %pOF\n", np);
priv = xzalloc(sizeof(*priv));
priv->hw = hw;
priv->id = ep.id;
- priv->vpl.node = dev->device_node;
+ priv->vpl.node = dev->of_node;
ret = vpl_register(&priv->vpl);
if (ret)
@@ -327,8 +327,9 @@ static __maybe_unused struct of_device_id ltdc_ids[] = {
{ .compatible = "st,stm32-ltdc" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, ltdc_ids);
-static struct driver_d ltdc_driver = {
+static struct driver ltdc_driver = {
.name = "stm32-ltdc",
.probe = ltdc_probe,
.of_compatible = DRV_OF_COMPAT(ltdc_ids),
diff --git a/drivers/video/tc358767.c b/drivers/video/tc358767.c
index 36f4125414..da106496fc 100644
--- a/drivers/video/tc358767.c
+++ b/drivers/video/tc358767.c
@@ -204,7 +204,7 @@ struct tc_edp_link {
struct tc_data {
struct i2c_client *client;
- struct device_d *dev;
+ struct device *dev;
/* DP AUX channel */
struct i2c_adapter adapter;
struct vpl vpl;
@@ -880,7 +880,7 @@ err:
static int tc_main_link_setup(struct tc_data *tc)
{
- struct device_d *dev = tc->dev;
+ struct device *dev = tc->dev;
unsigned int rate;
u32 dp_phy_ctrl;
int timeout;
@@ -1334,7 +1334,7 @@ static int tc_ioctl(struct vpl *vpl, unsigned int port,
return ret;
}
-static int tc_probe(struct device_d *dev)
+static int tc_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tc_data *tc;
@@ -1347,16 +1347,16 @@ static int tc_probe(struct device_d *dev)
tc->dev = dev;
/* Shut down GPIO is optional */
- tc->sd_gpio = of_get_named_gpio_flags(dev->device_node,
- "shutdown-gpios", 0, &flags);
+ tc->sd_gpio = of_get_named_gpio_flags(dev->of_node,
+ "shutdown-gpios", 0, &flags);
if (gpio_is_valid(tc->sd_gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
tc->sd_active_high = 1;
}
/* Reset GPIO is optional */
- tc->reset_gpio = of_get_named_gpio_flags(dev->device_node,
- "reset-gpios", 0, &flags);
+ tc->reset_gpio = of_get_named_gpio_flags(dev->of_node,
+ "reset-gpios", 0, &flags);
if (gpio_is_valid(tc->reset_gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
tc->reset_active_high = 1;
@@ -1371,7 +1371,7 @@ static int tc_probe(struct device_d *dev)
gpio_direction_output(tc->sd_gpio, 0);
}
- tc->refclk = of_clk_get_by_name(dev->device_node, "ref");
+ tc->refclk = of_clk_get_by_name(dev->of_node, "ref");
if (IS_ERR(tc->refclk)) {
ret = PTR_ERR(tc->refclk);
dev_err(dev, "Failed to get refclk: %d\n", ret);
@@ -1410,7 +1410,7 @@ static int tc_probe(struct device_d *dev)
}
/* add vlp */
- tc->vpl.node = dev->device_node;
+ tc->vpl.node = dev->of_node;
tc->vpl.ioctl = tc_ioctl;
return vpl_register(&tc->vpl);
@@ -1419,7 +1419,7 @@ err:
return ret;
}
-static struct driver_d tc_driver = {
+static struct driver tc_driver = {
.name = "tc358767",
.probe = tc_probe,
};
diff --git a/drivers/video/vpl.c b/drivers/video/vpl.c
index f94bd4a3e2..f8c2159cd9 100644
--- a/drivers/video/vpl.c
+++ b/drivers/video/vpl.c
@@ -18,7 +18,7 @@ int vpl_register(struct vpl *vpl)
{
list_add_tail(&vpl->list, &vpls);
- pr_debug("%s: %s\n", __func__, vpl->node->full_name);
+ pr_debug("%s: %pOF\n", __func__, vpl->node);
return 0;
}
@@ -40,13 +40,13 @@ struct vpl *of_vpl_get(struct device_node *node, int port)
if (!node)
return NULL;
- pr_debug("%s: port: %s\n", __func__, node->full_name);
+ pr_debug("%s: port: %pOF\n", __func__, node);
node = of_graph_get_remote_port_parent(node);
if (!node)
return NULL;
- pr_debug("%s: remote port parent: %s\n", __func__, node->full_name);
+ pr_debug("%s: remote port parent: %pOF\n", __func__, node);
return of_find_vpl(node);
}
@@ -57,11 +57,11 @@ int vpl_ioctl(struct vpl *vpl, unsigned int port,
struct device_node *node, *endpoint;
int ret;
- pr_debug("%s: %s port %d\n", __func__, vpl->node->full_name, port);
+ pr_debug("%s: %pOF port %d\n", __func__, vpl->node, port);
node = of_graph_get_port_by_id(vpl->node, port);
if (!node) {
- pr_err("%s: no port %d on %s\n", __func__, port, vpl->node->full_name);
+ pr_err("%s: no port %d on %pOF\n", __func__, port, vpl->node);
return -ENODEV;
}
@@ -72,7 +72,7 @@ int vpl_ioctl(struct vpl *vpl, unsigned int port,
remote = of_graph_get_remote_port(endpoint);
if (!remote) {
- pr_debug("%s: no remote for endpoint %s\n", __func__, endpoint->full_name);
+ pr_debug("%s: no remote for endpoint %pOF\n", __func__, endpoint);
continue;
}
@@ -89,10 +89,11 @@ int vpl_ioctl(struct vpl *vpl, unsigned int port,
remote_vpl = of_find_vpl(remote_parent);
if (!remote_vpl) {
- pr_debug("%s: cannot find remote vpl %s\n", __func__, remote->full_name);
+ pr_debug("%s: cannot find remote vpl %pOF\n", __func__, remote);
continue;
}
+ pr_debug("%s: looked up %pOF: %pS\n", __func__, remote, remote_vpl->ioctl);
ret = remote_vpl->ioctl(remote_vpl, remote_port_id, cmd, ptr);
if (ret)
return ret;
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 91a89d3e1b..ecf66987b3 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -28,7 +28,6 @@ config VIRTIO_MMIO
config VIRTIO_PCI
tristate "PCI driver for virtio devices"
depends on PCI && HAS_DMA
- depends on MMU && MIPS || !MIPS
select VIRTIO
help
This driver provides support for virtio based paravirtual device
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index c96c465e87..c4854e7d11 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -47,7 +47,7 @@ static inline int virtio_id_match(const struct virtio_device *dev,
/* This looks through all the IDs a driver claims to support. If any of them
* match, we return 1 and the kernel will call virtio_dev_probe(). */
-static int virtio_dev_match(struct device_d *_dv, struct driver_d *_dr)
+static int virtio_dev_match(struct device *_dv, struct driver *_dr)
{
unsigned int i;
struct virtio_device *dev = dev_to_virtio(_dv);
@@ -166,7 +166,7 @@ int virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
}
EXPORT_SYMBOL_GPL(virtio_find_vqs);
-static int virtio_dev_probe(struct device_d *_d)
+static int virtio_dev_probe(struct device *_d)
{
int err, i;
struct virtio_device *dev = dev_to_virtio(_d);
@@ -241,7 +241,7 @@ err:
}
-static void virtio_dev_remove(struct device_d *_d)
+static void virtio_dev_remove(struct device *_d)
{
struct virtio_device *dev = dev_to_virtio(_d);
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
@@ -289,6 +289,7 @@ int register_virtio_device(struct virtio_device *dev)
dev->dev.bus = &virtio_bus;
dev->dev.id = DEVICE_ID_DYNAMIC;
dev->dev.name = "virtio";
+ dev->dev.device_node = dev_of_node(dev->dev.parent);
spin_lock_init(&dev->config_lock);
dev->config_enabled = false;
@@ -324,7 +325,7 @@ out:
}
EXPORT_SYMBOL_GPL(register_virtio_device);
-bool is_virtio_device(struct device_d *dev)
+bool is_virtio_device(struct device *dev)
{
return dev->bus == &virtio_bus;
}
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 4a689495b8..5542674dd1 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -378,7 +378,7 @@ static const struct virtio_config_ops virtio_mmio_config_ops = {
/* Platform device */
-static int virtio_mmio_probe(struct device_d *dev)
+static int virtio_mmio_probe(struct device *dev)
{
struct virtio_mmio_device *vm_dev;
struct resource *res;
@@ -430,7 +430,7 @@ static int virtio_mmio_probe(struct device_d *dev)
return register_virtio_device(&vm_dev->vdev);
}
-static void virtio_mmio_remove(struct device_d *dev)
+static void virtio_mmio_remove(struct device *dev)
{
struct virtio_mmio_device *vm_dev = dev->priv;
unregister_virtio_device(&vm_dev->vdev);
@@ -443,8 +443,9 @@ static const struct of_device_id virtio_mmio_match[] = {
{ .compatible = "virtio,mmio", },
{},
};
+MODULE_DEVICE_TABLE(of, virtio_mmio_match);
-static struct driver_d virtio_mmio_driver = {
+static struct driver virtio_mmio_driver = {
.probe = virtio_mmio_probe,
.remove = virtio_mmio_remove,
.name = "virtio-mmio",
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index b0ac8befd4..c4644834c7 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -46,7 +46,7 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
rc = virtio_pci_modern_probe(vp_dev);
if (rc == -ENODEV)
- dev_err(&pci_dev->dev, "Legacy and transitional devices unsupported\n");
+ dev_err(&pci_dev->dev, "Legacy devices unsupported\n");
if (rc)
goto err_enable_device;
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index f816baf18e..2dd369b02e 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -223,14 +223,6 @@ error_available:
static void virtio_pci_del_vq(struct virtqueue *vq)
{
- struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
- unsigned int index = vq->index;
-
- iowrite16(index, &vp_dev->common->queue_select);
-
- /* Select and deactivate the queue */
- iowrite16(0, &vp_dev->common->queue_enable);
-
vring_del_virtqueue(vq);
}
@@ -355,19 +347,23 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
{
struct pci_dev *pci_dev = vp_dev->pci_dev;
- struct device_d *dev = &pci_dev->dev;
+ struct device *dev = &pci_dev->dev;
int common, notify, device;
int offset;
- /*
- * We only own devices >= 0x1000 and <= 0x107f. We don't support
- * transitional devices, so start at 0x1040 and leave the rest.
- */
- if (pci_dev->device < 0x1040 || pci_dev->device > 0x107f)
+ /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */
+ if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)
return -ENODEV;
- /* Modern devices: simply use PCI device id, but start from 0x1040. */
- vp_dev->vdev.id.device = pci_dev->device - 0x1040;
+ if (pci_dev->device < 0x1040) {
+ /* Transitional devices: use the PCI subsystem device id as
+ * virtio device id, same as legacy driver always did.
+ */
+ vp_dev->vdev.id.device = pci_dev->subsystem_device;
+ } else {
+ /* Modern devices: simply use PCI device id, but start from 0x1040. */
+ vp_dev->vdev.id.device = pci_dev->device - 0x1040;
+ }
vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
/* Check for a common config: if not, driver could fall back to legacy mode (bar 0) */
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 326fd22d3f..0efe1e0025 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -22,7 +22,7 @@
#define vq_info(vq, fmt, ...) \
dev_info(&vq->vdev->dev, fmt, ##__VA_ARGS__)
-static inline struct device_d *vring_dma_dev(const struct virtqueue *vq)
+static inline struct device *vring_dma_dev(const struct virtqueue *vq)
{
return vq->vdev->dev.parent;
}
@@ -256,8 +256,8 @@ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len)
virtio_store_mb(&vring_used_event(&vq->vring),
cpu_to_virtio16(vq->vdev, vq->last_used_idx));
- return (void *)(uintptr_t)virtio64_to_cpu(vq->vdev,
- vq->vring.desc[i].addr);
+ return IOMEM((uintptr_t)virtio64_to_cpu(vq->vdev,
+ vq->vring.desc[i].addr));
}
static struct virtqueue *__vring_new_virtqueue(unsigned int index,
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index 8f2f772c6e..20f79ae8df 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -39,10 +39,10 @@ static u8 w1_gpio_read_bit(struct w1_bus *bus)
return gpio_get_value(pdata->pin) ? 1 : 0;
}
-static int w1_gpio_probe_dt(struct device_d *dev)
+static int w1_gpio_probe_dt(struct device *dev)
{
struct w1_gpio_platform_data *pdata;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
int gpio;
if (dev->platform_data)
@@ -55,10 +55,8 @@ static int w1_gpio_probe_dt(struct device_d *dev)
gpio = of_get_gpio(np, 0);
if (!gpio_is_valid(gpio)) {
- if (gpio != -EPROBE_DEFER)
- dev_err(dev,
- "Failed to parse gpio property for data pin (%d)\n",
- gpio);
+ gpio = dev_err_probe(dev, gpio < 0 ? gpio : -EINVAL,
+ "parsing gpio property for data pin\n");
goto free_pdata;
}
@@ -79,7 +77,7 @@ free_pdata:
return gpio;
}
-static int __init w1_gpio_probe(struct device_d *dev)
+static int __init w1_gpio_probe(struct device *dev)
{
struct w1_bus *master;
struct w1_gpio_platform_data *pdata;
@@ -150,8 +148,9 @@ static __maybe_unused const struct of_device_id w1_gpio_dt_ids[] = {
{ .compatible = "w1-gpio" },
{}
};
+MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
-static struct driver_d w1_gpio_driver = {
+static struct driver w1_gpio_driver = {
.name = "w1-gpio",
.probe = w1_gpio_probe,
.of_compatible = DRV_OF_COMPAT(w1_gpio_dt_ids),
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 3f21cae5e6..ac5fd38982 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -362,7 +362,7 @@ int w1_reset_select_slave(struct w1_device *dev)
#define to_w1_device(d) container_of(d, struct w1_device, dev)
#define to_w1_driver(d) container_of(d, struct w1_driver, drv)
-static int w1_bus_match(struct device_d *_dev, struct driver_d *_drv)
+static int w1_bus_match(struct device *_dev, struct driver *_drv)
{
struct w1_device *dev = to_w1_device(_dev);
struct w1_driver *drv = to_w1_driver(_drv);
@@ -370,7 +370,7 @@ static int w1_bus_match(struct device_d *_dev, struct driver_d *_drv)
return !(drv->fid == dev->fid);
}
-static int w1_bus_probe(struct device_d *_dev)
+static int w1_bus_probe(struct device *_dev)
{
struct w1_driver *drv = to_w1_driver(_dev->driver);
struct w1_device *dev = to_w1_device(_dev);
@@ -378,7 +378,7 @@ static int w1_bus_probe(struct device_d *_dev)
return drv->probe(dev);
}
-static void w1_bus_remove(struct device_d *_dev)
+static void w1_bus_remove(struct device *_dev)
{
struct w1_driver *drv = to_w1_driver(_dev->driver);
struct w1_device *dev = to_w1_device(_dev);
@@ -396,7 +396,7 @@ struct bus_type w1_bustype= {
static bool w1_is_registered(struct w1_bus *bus, u64 rn)
{
- struct device_d *dev = NULL;
+ struct device *dev = NULL;
struct w1_device *w1_dev;
bus_for_each_device(&w1_bustype, dev) {
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h
index 1e8f6137d4..f268e42545 100644
--- a/drivers/w1/w1.h
+++ b/drivers/w1/w1.h
@@ -19,7 +19,7 @@ struct w1_device {
u8 crc;
struct w1_bus *bus;
- struct device_d dev;
+ struct device dev;
};
struct w1_driver {
@@ -28,7 +28,7 @@ struct w1_driver {
int (*probe) (struct w1_device *dev);
void (*remove) (struct w1_device *dev);
- struct driver_d drv;
+ struct driver drv;
};
int w1_driver_register(struct w1_driver *drv);
@@ -58,8 +58,8 @@ extern struct bus_type w1_bustype;
*/
struct w1_bus
{
- struct device_d dev;
- struct device_d *parent;
+ struct device dev;
+ struct device *parent;
/**
* Sample the line level
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6f209e096e..762e37c9c2 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -31,7 +31,7 @@ config WATCHDOG_AT91SAM9
config WATCHDOG_EFI
bool "Generic EFI Watchdog Driver"
- depends on EFI_BOOTUP
+ depends on EFI_PAYLOAD
help
Add support for the EFI watchdog.
@@ -59,6 +59,12 @@ config WATCHDOG_IMX
help
Add support for watchdog found on Freescale i.MX SoCs.
+config WATCHDOG_IMXULP
+ bool "i.MX ULP watchdog"
+ depends on ARCH_IMX || COMPILE_TEST
+ help
+ Add support for watchdog found on Freescale i.MX SoCs.
+
config WATCHDOG_JZ4740
bool "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_MIPS_XBURST || COMPILE_TEST
@@ -165,4 +171,10 @@ config WDAT_WDT
found on some desktop machines as well. This driver will take
over the native iTCO watchdog driver found on many Intel CPUs.
+config CADENCE_WATCHDOG
+ tristate "Cadence Watchdog Timer"
+ help
+ Say Y here if you want to include support for the watchdog
+ timer in the Xilinx Zynq.
+
endif
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 265ae179f1..2b0da7cea9 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o
obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
obj-$(CONFIG_WATCHDOG_IMX) += imxwd.o
+obj-$(CONFIG_WATCHDOG_IMXULP) += imxulp-wdt.o
obj-$(CONFIG_WATCHDOG_KVX) += kvx_wdt.o
obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
@@ -22,3 +23,4 @@ obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_ITCO_WDT) += itco_wdt.o
obj-$(CONFIG_STARFIVE_WDT) += starfive_wdt.o
obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o
+obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/ar9344_wdt.c b/drivers/watchdog/ar9344_wdt.c
index 3dd3197393..50e83fa685 100644
--- a/drivers/watchdog/ar9344_wdt.c
+++ b/drivers/watchdog/ar9344_wdt.c
@@ -29,7 +29,7 @@
struct ar9344_wd {
struct watchdog wd;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
unsigned int rate;
};
@@ -65,7 +65,7 @@ static void ar9344_watchdog_detect_reset_source(struct ar9344_wd *priv)
/* else keep the default 'unknown' state */
}
-static int ar9344_wdt_probe(struct device_d *dev)
+static int ar9344_wdt_probe(struct device *dev)
{
struct resource *iores;
struct ar9344_wd *priv;
@@ -124,8 +124,9 @@ static __maybe_unused struct of_device_id ar9344_wdt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, ar9344_wdt_dt_ids);
-static struct driver_d ar9344_wdt_driver = {
+static struct driver ar9344_wdt_driver = {
.name = "ar9344-wdt",
.probe = ar9344_wdt_probe,
.of_compatible = DRV_OF_COMPAT(ar9344_wdt_dt_ids),
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 1bad10ee53..46bb986229 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -8,7 +8,7 @@
#include <io.h>
#include <watchdog.h>
#include <linux/clk.h>
-#include <mach/at91_wdt.h>
+#include <mach/at91/at91_wdt.h>
#define MIN_WDT_TIMEOUT 1
#define MAX_WDT_TIMEOUT 16
@@ -54,7 +54,7 @@ static inline bool at91sam9x_wdt_is_disabled(struct at91sam9x_wdt *wdt)
return readl(wdt->base + AT91_WDT_MR) & AT91_WDT_WDDIS;
}
-static int at91sam9x_wdt_probe(struct device_d *dev)
+static int at91sam9x_wdt_probe(struct device *dev)
{
struct at91sam9x_wdt *wdt;
struct resource *iores;
@@ -95,8 +95,9 @@ static const __maybe_unused struct of_device_id at91sam9x_wdt_dt_ids[] = {
{ .compatible = "atmel,sama5d4-wdt", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, at91sam9x_wdt_dt_ids);
-static struct driver_d at91sam9x_wdt_driver = {
+static struct driver at91sam9x_wdt_driver = {
.name = "at91sam9x-wdt",
.of_compatible = DRV_OF_COMPAT(at91sam9x_wdt_dt_ids),
.probe = at91sam9x_wdt_probe,
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index c6e6a289f2..874315d502 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -21,7 +21,7 @@
struct bcm2835_wd {
struct watchdog wd;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
struct restart_handler restart;
};
@@ -60,7 +60,7 @@ static int bcm2835_wd_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int bcm2835_wd_probe(struct device_d *dev)
+static int bcm2835_wd_probe(struct device *dev)
{
struct resource *iores;
struct bcm2835_wd *priv;
@@ -101,8 +101,9 @@ static __maybe_unused struct of_device_id bcm2835_wd_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, bcm2835_wd_dt_ids);
-static struct driver_d bcm2835_wd_driver = {
+static struct driver bcm2835_wd_driver = {
.name = "bcm2835_wd",
.of_compatible = DRV_OF_COMPAT(bcm2835_wd_dt_ids),
.probe = bcm2835_wd_probe,
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c
new file mode 100644
index 0000000000..17655a188c
--- /dev/null
+++ b/drivers/watchdog/cadence_wdt.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cadence WDT driver - Used by Xilinx Zynq
+ *
+ * Copyright (C) 2010 - 2014 Xilinx, Inc.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <restart.h>
+#include <watchdog.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/reset.h>
+
+/* Supports 1 - 516 sec */
+#define CDNS_WDT_MAX_TIMEOUT 516
+
+/* Restart key */
+#define CDNS_WDT_RESTART_KEY 0x00001999
+
+/* Counter register access key */
+#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000
+
+/* Counter value divisor */
+#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000
+
+/* Clock prescaler value and selection */
+#define CDNS_WDT_PRESCALE_64 64
+#define CDNS_WDT_PRESCALE_512 512
+#define CDNS_WDT_PRESCALE_4096 4096
+#define CDNS_WDT_PRESCALE_SELECT_64 1
+#define CDNS_WDT_PRESCALE_SELECT_512 2
+#define CDNS_WDT_PRESCALE_SELECT_4096 3
+
+/* Input clock frequency */
+#define CDNS_WDT_CLK_10MHZ 10000000
+#define CDNS_WDT_CLK_75MHZ 75000000
+
+/* Counter maximum value */
+#define CDNS_WDT_COUNTER_MAX 0xFFF
+
+/**
+ * struct cdns_wdt - Watchdog device structure
+ * @regs: baseaddress of device
+ * @clk: struct clk * of a clock source
+ * @prescaler: for saving prescaler value
+ * @ctrl_clksel: counter clock prescaler selection
+ * @cdns_wdt_device: watchdog device structure
+ *
+ * Structure containing parameters specific to cadence watchdog.
+ */
+struct cdns_wdt {
+ void __iomem *regs;
+ struct clk *clk;
+ u32 prescaler;
+ u32 ctrl_clksel;
+ struct watchdog cdns_wdt_device;
+ unsigned timeout;
+};
+
+static inline struct cdns_wdt *to_cdns_wdt(struct watchdog *wdd)
+{
+ return container_of(wdd, struct cdns_wdt, cdns_wdt_device);
+}
+
+/* Write access to Registers */
+static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val)
+{
+ writel_relaxed(val, wdt->regs + offset);
+}
+
+/*************************Register Map**************************************/
+
+/* Register Offsets for the WDT */
+#define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */
+#define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */
+#define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */
+#define CDNS_WDT_SR_OFFSET 0xC /* Status Register */
+
+/*
+ * Zero Mode Register - This register controls how the time out is indicated
+ * and also contains the access code to allow writes to the register (0xABC).
+ */
+#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */
+#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */
+#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */
+#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */
+#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */
+/*
+ * Counter Control register - This register controls how fast the timer runs
+ * and the reset value and also contains the access code to allow writes to
+ * the register.
+ */
+#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */
+
+/**
+ * cdns_wdt_stop - Stop the watchdog.
+ *
+ * @wdt: cadence watchdog device
+ *
+ * Read the contents of the ZMR register, clear the WDEN bit
+ * in the register and set the access key for successful write.
+ */
+static void cdns_wdt_stop(struct cdns_wdt *wdt)
+{
+ cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET,
+ CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK));
+}
+
+/**
+ * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog).
+ *
+ * @wdt: cadence watchdog device
+ *
+ * Write the restart key value (0x00001999) to the restart register.
+ */
+static void cdns_wdt_reload(struct cdns_wdt *wdt)
+{
+ cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET,
+ CDNS_WDT_RESTART_KEY);
+}
+
+/**
+ * cdns_wdt_start - Enable and start the watchdog.
+ *
+ * @wdt: cadence watchdog device
+ * @timeout: new timeout
+ *
+ * The counter value is calculated according to the formula:
+ * calculated count = (timeout * clock) / prescaler + 1.
+ * The calculated count is divided by 0x1000 to obtain the field value
+ * to write to counter control register.
+ * Clears the contents of prescaler and counter reset value. Sets the
+ * prescaler to 4096 and the calculated count and access key
+ * to write to CCR Register.
+ * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit)
+ * or Interrupt signal(IRQEN) with a specified cycles and the access
+ * key to write to ZMR Register.
+ */
+static void cdns_wdt_start(struct cdns_wdt *wdt, unsigned timeout)
+{
+ unsigned int data = 0;
+ unsigned short count;
+ unsigned long clock_f = clk_get_rate(wdt->clk);
+
+ /*
+ * Counter value divisor to obtain the value of
+ * counter reset to be written to control register.
+ */
+ count = (timeout * (clock_f / wdt->prescaler)) /
+ CDNS_WDT_COUNTER_VALUE_DIVISOR + 1;
+
+ if (count > CDNS_WDT_COUNTER_MAX)
+ count = CDNS_WDT_COUNTER_MAX;
+
+ cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET,
+ CDNS_WDT_ZMR_ZKEY_VAL);
+
+ count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
+
+ /* Write counter access key first to be able write to register */
+ data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel;
+ cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data);
+ data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 |
+ CDNS_WDT_ZMR_ZKEY_VAL;
+
+ /* Reset on timeout regardless of what's specified in device tree. */
+ data |= CDNS_WDT_ZMR_RSTEN_MASK;
+ data &= ~CDNS_WDT_ZMR_IRQEN_MASK;
+
+ cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data);
+ cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET,
+ CDNS_WDT_RESTART_KEY);
+}
+
+/**
+ * cdns_wdt_settimeout - Set a new timeout value for the watchdog device.
+ *
+ * @wdd: watchdog device
+ * @new_time: new timeout value that needs to be set
+ * Return: 0 on success
+ *
+ * Update the watchdog device timeout with new value which is used when
+ * cdns_wdt_start is called.
+ */
+static int cdns_wdt_settimeout(struct watchdog *wdd,
+ unsigned int new_time)
+{
+ struct cdns_wdt *wdt = to_cdns_wdt(wdd);
+
+ if (new_time > wdd->timeout_max)
+ return -EINVAL;
+
+ if (new_time == 0) {
+ cdns_wdt_stop(wdt);
+ } else if (wdt->timeout != new_time) {
+ cdns_wdt_start(wdt, new_time);
+ wdt->timeout = new_time;
+ } else {
+ cdns_wdt_reload(wdt);
+ }
+
+ return 0;
+}
+
+/************************Platform Operations*****************************/
+/**
+ * cdns_wdt_probe - Probe call for the device.
+ *
+ * @pdev: handle to the platform device structure.
+ * Return: 0 on success, negative error otherwise.
+ *
+ * It does all the memory allocation and registration for the device.
+ */
+static int cdns_wdt_probe(struct device *dev)
+{
+ unsigned long clock_f;
+ struct cdns_wdt *wdt;
+ struct resource *res;
+ struct watchdog *cdns_wdt_device;
+
+ wdt = xzalloc(sizeof(*wdt));
+
+ cdns_wdt_device = &wdt->cdns_wdt_device;
+ cdns_wdt_device->name = "cdns_wdt";
+ cdns_wdt_device->hwdev = dev;
+ cdns_wdt_device->set_timeout = cdns_wdt_settimeout;
+ cdns_wdt_device->timeout_max = CDNS_WDT_MAX_TIMEOUT;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ wdt->regs = IOMEM(res->start);
+
+ /* We don't service interrupts in barebox, so a watchdog that doesn't
+ * reset the system isn't a useful thing to register
+ */
+ if (!of_property_read_bool(dev->of_node, "reset-on-timeout"))
+ dev_notice(dev, "proceeding as if reset-on-timeout was set\n");
+
+ wdt->clk = clk_get_enabled(dev, NULL);
+ if (IS_ERR(wdt->clk))
+ return dev_err_probe(dev, PTR_ERR(wdt->clk),
+ "input clock not found\n");
+
+ clock_f = clk_get_rate(wdt->clk);
+ if (clock_f <= CDNS_WDT_CLK_75MHZ) {
+ wdt->prescaler = CDNS_WDT_PRESCALE_512;
+ wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512;
+ } else {
+ wdt->prescaler = CDNS_WDT_PRESCALE_4096;
+ wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096;
+ }
+
+ return watchdog_register(cdns_wdt_device);
+}
+
+static const struct of_device_id cdns_wdt_of_match[] = {
+ { .compatible = "cdns,wdt-r1p2", },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, cdns_wdt_of_match);
+
+static struct driver cdns_wdt_driver = {
+ .name = "cdns-wdt",
+ .probe = cdns_wdt_probe,
+ .of_match_table = cdns_wdt_of_match,
+};
+device_platform_driver(cdns_wdt_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Watchdog driver for Cadence WDT");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index d632573ba7..0b2df50c48 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -127,7 +127,7 @@ static int davinci_wdt_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int davinci_wdt_probe(struct device_d *dev)
+static int davinci_wdt_probe(struct device *dev)
{
struct resource *iores;
int ret = 0;
@@ -160,8 +160,9 @@ static __maybe_unused struct of_device_id davinci_wdt_of_match[] = {
{ .compatible = "ti,davinci-wdt", },
{},
};
+MODULE_DEVICE_TABLE(of, davinci_wdt_of_match);
-static struct driver_d platform_wdt_driver = {
+static struct driver platform_wdt_driver = {
.name = "davinci-wdt",
.of_compatible = DRV_OF_COMPAT(davinci_wdt_of_match),
.probe = davinci_wdt_probe,
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index ad95b4a57e..178e0a29f1 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -128,7 +128,7 @@ static void __noreturn dw_wdt_restart_handle(struct restart_handler *rst)
hang();
}
-static int dw_wdt_drv_probe(struct device_d *dev)
+static int dw_wdt_drv_probe(struct device *dev)
{
struct watchdog *wdd;
struct dw_wdt *dw_wdt;
@@ -196,8 +196,9 @@ static struct of_device_id dw_wdt_of_match[] = {
{ .compatible = "snps,dw-wdt", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
-static struct driver_d dw_wdt_driver = {
+static struct driver dw_wdt_driver = {
.name = "dw-wdt",
.probe = dw_wdt_drv_probe,
.of_compatible = DRV_OF_COMPAT(dw_wdt_of_match),
diff --git a/drivers/watchdog/efi_wdt.c b/drivers/watchdog/efi_wdt.c
index eb50f24aed..072240fcaf 100644
--- a/drivers/watchdog/efi_wdt.c
+++ b/drivers/watchdog/efi_wdt.c
@@ -12,7 +12,7 @@
struct efi_wdt_priv {
struct watchdog wd;
- struct device_d *dev;
+ struct device *dev;
};
#define to_efi_wdt(h) container_of(h, struct efi_wdt_priv, wd)
@@ -31,7 +31,7 @@ static int efi_wdt_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int efi_wdt_probe(struct device_d *dev)
+static int efi_wdt_probe(struct device *dev)
{
struct efi_wdt_priv *priv;
int ret;
@@ -58,7 +58,7 @@ on_error:
return ret;
}
-static struct driver_d efi_wdt_driver = {
+static struct driver efi_wdt_driver = {
.name = "efi-wdt",
.probe = efi_wdt_probe,
};
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index b2cf0f8ddd..5bee066366 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -239,7 +239,7 @@ static int f71808e_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeou
return 0;
}
-static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev)
+static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device *dev)
{
struct watchdog *wdd = &wd->wdd;
const char * const *names = pulse_width_names;
@@ -376,7 +376,7 @@ static struct platform_device_id f71808e_wdt_ids[] = {
{ /* sentinel */ },
};
-static int f71808e_probe(struct device_d *dev)
+static int f71808e_probe(struct device *dev)
{
struct f71808e_wdt *wd;
struct resource *res;
@@ -396,7 +396,7 @@ static int f71808e_probe(struct device_d *dev)
return f71808e_wdt_init(wd, dev);
}
-static struct driver_d f71808e_wdt_driver = {
+static struct driver f71808e_wdt_driver = {
.probe = f71808e_probe,
.name = "f71808e_wdt",
.id_table = f71808e_wdt_ids,
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 4de49dcee3..5fa98f87c6 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -9,7 +9,7 @@
#include <driver.h>
#include <watchdog.h>
#include <superio.h>
-#include <gpiod.h>
+#include <linux/gpio/consumer.h>
enum {
HW_ALGO_TOGGLE,
@@ -17,7 +17,7 @@ enum {
};
struct gpio_wdt_priv {
- int gpio;
+ struct gpio_desc *gpiod;
bool state;
bool started;
unsigned int hw_algo;
@@ -32,11 +32,11 @@ static inline struct gpio_wdt_priv *to_gpio_wdt_priv(struct watchdog *wdd)
static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
{
/* Eternal ping */
- gpio_set_active(priv->gpio, 1);
+ gpiod_set_value(priv->gpiod, 1);
/* Put GPIO back to tristate */
if (priv->hw_algo == HW_ALGO_TOGGLE)
- gpio_direction_input(priv->gpio);
+ gpiod_direction_input(priv->gpiod);
priv->started = false;
}
@@ -47,13 +47,13 @@ static void gpio_wdt_ping(struct gpio_wdt_priv *priv)
case HW_ALGO_TOGGLE:
/* Toggle output pin */
priv->state = !priv->state;
- gpio_set_active(priv->gpio, priv->state);
+ gpiod_set_value(priv->gpiod, priv->state);
break;
case HW_ALGO_LEVEL:
/* Pulse */
- gpio_set_active(priv->gpio, true);
+ gpiod_set_value(priv->gpiod, true);
udelay(1);
- gpio_set_active(priv->gpio, false);
+ gpiod_set_value(priv->gpiod, false);
break;
}
}
@@ -61,7 +61,7 @@ static void gpio_wdt_ping(struct gpio_wdt_priv *priv)
static void gpio_wdt_start(struct gpio_wdt_priv *priv)
{
priv->state = false;
- gpio_direction_active(priv->gpio, priv->state);
+ gpiod_direction_output(priv->gpiod, priv->state);
priv->started = true;
}
@@ -81,9 +81,9 @@ static int gpio_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeout)
return 0;
}
-static int gpio_wdt_probe(struct device_d *dev)
+static int gpio_wdt_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct gpio_wdt_priv *priv;
enum gpiod_flags gflags;
unsigned int hw_margin;
@@ -113,9 +113,9 @@ static int gpio_wdt_probe(struct device_d *dev)
return -EINVAL;
}
- priv->gpio = gpiod_get(dev, NULL, gflags);
- if (priv->gpio < 0)
- return priv->gpio;
+ priv->gpiod = gpiod_get(dev, NULL, gflags);
+ if (IS_ERR(priv->gpiod))
+ return PTR_ERR(priv->gpiod);
priv->wdd.hwdev = dev;
priv->wdd.timeout_max = hw_margin / 1000;
@@ -129,8 +129,9 @@ static const struct of_device_id gpio_wdt_dt_ids[] = {
{ .compatible = "linux,wdt-gpio", },
{ }
};
+MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids);
-static struct driver_d gpio_wdt_driver = {
+static struct driver gpio_wdt_driver = {
.name = "gpio-wdt",
.of_compatible = gpio_wdt_dt_ids,
.probe = gpio_wdt_probe,
diff --git a/drivers/watchdog/im28wd.c b/drivers/watchdog/im28wd.c
index efe26f42d8..b52e585175 100644
--- a/drivers/watchdog/im28wd.c
+++ b/drivers/watchdog/im28wd.c
@@ -175,7 +175,7 @@ static void __maybe_unused imx28_detect_reset_source(const struct imx28_wd *p)
reset_source_set(RESET_RST);
}
-static int imx28_wd_probe(struct device_d *dev)
+static int imx28_wd_probe(struct device *dev)
{
struct resource *iores;
struct imx28_wd *priv;
@@ -187,7 +187,7 @@ static int imx28_wd_probe(struct device_d *dev)
return PTR_ERR(iores);
priv->regs = IOMEM(iores->start);
priv->wd.set_timeout = imx28_watchdog_set_timeout;
- priv->wd.timeout_max = ULONG_MAX / WDOG_TICK_RATE;
+ priv->wd.timeout_max = U32_MAX / WDOG_TICK_RATE;
priv->wd.hwdev = dev;
if (!(readl(priv->regs + MXS_RTC_STAT) & MXS_RTC_STAT_WD_PRESENT)) {
@@ -213,7 +213,7 @@ on_error:
return rc;
}
-static void imx28_wd_remove(struct device_d *dev)
+static void imx28_wd_remove(struct device *dev)
{
struct imx28_wd *priv= dev->priv;
watchdog_deregister(&priv->wd);
@@ -227,8 +227,9 @@ static __maybe_unused struct of_device_id imx28_wdt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx28_wdt_dt_ids);
-static struct driver_d imx28_wd_driver = {
+static struct driver imx28_wd_driver = {
.name = "im28wd",
.probe = imx28_wd_probe,
.remove = imx28_wd_remove,
diff --git a/drivers/watchdog/imxulp-wdt.c b/drivers/watchdog/imxulp-wdt.c
new file mode 100644
index 0000000000..5a89876175
--- /dev/null
+++ b/drivers/watchdog/imxulp-wdt.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <errno.h>
+#include <malloc.h>
+#include <restart.h>
+#include <watchdog.h>
+#include <reset_source.h>
+#include <linux/clk.h>
+#include <asm/system.h>
+
+struct imxulp_socdata {
+ bool prescaler_enable;
+ unsigned int rate;
+};
+
+struct imxulp_wd {
+ struct watchdog wd;
+ void __iomem *base;
+ bool prescaler_enable;
+ struct device *dev;
+ const struct imxulp_socdata *socdata;
+};
+
+#define REFRESH_WORD0 0xA602 /* 1st refresh word */
+#define REFRESH_WORD1 0xB480 /* 2nd refresh word */
+
+#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
+#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */
+
+#define UNLOCK_WORD 0xD928C520 /* unlock word */
+#define REFRESH_WORD 0xB480A602 /* refresh word */
+
+#define WDOG_CS 0x0
+#define WDOG_CS_UPDATE BIT(5)
+#define WDOG_CS_EN BIT(7)
+#define WDOG_CS_RCS BIT(10)
+#define WDOG_CS_ULK BIT(11)
+#define WDOG_CS_PRES BIT(12)
+#define WDOG_CS_CMD32EN BIT(13)
+#define WDOG_CS_FLG BIT(14)
+#define WDOG_CS_INT BIT(6)
+#define WDOG_CS_LPO_CLK (0x1 << 8)
+
+#define WDOG_CNT 0x4
+#define WDOG_TOVAL 0x8
+
+#define CLK_RATE_1KHZ 1000
+#define CLK_RATE_32KHZ 128
+
+static int imxulp_watchdog_set_timeout(struct watchdog *wd, unsigned int timeout)
+{
+ struct imxulp_wd *imxwd = container_of(wd, struct imxulp_wd, wd);
+ u32 cmd32 = 0;
+
+ if (timeout == 0)
+ return -ENOSYS;
+
+ if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
+ writel(UNLOCK_WORD, imxwd->base + WDOG_CNT);
+ cmd32 = WDOG_CS_CMD32EN;
+ } else {
+ writel(UNLOCK_WORD0, imxwd->base + WDOG_CNT);
+ writel(UNLOCK_WORD1, imxwd->base + WDOG_CNT);
+ }
+
+ /* Wait WDOG Unlock */
+ while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_ULK))
+ ;
+
+ writel(timeout * imxwd->socdata->rate, imxwd->base + WDOG_TOVAL);
+
+ if (imxwd->socdata->prescaler_enable)
+ cmd32 |= WDOG_CS_PRES;
+
+ writel(cmd32 | WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_LPO_CLK |
+ WDOG_CS_FLG | WDOG_CS_INT, imxwd->base + WDOG_CS);
+
+ /* Wait WDOG reconfiguration */
+ while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_RCS))
+ ;
+
+ if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
+ writel(REFRESH_WORD, imxwd->base + WDOG_CNT);
+ } else {
+ writel(REFRESH_WORD0, imxwd->base + WDOG_CNT);
+ writel(REFRESH_WORD1, imxwd->base + WDOG_CNT);
+ }
+
+ return 0;
+}
+
+static enum wdog_hw_runnning imxulp_wd_running(struct imxulp_wd *imxwd)
+{
+ if (readl(imxwd->base + WDOG_CS) & WDOG_CS_EN)
+ return WDOG_HW_RUNNING;
+ else
+ return WDOG_HW_NOT_RUNNING;
+}
+
+static int imxulp_wd_probe(struct device *dev)
+{
+ struct imxulp_wd *imxwd;
+ struct resource *iores;
+ struct imxulp_socdata *socdata;
+ int ret;
+
+ ret = dev_get_drvdata(dev, (const void **)&socdata);
+ if (ret)
+ return ret;
+
+ imxwd = xzalloc(sizeof(*imxwd));
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return dev_err_probe(dev, PTR_ERR(iores), "could not get memory region\n");
+
+ imxwd->socdata = socdata;
+ imxwd->base = IOMEM(iores->start);
+ imxwd->wd.set_timeout = imxulp_watchdog_set_timeout;
+ imxwd->wd.timeout_max = 0xffff / imxwd->socdata->rate;
+ imxwd->wd.hwdev = dev;
+ imxwd->wd.running = imxulp_wd_running(imxwd);
+
+ ret = watchdog_register(&imxwd->wd);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register watchdog device\n");
+
+ return 0;
+}
+
+static struct imxulp_socdata imx7ulp_wd_socdata = {
+ .prescaler_enable = false,
+ .rate = CLK_RATE_1KHZ,
+};
+
+static struct imxulp_socdata imx93_wd_socdata = {
+ .prescaler_enable = true,
+ .rate = CLK_RATE_32KHZ,
+};
+
+static __maybe_unused struct of_device_id imxulp_wdt_dt_ids[] = {
+ {
+ .compatible = "fsl,imx7ulp-wdt",
+ .data = &imx7ulp_wd_socdata,
+ }, {
+ .compatible = "fsl,imx8ulp-wdt",
+ .data = &imx7ulp_wd_socdata,
+ }, {
+ .compatible = "fsl,imx93-wdt",
+ .data = &imx93_wd_socdata,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);
+
+static struct driver imxulp_wd_driver = {
+ .name = "imxulp-watchdog",
+ .probe = imxulp_wd_probe,
+ .of_compatible = DRV_OF_COMPAT(imxulp_wdt_dt_ids),
+};
+device_platform_driver(imxulp_wd_driver);
diff --git a/drivers/watchdog/imxwd.c b/drivers/watchdog/imxwd.c
index dba92cb46a..8f4de5a994 100644
--- a/drivers/watchdog/imxwd.c
+++ b/drivers/watchdog/imxwd.c
@@ -27,7 +27,7 @@ struct imx_wd_ops {
struct imx_wd {
struct watchdog wd;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
const struct imx_wd_ops *ops;
struct restart_handler restart;
struct restart_handler restart_warm;
@@ -213,7 +213,7 @@ static void imx_watchdog_detect_reset_source(struct imx_wd *priv)
/* else keep the default 'unknown' state */
}
-static int imx21_wd_init(struct imx_wd *priv)
+static int imx21_wd_init_no_warm_reset(struct imx_wd *priv)
{
imx_watchdog_detect_reset_source(priv);
@@ -225,7 +225,19 @@ static int imx21_wd_init(struct imx_wd *priv)
return 0;
}
-static int imx_wd_probe(struct device_d *dev)
+static int imx21_wd_init(struct imx_wd *priv)
+{
+ priv->restart_warm.name = "imxwd-warm";
+ priv->restart_warm.restart = imxwd_force_soc_reset_internal;
+ priv->restart_warm.priority = RESTART_DEFAULT_PRIORITY - 10;
+ priv->restart_warm.flags = RESTART_FLAG_WARM_BOOTROM;
+
+ restart_handler_register(&priv->restart_warm);
+
+ return imx21_wd_init_no_warm_reset(priv);
+}
+
+static int imx_wd_probe(struct device *dev)
{
struct resource *iores;
struct imx_wd *priv;
@@ -257,9 +269,9 @@ static int imx_wd_probe(struct device_d *dev)
priv->wd.timeout_max = priv->ops->timeout_max;
priv->wd.hwdev = dev;
priv->dev = dev;
- priv->bigendian = of_device_is_big_endian(dev->device_node);
+ priv->bigendian = of_device_is_big_endian(dev->of_node);
- priv->ext_reset = of_property_read_bool(dev->device_node,
+ priv->ext_reset = of_property_read_bool(dev->of_node,
"fsl,ext-reset-output");
if (IS_ENABLED(CONFIG_WATCHDOG_IMX)) {
@@ -294,12 +306,6 @@ static int imx_wd_probe(struct device_d *dev)
restart_handler_register(&priv->restart);
- priv->restart_warm.name = "imxwd-warm";
- priv->restart_warm.restart = imxwd_force_soc_reset_internal;
- priv->restart_warm.priority = RESTART_DEFAULT_PRIORITY - 10;
-
- restart_handler_register(&priv->restart_warm);
-
return 0;
error_unregister:
@@ -310,6 +316,14 @@ on_error:
return ret;
}
+static const struct imx_wd_ops imx7d_wd_ops = {
+ .set_timeout = imx21_watchdog_set_timeout,
+ .soc_reset = imx21_soc_reset,
+ .init = imx21_wd_init_no_warm_reset,
+ .is_running = imx21_watchdog_is_running,
+ .timeout_max = 128,
+};
+
static const struct imx_wd_ops imx21_wd_ops = {
.set_timeout = imx21_watchdog_set_timeout,
.soc_reset = imx21_soc_reset,
@@ -332,9 +346,13 @@ static __maybe_unused struct of_device_id imx_wdt_dt_ids[] = {
.compatible = "fsl,imx21-wdt",
.data = &imx21_wd_ops,
}, {
+ .compatible = "fsl,imx7d-wdt",
+ .data = &imx7d_wd_ops,
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);
static struct platform_device_id imx_wdt_ids[] = {
{
@@ -348,7 +366,7 @@ static struct platform_device_id imx_wdt_ids[] = {
},
};
-static struct driver_d imx_wd_driver = {
+static struct driver imx_wd_driver = {
.name = "imx-watchdog",
.probe = imx_wd_probe,
.of_compatible = DRV_OF_COMPAT(imx_wdt_dt_ids),
diff --git a/drivers/watchdog/jz4740.c b/drivers/watchdog/jz4740.c
index ed38ba7bd8..8b4b985cd4 100644
--- a/drivers/watchdog/jz4740.c
+++ b/drivers/watchdog/jz4740.c
@@ -60,7 +60,7 @@ static void __noreturn jz4740_reset_soc(struct restart_handler *rst)
hang();
}
-static int jz4740_wdt_probe(struct device_d *dev)
+static int jz4740_wdt_probe(struct device *dev)
{
struct resource *iores;
struct jz4740_wdt_drvdata *priv;
@@ -89,8 +89,9 @@ static __maybe_unused struct of_device_id jz4740_wdt_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, jz4740_wdt_dt_ids);
-static struct driver_d jz4740_wdt_driver = {
+static struct driver jz4740_wdt_driver = {
.name = "jz4740-wdt",
.probe = jz4740_wdt_probe,
.of_compatible = DRV_OF_COMPAT(jz4740_wdt_dt_ids),
diff --git a/drivers/watchdog/kvx_wdt.c b/drivers/watchdog/kvx_wdt.c
index df9430769b..be6b08b71c 100644
--- a/drivers/watchdog/kvx_wdt.c
+++ b/drivers/watchdog/kvx_wdt.c
@@ -48,7 +48,7 @@ static int kvx_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
static int count;
-static int kvx_wdt_drv_probe(struct device_d *dev)
+static int kvx_wdt_drv_probe(struct device *dev)
{
struct watchdog *wdd;
struct clk *clk;
@@ -83,8 +83,9 @@ static struct of_device_id kvx_wdt_of_match[] = {
{ .compatible = "kalray,kvx-core-watchdog", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, kvx_wdt_of_match);
-static struct driver_d kvx_wdt_driver = {
+static struct driver kvx_wdt_driver = {
.name = "kvx-wdt",
.probe = kvx_wdt_drv_probe,
.of_compatible = DRV_OF_COMPAT(kvx_wdt_of_match),
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index b61d77aaee..0ebc1172aa 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -150,7 +150,7 @@ static int omap_wdt_set_timeout(struct watchdog *wdog,
return 0;
}
-static int omap_wdt_probe(struct device_d *dev)
+static int omap_wdt_probe(struct device *dev)
{
struct resource *iores;
struct omap_wdt_dev *wdev;
@@ -191,8 +191,9 @@ static const struct of_device_id omap_wdt_of_match[] = {
{ .compatible = "ti,omap3-wdt", },
{},
};
+MODULE_DEVICE_TABLE(of, omap_wdt_of_match);
-static struct driver_d omap_wdt_driver = {
+static struct driver omap_wdt_driver = {
.probe = omap_wdt_probe,
.name = "omap_wdt",
.of_compatible = DRV_OF_COMPAT(omap_wdt_of_match),
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 6b7614ef7c..227f8b7bb1 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -68,7 +68,7 @@ static int armada_xp_set_timeout(struct watchdog *wd, unsigned timeout)
return 0;
}
-static int orion_wdt_probe(struct device_d *dev)
+static int orion_wdt_probe(struct device *dev)
{
struct orion_wdt_ddata* ddata;
struct resource *res_timer, *res_rstout;
@@ -104,8 +104,9 @@ static const struct of_device_id orion_wdt_of_match[] = {
.compatible = "marvell,armada-xp-wdt",
}, { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match);
-static struct driver_d orion_wdt_driver = {
+static struct driver orion_wdt_driver = {
.probe = orion_wdt_probe,
.name = "orion_wdt",
.of_compatible = DRV_OF_COMPAT(orion_wdt_of_match),
diff --git a/drivers/watchdog/rave-sp-wdt.c b/drivers/watchdog/rave-sp-wdt.c
index 26540395cb..b4fc18cb8b 100644
--- a/drivers/watchdog/rave-sp-wdt.c
+++ b/drivers/watchdog/rave-sp-wdt.c
@@ -252,6 +252,7 @@ static const struct of_device_id rave_sp_wdt_of_match[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match);
static int rave_sp_wdt_set_boot_source(struct param_d *param, void *priv)
{
@@ -286,7 +287,7 @@ static int rave_sp_wdt_get_boot_source(struct param_d *param, void *priv)
static int rave_sp_wdt_add_params(struct rave_sp_wdt *sp_wd)
{
struct watchdog *wdd = &sp_wd->wdd;
- struct device_node *np = wdd->hwdev->device_node;
+ struct device_node *np = wdd->hwdev->of_node;
struct param_d *p;
sp_wd->boot_source_cell = of_nvmem_cell_get(np, "boot-source");
@@ -302,7 +303,7 @@ static int rave_sp_wdt_add_params(struct rave_sp_wdt *sp_wd)
return PTR_ERR_OR_ZERO(p);
}
-static int rave_sp_wdt_probe(struct device_d *dev)
+static int rave_sp_wdt_probe(struct device *dev)
{
struct rave_sp_wdt *sp_wd;
const char *reset_reason;
@@ -415,7 +416,7 @@ static int rave_sp_wdt_probe(struct device_d *dev)
return 0;
}
-static struct driver_d rave_sp_wdt_driver = {
+static struct driver rave_sp_wdt_driver = {
.name = "rave-sp-wdt",
.probe = rave_sp_wdt_probe,
.of_compatible = DRV_OF_COMPAT(rave_sp_wdt_of_match),
diff --git a/drivers/watchdog/rn5t568_wdt.c b/drivers/watchdog/rn5t568_wdt.c
index a6443609e2..2011e3e01c 100644
--- a/drivers/watchdog/rn5t568_wdt.c
+++ b/drivers/watchdog/rn5t568_wdt.c
@@ -8,7 +8,7 @@
#include <common.h>
#include <init.h>
#include <watchdog.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include <of.h>
#define RN5T568_WATCHDOG 0x0b
@@ -107,7 +107,7 @@ static int rn5t568_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
return ret;
}
-static int rn5t568_wdt_probe(struct device_d *dev)
+static int rn5t568_wdt_probe(struct device *dev)
{
struct rn5t568_wdt *wdt;
struct watchdog *wdd;
@@ -137,8 +137,9 @@ static __maybe_unused const struct of_device_id rn5t568_wdt_of_match[] = {
{ .compatible = "ricoh,rn5t568-wdt" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, rn5t568_wdt_of_match);
-static struct driver_d rn5t568_wdt_driver = {
+static struct driver rn5t568_wdt_driver = {
.name = "rn5t568-wdt",
.probe = rn5t568_wdt_probe,
.of_compatible = DRV_OF_COMPAT(rn5t568_wdt_of_match),
diff --git a/drivers/watchdog/starfive_wdt.c b/drivers/watchdog/starfive_wdt.c
index 6779566fd6..90f1e0ae6b 100644
--- a/drivers/watchdog/starfive_wdt.c
+++ b/drivers/watchdog/starfive_wdt.c
@@ -44,7 +44,7 @@ static int starfive_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
return 0;
}
-static int starfive_wdt_drv_probe(struct device_d *dev)
+static int starfive_wdt_drv_probe(struct device *dev)
{
struct starfive_wdt *wd;
struct resource *iores;
@@ -97,8 +97,9 @@ static struct of_device_id starfive_wdt_of_match[] = {
{ .compatible = "starfive,wdt", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, starfive_wdt_of_match);
-static struct driver_d starfive_wdt_driver = {
+static struct driver starfive_wdt_driver = {
.name = "starfive-wdt",
.probe = starfive_wdt_drv_probe,
.of_compatible = starfive_wdt_of_match,
diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c
index 4d7a263b7e..6ac9e7d56e 100644
--- a/drivers/watchdog/stm32_iwdg.c
+++ b/drivers/watchdog/stm32_iwdg.c
@@ -125,8 +125,9 @@ static const struct of_device_id stm32_iwdg_of_match[] = {
{ .compatible = "st,stm32mp1-iwdg", .data = &stm32mp1_iwdg_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
-static int stm32_iwdg_probe(struct device_d *dev)
+static int stm32_iwdg_probe(struct device *dev)
{
struct stm32_iwdg_data *data;
struct stm32_iwdg *wd;
@@ -187,7 +188,7 @@ static int stm32_iwdg_probe(struct device_d *dev)
return 0;
}
-static struct driver_d stm32_iwdg_driver = {
+static struct driver stm32_iwdg_driver = {
.name = "stm32-iwdg",
.probe = stm32_iwdg_probe,
.of_compatible = DRV_OF_COMPAT(stm32_iwdg_of_match),
diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
index 105ba39fb7..4a0519690a 100644
--- a/drivers/watchdog/stpmic1_wdt.c
+++ b/drivers/watchdog/stpmic1_wdt.c
@@ -11,6 +11,7 @@
#include <linux/iopoll.h>
#include <poweroff.h>
#include <mfd/syscon.h>
+#include <linux/regmap.h>
#include <restart.h>
#include <reset_source.h>
@@ -157,7 +158,7 @@ static int stpmic1_set_reset_reason(struct regmap *map)
return 0;
}
-static int stpmic1_wdt_probe(struct device_d *dev)
+static int stpmic1_wdt_probe(struct device *dev)
{
struct stpmic1_wdt *wdt;
struct watchdog *wdd;
@@ -208,8 +209,9 @@ static __maybe_unused const struct of_device_id stpmic1_wdt_of_match[] = {
{ .compatible = "st,stpmic1-wdt" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stpmic1_wdt_of_match);
-static struct driver_d stpmic1_wdt_driver = {
+static struct driver stpmic1_wdt_driver = {
.name = "stpmic1-wdt",
.probe = stpmic1_wdt_probe,
.of_compatible = DRV_OF_COMPAT(stpmic1_wdt_of_match),
diff --git a/drivers/watchdog/wd_core.c b/drivers/watchdog/wd_core.c
index aa5dee8d20..f39a8f4522 100644
--- a/drivers/watchdog/wd_core.c
+++ b/drivers/watchdog/wd_core.c
@@ -139,12 +139,12 @@ static int watchdog_register_dev(struct watchdog *wd, const char *name, int id)
*
* return: The priority
*/
-static unsigned int dev_get_watchdog_priority(struct device_d *dev)
+static unsigned int dev_get_watchdog_priority(struct device *dev)
{
unsigned int priority = WATCHDOG_DEFAULT_PRIORITY;
if (dev)
- of_property_read_u32(dev->device_node, "watchdog-priority",
+ of_property_read_u32(dev->of_node, "watchdog-priority",
&priority);
return priority;
@@ -179,7 +179,9 @@ static void __noreturn watchdog_restart_handle(struct restart_handler *this)
BUG_ON(ret);
mdelay(2000);
- __builtin_unreachable();
+
+ pr_emerg("Watchdog failed to reset the machine\n");
+ hang();
}
static struct restart_handler restart_handler = {
@@ -194,7 +196,7 @@ int watchdog_register(struct watchdog *wd)
int ret = 0;
if (wd->hwdev)
- alias = of_alias_get(wd->hwdev->device_node);
+ alias = of_alias_get(wd->hwdev->of_node);
if (alias)
ret = watchdog_register_dev(wd, alias, DEVICE_ID_SINGLE);
@@ -309,7 +311,7 @@ EXPORT_SYMBOL(watchdog_get_default);
int watchdog_get_alias_id_from(struct watchdog *wd, struct device_node *root)
{
- struct device_node *dstnp, *srcnp = wd->hwdev ? wd->hwdev->device_node : NULL;
+ struct device_node *dstnp, *srcnp = wd->hwdev ? wd->hwdev->of_node : NULL;
char *name;
if (!srcnp)
@@ -322,14 +324,14 @@ int watchdog_get_alias_id_from(struct watchdog *wd, struct device_node *root)
if (!dstnp)
return -ENODEV;
- return of_alias_get_id_from(root, wd->hwdev->device_node, "watchdog");
+ return of_alias_get_id_from(root, wd->hwdev->of_node, "watchdog");
}
EXPORT_SYMBOL(watchdog_get_alias_id_from);
struct watchdog *watchdog_get_by_name(const char *name)
{
struct watchdog *tmp;
- struct device_d *dev = get_device_by_name(name);
+ struct device *dev = get_device_by_name(name);
if (!dev)
return NULL;
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
index f58aee1283..522f1c25f1 100644
--- a/drivers/watchdog/wdat_wdt.c
+++ b/drivers/watchdog/wdat_wdt.c
@@ -382,7 +382,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
return 0;
}
-static int wdat_wdt_probe(struct device_d *const dev)
+static int wdat_wdt_probe(struct device *const dev)
{
const struct acpi_wdat_entry *entries;
struct acpi_table_wdat *tbl;