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.c12
-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/Kconfig8
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/bus.c10
-rw-r--r--drivers/base/driver.c317
-rw-r--r--drivers/base/featctrl.c159
-rw-r--r--drivers/base/platform.c13
-rw-r--r--drivers/base/power.c291
-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.c22
-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/Kconfig51
-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.c176
-rw-r--r--drivers/clk/clkdev.c16
-rw-r--r--drivers/clk/imx/Makefile1
-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.c49
-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-imx8mm.c65
-rw-r--r--drivers/clk/imx/clk-imx8mn.c65
-rw-r--r--drivers/clk/imx/clk-imx8mp.c8
-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.h46
-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.c7
-rw-r--r--drivers/clk/rockchip/clk-rk3588.c2530
-rw-r--r--drivers/clk/rockchip/clk.c5
-rw-r--r--drivers/clk/rockchip/clk.h145
-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.c16
-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/Kconfig (renamed from drivers/ddr/imx8m/Kconfig)12
-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/Makefile7
-rw-r--r--drivers/ddr/imx8m/ddr_init.c213
-rw-r--r--drivers/dma/Kconfig15
-rw-r--r--drivers/dma/Makefile4
-rw-r--r--drivers/dma/apbh_dma.c519
-rw-r--r--drivers/dma/debug.c201
-rw-r--r--drivers/dma/debug.h56
-rw-r--r--drivers/dma/map.c47
-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.c308
-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/Kconfig20
-rw-r--r--drivers/gpio/Makefile2
-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-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.c788
-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.c246
-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/Kconfig7
-rw-r--r--drivers/i2c/busses/Makefile1
-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-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.c115
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c8
-rw-r--r--drivers/input/gpio_keys.c42
-rw-r--r--drivers/input/imx_keypad.c7
-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.c2
-rw-r--r--drivers/input/virtio_input.c2
-rw-r--r--drivers/led/led-gpio.c14
-rw-r--r--drivers/led/led-pca955x.c7
-rw-r--r--drivers/led/led-pwm.c7
-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.c616
-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.c704
-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.c472
-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/Kconfig36
-rw-r--r--drivers/mfd/Makefile4
-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.c73
-rw-r--r--drivers/mfd/axp20x.c382
-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.c24
-rw-r--r--drivers/mtd/core.c15
-rw-r--r--drivers/mtd/devices/docg3.c6
-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/nand/Kconfig37
-rw-r--r--drivers/mtd/nand/Makefile5
-rw-r--r--drivers/mtd/nand/atmel/Makefile3
-rw-r--r--drivers/mtd/nand/atmel/atmel_nand_ecc.h (renamed from drivers/mtd/nand/atmel_nand_ecc.h)0
-rw-r--r--drivers/mtd/nand/atmel/legacy.c (renamed from drivers/mtd/nand/atmel_nand.c)31
-rw-r--r--drivers/mtd/nand/atmel/nand-controller.c2049
-rw-r--r--drivers/mtd/nand/atmel/pmecc.c993
-rw-r--r--drivers/mtd/nand/atmel/pmecc.h70
-rw-r--r--drivers/mtd/nand/denali.h2
-rw-r--r--drivers/mtd/nand/nand_base.c86
-rw-r--r--drivers/mtd/nand/nand_denali_dt.c78
-rw-r--r--drivers/mtd/nand/nand_esmt.c10
-rw-r--r--drivers/mtd/nand/nand_fsl_ifc.c11
-rw-r--r--drivers/mtd/nand/nand_hynix.c40
-rw-r--r--drivers/mtd/nand/nand_imx.c13
-rw-r--r--drivers/mtd/nand/nand_jedec.c4
-rw-r--r--drivers/mtd/nand/nand_micron.c16
-rw-r--r--drivers/mtd/nand/nand_mrvl_nfc.c11
-rw-r--r--drivers/mtd/nand/nand_mxs.c588
-rw-r--r--drivers/mtd/nand/nand_omap_gpmc.c14
-rw-r--r--drivers/mtd/nand/nand_onfi.c8
-rw-r--r--drivers/mtd/nand/nand_orion.c7
-rw-r--r--drivers/mtd/nand/nand_s3c24xx.c649
-rw-r--r--drivers/mtd/nand/nand_samsung.c18
-rw-r--r--drivers/mtd/nand/nand_toshiba.c12
-rw-r--r--drivers/mtd/nand/nomadik_nand.c8
-rw-r--r--drivers/mtd/nand/omap_elm.c7
-rw-r--r--drivers/mtd/nand/stm32_fmc2_nand.c1354
-rw-r--r--drivers/mtd/nor/cfi_flash.c14
-rw-r--r--drivers/mtd/nor/cfi_flash.h2
-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.c22
-rw-r--r--drivers/mtd/ubi/ubi.h4
-rw-r--r--drivers/net/Kconfig56
-rw-r--r--drivers/net/Makefile8
-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.c627
-rw-r--r--drivers/net/cpsw.c94
-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/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.c78
-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.c146
-rw-r--r--drivers/net/phy/micrel.c266
-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.c177
-rw-r--r--drivers/net/phy/realtek.c8
-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.c24
-rw-r--r--drivers/net/usb/ax88179_178a.c6
-rw-r--r--drivers/net/usb/r8152.c4
-rw-r--r--drivers/net/usb/r8152_fw.c4
-rw-r--r--drivers/net/usb/smsc95xx.c4
-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.c127
-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.c360
-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/Kconfig18
-rw-r--r--drivers/of/address.c16
-rw-r--r--drivers/of/barebox.c13
-rw-r--r--drivers/of/base.c286
-rw-r--r--drivers/of/device.c10
-rw-r--r--drivers/of/fdt.c297
-rw-r--r--drivers/of/of_firmware.c3
-rw-r--r--drivers/of/of_gpio.c73
-rw-r--r--drivers/of/of_path.c97
-rw-r--r--drivers/of/overlay.c40
-rw-r--r--drivers/of/partition.c88
-rw-r--r--drivers/of/platform.c194
-rw-r--r--drivers/of/reserved-mem.c38
-rw-r--r--drivers/pci/Kconfig11
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/bus.c13
-rw-r--r--drivers/pci/pci-ecam-generic.c9
-rw-r--r--drivers/pci/pci-efi.c6
-rw-r--r--drivers/pci/pci-imx6.c29
-rw-r--r--drivers/pci/pci-layerscape.c22
-rw-r--r--drivers/pci/pci-mvebu.c13
-rw-r--r--drivers/pci/pci-tegra.c15
-rw-r--r--drivers/pci/pci.c261
-rw-r--r--drivers/pci/pcie-designware-host.c6
-rw-r--r--drivers/pci/pcie-designware.c4
-rw-r--r--drivers/pci/pcie-designware.h2
-rw-r--r--drivers/pci/pcie-dw-rockchip.c300
-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.c61
-rw-r--r--drivers/phy/phy-stm32-usbphyc.c353
-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/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.c3116
-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.c1331
-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.c28
-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/core.c17
-rw-r--r--drivers/pwm/pwm-atmel.c9
-rw-r--r--drivers/pwm/pwm-imx.c54
-rw-r--r--drivers/pwm/pwm-mxs.c7
-rw-r--r--drivers/pwm/pwm-stm32.c10
-rw-r--r--drivers/pwm/pxa_pwm.c12
-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/Kconfig1
-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.c4
-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/Kconfig15
-rw-r--r--drivers/soc/imx/Makefile3
-rw-r--r--drivers/soc/imx/gpcv2.c516
-rw-r--r--drivers/soc/imx/imx8m-featctrl.c90
-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/sound/gpio-beeper.c23
-rw-r--r--drivers/sound/pwm-beeper.c32
-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.c8
-rw-r--r--drivers/usb/core/of.c4
-rw-r--r--drivers/usb/core/usb.c54
-rw-r--r--drivers/usb/dwc2/core.c32
-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.c4
-rw-r--r--drivers/usb/dwc3/core.c751
-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.c210
-rw-r--r--drivers/usb/gadget/function/Makefile6
-rw-r--r--drivers/usb/gadget/function/dfu.c (renamed from drivers/usb/gadget/dfu.c)25
-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)91
-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.c70
-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.c292
-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
954 files changed, 83313 insertions, 19911 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..1fbb7b9188 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;
}
@@ -91,10 +91,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 5bc70aa1e5..21a4793cfa 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -2,3 +2,11 @@
config PM_GENERIC_DOMAINS
bool
+
+config FEATURE_CONTROLLER
+ 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 59645c6f53..acc53763da 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -6,3 +6,5 @@ obj-y += resource.o
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 e7288f6a61..fbc5cbebe0 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -24,9 +24,11 @@
#include <fs.h>
#include <of.h>
#include <linux/list.h>
+#include <linux/overflow.h>
#include <linux/err.h>
#include <complete.h>
#include <pinctrl.h>
+#include <featctrl.h>
#include <linux/clk/clk-conf.h>
#ifdef CONFIG_DEBUG_PROBES
@@ -45,21 +47,45 @@ LIST_HEAD(active_device_list);
EXPORT_SYMBOL(active_device_list);
static LIST_HEAD(deferred);
-struct device_d *get_device_by_name(const char *name)
+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);
+ if (dev)
+ return dev;
+
+ np = of_find_node_by_path_or_alias(NULL, str);
+ if (np)
+ return of_find_device_by_node(np);
+
+ return NULL;
+}
+
+struct device *get_device_by_name(const char *name)
+{
+ 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)
@@ -80,53 +106,78 @@ 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->of_node);
+ if (ret < 0)
+ return ret;
+ if (ret == FEATCTRL_GATED) {
+ dev_dbg(dev, "feature gated, skipping probe\n");
+ return -ENODEV;
+ }
+
depth++;
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_del(&dev->active);
- INIT_LIST_HEAD(&dev->active);
+ 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;
+ }
- if (ret == -ENODEV || ret == -ENXIO)
- dev_dbg(dev, "probe failed: %s\n", strerror(-ret));
- else
- dev_err(dev, "probe failed: %s\n", strerror(-ret));
+ list_del_init(&dev->active);
-out:
- depth--;
return ret;
}
-int device_detect(struct device_d *dev)
+int device_detect(struct device *dev)
{
if (!dev->detect)
return -ENOSYS;
@@ -137,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) {
@@ -160,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;
@@ -175,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)
@@ -187,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);
@@ -234,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));
@@ -252,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);
}
}
@@ -265,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;
}
@@ -279,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;
@@ -296,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);
@@ -312,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 {
@@ -336,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))
@@ -359,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;
@@ -380,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;
@@ -397,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;
@@ -409,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)
{
@@ -428,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;
@@ -436,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;
@@ -452,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;
@@ -460,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;
@@ -512,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;
@@ -537,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;
@@ -569,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;
@@ -580,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;
@@ -630,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;
@@ -654,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
*
@@ -663,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
new file mode 100644
index 0000000000..153720e5ee
--- /dev/null
+++ b/drivers/base/featctrl.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2022 Ahmad Fatoum, Pengutronix
+
+#define pr_fmt(fmt) "featctrl: " fmt
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <of.h>
+
+#include <featctrl.h>
+
+/* List of registered feature controllers */
+static LIST_HEAD(of_feature_controllers);
+
+/**
+ * feature_controller_register() - Register a feature controller
+ * @feat: Pointer to feature controller
+ */
+int feature_controller_register(struct feature_controller *feat)
+{
+ struct device_node *np = dev_of_node(feat->dev);
+
+ if (!np)
+ return -EINVAL;
+
+ list_add(&feat->list, &of_feature_controllers);
+ dev_dbg(feat->dev, "Registering feature controller\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(feature_controller_register);
+
+/**
+ * featctrl_get_from_provider() - Look-up feature gate
+ * @spec: OF phandle args to use for look-up
+ * @gateid: ID of feature controller gate populated on successful lookup
+ *
+ * Looks for a feature controller under the node specified by @spec.
+ *
+ * Returns a valid pointer to struct feature_controller on success or ERR_PTR()
+ * on failure.
+ */
+static struct feature_controller *featctrl_get_from_provider(struct of_phandle_args *spec,
+ unsigned *gateid)
+{
+ struct feature_controller *featctrl;
+ int ret;
+
+ 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_count ? spec->args[0] : 0;
+ return featctrl;
+ }
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+/**
+ * of_feature_controller_check - Check whether a feature controller gates the device
+ * @np: Device node to check
+ *
+ * Parse device's OF node to find a feature controller specifier. If such is
+ * found, checks it to determine whether device is gated.
+ *
+ * Returns FEATCTRL_GATED if a specified feature controller gates the device
+ * and FEATCTRL_OKAY if none do. On error a negative error code is returned.
+ */
+int of_feature_controller_check(struct device_node *np)
+{
+ struct of_phandle_args featctrl_args;
+ struct feature_controller *featctrl;
+ int ret, err = 0, i, ngates;
+
+ ngates = of_count_phandle_with_args(np, "barebox,feature-gates",
+ "#feature-cells");
+ if (ngates <= 0)
+ return FEATCTRL_OKAY;
+
+ for (i = 0; i < ngates; i++) {
+ unsigned gateid = 0;
+
+ ret = of_parse_phandle_with_args(np, "barebox,feature-gates",
+ "#feature-cells", i, &featctrl_args);
+ if (ret < 0)
+ return ret;
+
+ featctrl = featctrl_get_from_provider(&featctrl_args, &gateid);
+ if (IS_ERR(featctrl)) {
+ ret = PTR_ERR(featctrl);
+ pr_debug("%s() failed to find feature controller: %pe\n",
+ __func__, ERR_PTR(ret));
+ /*
+ * Assume that missing featctrls are unresolved
+ * dependency are report them as deferred
+ */
+ return (ret == -ENOENT) ? -EPROBE_DEFER : ret;
+ }
+
+ ret = featctrl->check(featctrl, gateid);
+
+ dev_dbg(featctrl->dev, "checking %pOF: %d\n", np, ret);
+
+ if (ret == FEATCTRL_OKAY)
+ return FEATCTRL_OKAY;
+ if (ret != FEATCTRL_GATED)
+ err = ret;
+ }
+
+ return err ?: FEATCTRL_GATED;
+}
+EXPORT_SYMBOL_GPL(of_feature_controller_check);
+
+static int of_featctrl_fixup(struct device_node *root, void *context)
+{
+ struct device_node *srcnp, *dstnp;
+ int err = 0;
+
+ for_each_node_with_property(srcnp, "barebox,feature-gates") {
+ int ret;
+
+ ret = of_feature_controller_check(srcnp);
+ if (ret < 0)
+ err = ret;
+ if (ret != FEATCTRL_GATED)
+ continue;
+
+ dstnp = of_get_node_by_reproducible_name(root, srcnp);
+ if (!dstnp) {
+ pr_debug("gated node %pOF not in fixup DT\n", srcnp);
+ continue;
+ }
+
+ 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);
+ else
+ of_device_disable(dstnp);
+ }
+
+ return err;
+}
+
+static __maybe_unused int of_featctrl_fixup_register(void)
+{
+ return of_register_fixup(of_featctrl_fixup, NULL);
+}
+#ifdef CONFIG_FEATURE_CONTROLLER_FIXUP
+device_initcall(of_featctrl_fixup_register);
+#endif
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 4a206051b1..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,19 +412,124 @@ 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;
+
+ printf("%-20s %6s\n", "name", "active");
+ list_for_each_entry(genpd, &gpd_list, gpd_list_node)
+ printf("%-20s %6s\n", genpd->name,
+ genpd->status == GPD_STATE_ACTIVE ? "on" : "off");
+}
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 c8422ca46f..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"
@@ -46,7 +46,7 @@ static int regmap_mmio_get_min_stride(size_t val_bits)
case 8:
/* The core treats 0 as 1 */
min_stride = 0;
- return 0;
+ break;
case 16:
min_stride = 2;
break;
@@ -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 5b5acf4e06..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
@@ -20,13 +40,24 @@ config CLK_SOCFPGA
select COMMON_CLK_OF_PROVIDER
default y if ARCH_SOCFPGA && OFDEVICE
+if COMMON_CLK
config COMMON_CLK_STM32F
bool "STM32F4 and STM32F7 clock driver" if COMPILE_TEST
- depends on COMMON_CLK && ARCH_STM32
+ depends on ARCH_STM32
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
@@ -37,4 +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..d3f5d5e838 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,6 +1018,15 @@ static const char *clk_hw_stat(struct clk *clk)
return "unknown";
}
+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(struct clk *clk, int verbose, int indent)
{
int enabled = clk_is_enabled(clk);
@@ -959,7 +1051,7 @@ static void dump_one(struct clk *clk, int verbose, int indent)
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");
}
}
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-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 8aa87a5200..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"
@@ -37,25 +37,25 @@
#define CCM_MCR 0x64
enum mx25_clks {
- dummy, osc, mpll, upll, mpll_cpu_3_4, cpu_sel, cpu, ahb, usb_div, ipg,
- per0_sel, per1_sel, per2_sel, per3_sel, per4_sel, per5_sel, per6_sel,
- per7_sel, per8_sel, per9_sel, per10_sel, per11_sel, per12_sel,
- per13_sel, per14_sel, per15_sel, per0, per1, per2, per3, per4, per5,
- per6, per7, per8, per9, per10, per11, per12, per13, per14, per15,
- csi_ipg_per, epit_ipg_per, esai_ipg_per, esdhc1_ipg_per, esdhc2_ipg_per,
- gpt_ipg_per, i2c_ipg_per, lcdc_ipg_per, nfc_ipg_per, owire_ipg_per,
- pwm_ipg_per, sim1_ipg_per, sim2_ipg_per, ssi1_ipg_per, ssi2_ipg_per,
- uart_ipg_per, ata_ahb, reserved1, csi_ahb, emi_ahb, esai_ahb, esdhc1_ahb,
- esdhc2_ahb, fec_ahb, lcdc_ahb, rtic_ahb, sdma_ahb, slcdc_ahb, usbotg_ahb,
- reserved2, reserved3, reserved4, reserved5, can1_ipg, can2_ipg, csi_ipg,
- cspi1_ipg, cspi2_ipg, cspi3_ipg, dryice_ipg, ect_ipg, epit1_ipg, epit2_ipg,
- reserved6, esdhc1_ipg, esdhc2_ipg, fec_ipg, reserved7, reserved8, reserved9,
- gpt1_ipg, gpt2_ipg, gpt3_ipg, gpt4_ipg, reserved10, reserved11, reserved12,
- iim_ipg, reserved13, reserved14, kpp_ipg, lcdc_ipg, reserved15, pwm1_ipg,
- pwm2_ipg, pwm3_ipg, pwm4_ipg, rngb_ipg, reserved16, scc_ipg, sdma_ipg,
- sim1_ipg, sim2_ipg, slcdc_ipg, spba_ipg, ssi1_ipg, ssi2_ipg, tsc_ipg,
- uart1_ipg, uart2_ipg, uart3_ipg, uart4_ipg, uart5_ipg, reserved17,
- wdt_ipg, clk_max
+ /* 0 */ dummy, osc, mpll, upll, mpll_cpu_3_4, cpu_sel, cpu, ahb, usb_div, ipg,
+ /* 10 */ per0_sel, per1_sel, per2_sel, per3_sel, per4_sel, per5_sel, per6_sel,
+ /* 17 */ per7_sel, per8_sel, per9_sel, per10_sel, per11_sel, per12_sel,
+ /* 23 */ per13_sel, per14_sel, per15_sel, per0, per1, per2, per3, per4, per5,
+ /* 32 */ per6, per7, per8, per9, per10, per11, per12, per13, per14, per15,
+ /* 42 */ csi_ipg_per, epit_ipg_per, esai_ipg_per, esdhc1_ipg_per, esdhc2_ipg_per,
+ /* 47 */ gpt_ipg_per, i2c_ipg_per, lcdc_ipg_per, nfc_ipg_per, owire_ipg_per,
+ /* 52 */ pwm_ipg_per, sim1_ipg_per, sim2_ipg_per, ssi1_ipg_per, ssi2_ipg_per,
+ /* 57 */ uart_ipg_per, ata_ahb, reserved1, csi_ahb, emi_ahb, esai_ahb, esdhc1_ahb,
+ /* 64 */ esdhc2_ahb, fec_ahb, lcdc_ahb, rtic_ahb, sdma_ahb, slcdc_ahb, usbotg_ahb,
+ /* 71 */ reserved2, reserved3, reserved4, reserved5, can1_ipg, can2_ipg, csi_ipg,
+ /* 78 */ cspi1_ipg, cspi2_ipg, cspi3_ipg, dryice_ipg, ect_ipg, epit1_ipg, epit2_ipg,
+ /* 85 */ reserved6, esdhc1_ipg, esdhc2_ipg, fec_ipg, reserved7, reserved8, reserved9,
+ /* 92 */ gpt1_ipg, gpt2_ipg, gpt3_ipg, gpt4_ipg, reserved10, reserved11, reserved12,
+ /* 99 */ iim_ipg, reserved13, reserved14, kpp_ipg, lcdc_ipg, reserved15, pwm1_ipg,
+ /* 106 */ pwm2_ipg, pwm3_ipg, pwm4_ipg, rngb_ipg, reserved16, scc_ipg, sdma_ipg,
+ /* 113 */ sim1_ipg, sim2_ipg, slcdc_ipg, spba_ipg, ssi1_ipg, ssi2_ipg, tsc_ipg,
+ /* 120 */ uart1_ipg, uart2_ipg, uart3_ipg, uart4_ipg, uart5_ipg, reserved17,
+ /* 126 */ wdt_ipg, clk_max
};
static struct clk *clks[clk_max];
@@ -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;
@@ -143,6 +143,9 @@ static int imx25_ccm_probe(struct device_d *dev)
clks[rngb_ipg] = imx_clk_gate("rngb_ipg", "ipg", base + CCM_CGCR2, 3);
clks[dryice_ipg] = imx_clk_gate("dryice_ipg", "ipg", base + CCM_CGCR1, 8);
+ /* reserved in datasheet, but used as wdt in FSL kernel */
+ clks[wdt_ipg] = imx_clk_gate("wdt_ipg", "ipg", base + CCM_CGCR2, 19);
+
clkdev_add_physbase(clks[per15], MX25_UART1_BASE_ADDR, NULL);
clkdev_add_physbase(clks[per15], MX25_UART2_BASE_ADDR, NULL);
clkdev_add_physbase(clks[per15], MX25_UART3_BASE_ADDR, NULL);
@@ -168,6 +171,7 @@ static int imx25_ccm_probe(struct device_d *dev)
clkdev_add_physbase(clks[scc_ipg], MX25_SCC_BASE_ADDR, "ipg");
clkdev_add_physbase(clks[rngb_ipg], MX25_RNGB_BASE_ADDR, "ipg");
clkdev_add_physbase(clks[dryice_ipg], MX25_DRYICE_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[wdt_ipg], MX25_WATCHDOG_BASE_ADDR, NULL);
return 0;
}
@@ -179,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-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
index e6927f58f4..d467062e64 100644
--- a/drivers/clk/imx/clk-imx8mm.c
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -280,6 +280,7 @@ static int imx8mm_clocks_init(struct device_node *ccm_np)
{
struct device_node *anatop_np;
void __iomem *ccm, *ana;
+ u32 val;
int ret;
anatop_np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
@@ -340,45 +341,45 @@ static int imx8mm_clocks_init(struct device_node *ccm_np)
clks[IMX8MM_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", ana + 0x114, 11);
/* SYS PLL1 fixed output */
- clks[IMX8MM_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1", ana + 0x94, 27);
- clks[IMX8MM_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1", ana + 0x94, 25);
- clks[IMX8MM_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1", ana + 0x94, 23);
- clks[IMX8MM_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1", ana + 0x94, 21);
- clks[IMX8MM_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1", ana + 0x94, 19);
- clks[IMX8MM_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1", ana + 0x94, 17);
- clks[IMX8MM_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1", ana + 0x94, 15);
- clks[IMX8MM_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1", ana + 0x94, 13);
+ /*
+ * The gates in CCM_ANALOG_SYS_PLL1_GEN_CTRL are not handled by the
+ * driver, make sure they are all enabled.
+ */
+ val = readl(ana + 0x94);
+ val |= BIT(13) | BIT(15) | BIT(17) | BIT(19) | BIT(21) | BIT(23) | BIT(25) | BIT(27);
+ writel(val, ana + 0x94);
+
clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1", ana + 0x94, 11);
- clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20);
- clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10);
- clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8);
- clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6);
- clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5);
- clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4);
- clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3);
- clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2);
+ clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20);
+ clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10);
+ clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8);
+ clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6);
+ clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5);
+ clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4);
+ clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3);
+ clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2);
clks[IMX8MM_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1);
/* SYS PLL2 fixed output */
- clks[IMX8MM_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2", ana + 0x104, 27);
- clks[IMX8MM_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2", ana + 0x104, 25);
- clks[IMX8MM_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2", ana + 0x104, 23);
- clks[IMX8MM_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2", ana + 0x104, 21);
- clks[IMX8MM_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2", ana + 0x104, 19);
- clks[IMX8MM_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2", ana + 0x104, 17);
- clks[IMX8MM_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2", ana + 0x104, 15);
- clks[IMX8MM_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2", ana + 0x104, 13);
+ /*
+ * The gates in CCM_ANALOG_SYS_PLL2_GEN_CTRL are not handled by the
+ * driver, make sure they are all enabled.
+ */
+ val = readl(ana + 0x104);
+ val |= BIT(13) | BIT(15) | BIT(17) | BIT(19) | BIT(21) | BIT(23) | BIT(25) | BIT(27);
+ writel(val, ana + 0x104);
+
clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2", ana + 0x104, 11);
- clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20);
- clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10);
- clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8);
- clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6);
- clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5);
- clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4);
- clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3);
- clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2);
+ clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20);
+ clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10);
+ clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8);
+ clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6);
+ clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5);
+ clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4);
+ clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3);
+ clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2);
clks[IMX8MM_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1);
/* Core Slice */
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c
index a57bfffcb6..02522add39 100644
--- a/drivers/clk/imx/clk-imx8mn.c
+++ b/drivers/clk/imx/clk-imx8mn.c
@@ -281,6 +281,7 @@ static int imx8mn_clocks_init(struct device_node *ccm_np)
{
struct device_node *anatop_np;
void __iomem *ccm, *ana;
+ u32 val;
int ret;
anatop_np = of_find_compatible_node(NULL, NULL, "fsl,imx8mn-anatop");
@@ -341,45 +342,45 @@ static int imx8mn_clocks_init(struct device_node *ccm_np)
clks[IMX8MN_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", ana + 0x114, 11);
/* SYS PLL1 fixed output */
- clks[IMX8MN_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1", ana + 0x94, 27);
- clks[IMX8MN_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1", ana + 0x94, 25);
- clks[IMX8MN_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1", ana + 0x94, 23);
- clks[IMX8MN_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1", ana + 0x94, 21);
- clks[IMX8MN_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1", ana + 0x94, 19);
- clks[IMX8MN_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1", ana + 0x94, 17);
- clks[IMX8MN_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1", ana + 0x94, 15);
- clks[IMX8MN_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1", ana + 0x94, 13);
+ /*
+ * The gates in CCM_ANALOG_SYS_PLL1_GEN_CTRL are not handled by the
+ * driver, make sure they are all enabled.
+ */
+ val = readl(ana + 0x94);
+ val |= BIT(13) | BIT(15) | BIT(17) | BIT(19) | BIT(21) | BIT(23) | BIT(25) | BIT(27);
+ writel(val, ana + 0x94);
+
clks[IMX8MN_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1", ana + 0x94, 11);
- clks[IMX8MN_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20);
- clks[IMX8MN_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10);
- clks[IMX8MN_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8);
- clks[IMX8MN_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6);
- clks[IMX8MN_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5);
- clks[IMX8MN_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4);
- clks[IMX8MN_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3);
- clks[IMX8MN_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2);
+ clks[IMX8MN_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20);
+ clks[IMX8MN_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10);
+ clks[IMX8MN_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8);
+ clks[IMX8MN_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6);
+ clks[IMX8MN_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5);
+ clks[IMX8MN_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4);
+ clks[IMX8MN_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3);
+ clks[IMX8MN_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2);
clks[IMX8MN_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1);
/* SYS PLL2 fixed output */
- clks[IMX8MN_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2", ana + 0x104, 27);
- clks[IMX8MN_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2", ana + 0x104, 25);
- clks[IMX8MN_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2", ana + 0x104, 23);
- clks[IMX8MN_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2", ana + 0x104, 21);
- clks[IMX8MN_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2", ana + 0x104, 19);
- clks[IMX8MN_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2", ana + 0x104, 17);
- clks[IMX8MN_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2", ana + 0x104, 15);
- clks[IMX8MN_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2", ana + 0x104, 13);
+ /*
+ * The gates in CCM_ANALOG_SYS_PLL2_GEN_CTRL are not handled by the
+ * driver, make sure they are all enabled.
+ */
+ val = readl(ana + 0x104);
+ val |= BIT(13) | BIT(15) | BIT(17) | BIT(19) | BIT(21) | BIT(23) | BIT(25) | BIT(27);
+ writel(val, ana + 0x104);
+
clks[IMX8MN_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2", ana + 0x104, 11);
- clks[IMX8MN_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20);
- clks[IMX8MN_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10);
- clks[IMX8MN_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8);
- clks[IMX8MN_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6);
- clks[IMX8MN_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5);
- clks[IMX8MN_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4);
- clks[IMX8MN_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3);
- clks[IMX8MN_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2);
+ clks[IMX8MN_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20);
+ clks[IMX8MN_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10);
+ clks[IMX8MN_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8);
+ clks[IMX8MN_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6);
+ clks[IMX8MN_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5);
+ clks[IMX8MN_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4);
+ clks[IMX8MN_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3);
+ clks[IMX8MN_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2);
clks[IMX8MN_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1);
clks[IMX8MN_CLK_CLKOUT1_SEL] = imx_clk_mux("clkout1_sel", ana + 0x128, 4, 4, clkout_sels, ARRAY_SIZE(clkout_sels));
diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index a1611be183..e08cebc19c 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -174,10 +174,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", };
@@ -560,7 +556,6 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
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);
@@ -665,7 +660,8 @@ static int imx8mp_clocks_init(struct device_node *ccm_np)
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);
+ hws[IMX8MP_CLK_USB_SUSP] = imx_clk_hw_gate2_shared2("usb_suspend_clk", "osc_32k", ccm_base + 0x44d0, 0);
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);
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..9058f913d3 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,
@@ -296,6 +304,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
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..d07d7aef5d 100644
--- a/drivers/clk/rockchip/clk-rk3568.c
+++ b/drivers/clk/rockchip/clk-rk3568.c
@@ -1718,10 +1718,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 +1732,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..8b5c68debe
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rk3588.c
@@ -0,0 +1,2530 @@
+// 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;
+ void __iomem *reg_base;
+
+ 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..aca107a45d 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;
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index a3db88dfc8..df819c61f1 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)
@@ -808,18 +904,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 70e016486a..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,9 +287,8 @@ 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 device_node *nprop;
struct caam_job_ring __iomem *ctrl;
struct caam_drv_private_jr *jrpriv;
static int total_jobrs;
@@ -303,7 +302,6 @@ int caam_jr_probe(struct device_d *dev)
/* save ring identity relative to detection */
jrpriv->ridx = total_jobrs++;
- nprop = dev->device_node;
/* Get configuration properties from device tree */
/* First, get register page */
ctrl = dev_get_mem_region(dev, 0);
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/imx8m/Kconfig b/drivers/ddr/imx/Kconfig
index 720448f551..71d4144e85 100644
--- a/drivers/ddr/imx8m/Kconfig
+++ b/drivers/ddr/imx/Kconfig
@@ -1,8 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "i.MX8M DDR controllers"
- depends on ARCH_IMX8MQ || ARCH_IMX8MM || ARCH_IMX8MN || ARCH_IMX8MP
+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"
+config IMX9_DRAM
+ select IMX_DRAM
+ bool "imx9 dram controller support"
+
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/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 4802bf522d..e7516466d9 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -3,8 +3,21 @@ menu "DMA support"
config MXS_APBH_DMA
tristate "MXS APBH DMA ENGINE"
- depends on ARCH_IMX23 || ARCH_IMX28 || ARCH_IMX6
+ depends on ARCH_IMX23 || ARCH_IMX28 || ARCH_IMX6 || ARCH_IMX7
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..e524dc4127
--- /dev/null
+++ b/drivers/dma/debug.c
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <dma.h>
+#include <linux/list.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");
+}
+
+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..ab86a8c7b1 100644
--- a/drivers/dma/map.c
+++ b/drivers/dma/map.c
@@ -1,42 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* SPDX-FileCopyrightText: 2012 Marc Kleine-Budde <mkl@pengutronix.de> */
-
#include <dma.h>
+#include "debug.h"
-static inline dma_addr_t cpu_to_dma(struct device_d *dev, unsigned long cpu_addr)
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t address,
+ size_t size, enum dma_data_direction dir)
{
- dma_addr_t dma_addr = cpu_addr;
+ void *ptr = dma_to_cpu(dev, address);
- if (dev)
- dma_addr -= dev->dma_offset;
+ debug_dma_sync_single_for_cpu(dev, address, size, dir);
- return dma_addr;
+ 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);
- dma_sync_single_for_device(addr, size, dir);
+ debug_dma_map(dev, ptr, size, dir, dma_addr);
- return cpu_to_dma(dev, addr);
+ if (!dev_is_dma_coherent(dev))
+ arch_sync_dma_for_device(ptr, size, dir);
+
+ 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..1ba81d1b50
--- /dev/null
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -0,0 +1,308 @@
+// 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;
+ dma_addr_t mapping;
+
+ if (pos != 0)
+ return -EINVAL;
+
+ mapping = dma_map_single(dev, (void *)buf, count, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, mapping))
+ return -EFAULT;
+
+ 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);
+
+ return 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..9f27addaa2 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,12 @@ 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.
+
endmenu
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fcb6a232e0..628e975285 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,4 @@ 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
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-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 97a99b84e3..5bc261a010 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,302 @@ 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);
+}
+
+bool gpio_slice_acquired(unsigned gpio)
+{
+ struct gpio_desc *desc = gpio_to_desc(gpio);
+
+ if (!desc)
+ return false;
+
+ return slice_acquired(&desc->chip->slice);
}
-static void gpioinfo_free(struct gpio_info *gi)
+static void gpiodesc_free(struct gpio_desc *desc)
{
- if (!gi->requested)
+ 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 +471,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 +498,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 +599,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;
- gpio = gpio_of_xlate(chip->dev, &gpiospec, &gpio_flags);
+ /*
+ * 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;
+
+ 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 +676,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 +697,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,35 +842,172 @@ 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)
{
int i;
@@ -605,13 +1023,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,83 +1038,93 @@ 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)
+struct gpio_chip *gpio_get_chip_by_dev(struct device *dev)
{
- /*
- * Support for GPIOs that don't have #gpio-cells set to 2 is
- * not implemented
- */
- if (WARN_ON(gpiospec->args_count != 2))
- return -ENOTSUPP;
+ struct gpio_chip *chip;
- if (flags)
- *flags = gpiospec->args[1];
+ list_for_each_entry(chip, &chip_list, list) {
+ if (chip->dev == dev)
+ return chip;
+ }
- return chip->base + gpiospec->args[0];
+ 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;
if (!dev)
return -ENODEV;
- list_for_each_entry(chip, &chip_list, list) {
- if (chip->dev != dev)
- continue;
- if (chip->ops->of_xlate)
- return chip->ops->of_xlate(chip, gpiospec, flags);
- else
- return of_gpio_simple_xlate(chip, gpiospec, flags);
- }
+ chip = gpio_get_chip_by_dev(dev);
+ if (!chip)
+ return -EPROBE_DEFER;
- return -EPROBE_DEFER;
+ 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
static int do_gpiolib(int argc, char *argv[])
{
+ struct gpio_chip *chip = NULL;
int i;
+ if (argc > 2)
+ return COMMAND_ERROR_USAGE;
+
+ if (argc > 1) {
+ struct device *dev;
+
+ dev = find_device(argv[1]);
+ if (!dev)
+ return -ENODEV;
+
+ chip = gpio_get_chip_by_dev(dev);
+ if (!chip)
+ return -EINVAL;
+ }
+
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 != 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");
}
- if (gi->chip->ops->get_direction)
- dir = gi->chip->ops->get_direction(gi->chip,
- i - gi->chip->base);
- if (gi->chip->ops->get)
- val = gi->chip->ops->get(gi->chip,
- i - gi->chip->base);
+ idx = i - desc->chip->base;
+
+ 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", i,
+ 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;
@@ -704,6 +1133,7 @@ static int do_gpiolib(int argc, char *argv[])
BAREBOX_CMD_START(gpioinfo)
.cmd = do_gpiolib,
BAREBOX_CMD_DESC("list registered GPIOs")
+ BAREBOX_CMD_OPTS("[CONTROLLER]")
BAREBOX_CMD_GROUP(CMD_GRP_INFO)
BAREBOX_CMD_COMPLETE(empty_complete)
BAREBOX_CMD_END
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..a1d823ed25 100644
--- a/drivers/hab/habv4.c
+++ b/drivers/hab/habv4.c
@@ -11,13 +11,17 @@
#include <hab.h>
#include <init.h>
#include <types.h>
+#include <mmu.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 +69,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 +145,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 +240,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 +303,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 +319,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 +367,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 +525,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 +572,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;
@@ -515,14 +595,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 +617,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 +648,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 +672,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..a274baf4b6 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
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a1ab46fb28..b4225995c0 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_I2C_BCM283X) += i2c-bcm283x.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-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 b24fd88f5b..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,37 +426,68 @@ 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;
int len;
+ if (n->dev) {
+ dev_dbg(&adap->dev, "of_i2c: skipping already registered %s\n",
+ dev_name(n->dev));
+ continue;
+ }
+
of_modalias_node(n, info.type, I2C_NAME_SIZE);
info.of_node = n;
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);
+ }
+}
+
+int of_i2c_register_devices_by_node(struct device_node *node)
+{
+ struct i2c_adapter *adap;
+
+ adap = of_find_i2c_adapter_by_node(node);
+ if (!adap)
+ return -ENODEV;
+ if (IS_ERR(adap))
+ return PTR_ERR(adap);
+
+ of_i2c_register_devices(adap);
+ 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;
}
}
@@ -521,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
@@ -555,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;
@@ -566,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;
@@ -577,12 +615,26 @@ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
return to_i2c_client(dev);
}
-static void i2c_parse_timing(struct device_d *dev, char *prop_name, u32 *cur_val_p,
- u32 def_val, bool use_def)
+int of_i2c_device_enable_and_register_by_alias(const char *alias)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_alias(NULL, alias);
+ if (!np)
+ return -ENODEV;
+
+ of_device_enable(np);
+ return of_i2c_register_devices_by_node(np->parent);
+}
+
+
+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;
@@ -605,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;
@@ -649,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) {
@@ -672,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/gpio_keys.c b/drivers/input/gpio_keys.c
index 45b3c562f8..c23d20563c 100644
--- a/drivers/input/gpio_keys.c
+++ b/drivers/input/gpio_keys.c
@@ -29,24 +29,25 @@ 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;
+ struct device *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);
@@ -64,9 +65,11 @@ static void gpio_key_poller(struct poller_struct *poller)
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 +95,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,7 +134,7 @@ 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;
@@ -140,7 +143,7 @@ static int __init gpio_keys_probe(struct device_d *dev)
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 +162,16 @@ 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;
-
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 +179,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..fc6b45b3cb 100644
--- a/drivers/input/imx_keypad.c
+++ b/drivers/input/imx_keypad.c
@@ -72,7 +72,7 @@
struct imx_keypad {
struct input_device input;
struct clk *clk;
- struct device_d *dev;
+ struct device *dev;
void __iomem *mmio_base;
struct poller_struct poller;
@@ -358,7 +358,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;
@@ -421,8 +421,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/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..86b48db2a7 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>
diff --git a/drivers/input/virtio_input.c b/drivers/input/virtio_input.c
index b5430886ab..5c6849b561 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;
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..2ffb72e692 100644
--- a/drivers/led/led-pwm.c
+++ b/drivers/led/led-pwm.c
@@ -39,12 +39,12 @@ static void led_pwm_set(struct led *led, unsigned int brightness)
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..b7dd98049f 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,50 @@
#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 */
+ 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 +111,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 +188,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 +231,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 (is_timeout(start, 1000 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for done\n");
- return -ETIMEDOUT;
- }
- } while ((stat & mask) != mask);
+ if (ret == -ETIMEDOUT)
+ return;
- 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 +248,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 +278,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 +304,404 @@ 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_zynqmp_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_zynqmp_sdcardclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- struct device_node *np = dev->device_node;
+ 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 clk_sampleclk_ops = {
+ .recalc_rate = arasan_zynqmp_sampleclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sampleclk_set_phase,
+};
+
+static const struct clk_ops clk_sdcardclk_ops = {
+ .recalc_rate = arasan_zynqmp_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 sdhci_arasan_clk_data *clk_data,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ 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 = &clk_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 sdhci_arasan_clk_data *clk_data,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ 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 = &clk_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 sdhci_arasan_clk_data *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)
+{
+ 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 +746,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 +761,22 @@ 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 (IS_ENABLED(CONFIG_ARCH_ZYNQMP))
+ arasan_sdhci_register_sdclk(&arasan_sdhci->clk_data, 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 +791,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 8c08a4f61f..a3b25ea01a 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 = xmalloc(512);
+
+ ret = mci_send_ext_csd(mci, ext_csd);
+ if (ret) {
+ printf("Failure to read EXT_CSD register\n");
+ 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;
}
@@ -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;
@@ -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));
@@ -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) {
- dev_warn(&mci->dev, "No partition table found\n");
- rc = 0; /* it's not a failure */
- }
-
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];
+
+ 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..8bba1e3bf9 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)
@@ -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 6c6b65dacf..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,10 +120,37 @@ 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.
This driver provides common support for accessing the device
through I2C interface.
+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 7b5d0398d1..00f3eacf3c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -22,3 +22,7 @@ obj-$(CONFIG_SMSC_SUPERIO) += smsc-superio.o
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
new file mode 100644
index 0000000000..bfd93902b4
--- /dev/null
+++ b/drivers/mfd/axp20x-i2c.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2C driver for the X-Powers' Power Management ICs
+ *
+ * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC
+ * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature
+ * as well as configurable GPIOs.
+ *
+ * This driver supports the I2C variants.
+ *
+ * Copyright (C) 2014 Carlo Caione
+ *
+ * Author: Carlo Caione <carlo@caione.org>
+ */
+
+#include <common.h>
+#include <of.h>
+#include <linux/err.h>
+#include <i2c/i2c.h>
+#include <module.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/regmap.h>
+
+static int axp20x_i2c_probe(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct axp20x_dev *axp20x;
+ int ret;
+
+ axp20x = xzalloc(sizeof(*axp20x));
+
+ axp20x->dev = dev;
+
+ ret = axp20x_match_device(axp20x);
+ if (ret)
+ return ret;
+
+ axp20x->regmap = regmap_init_i2c(client, axp20x->regmap_cfg);
+ if (IS_ERR(axp20x->regmap))
+ return dev_err_probe(dev, PTR_ERR(axp20x->regmap),
+ "regmap init failed\n");
+
+ ret = axp20x_device_probe(axp20x);
+ if (ret)
+ return ret;
+
+ return regmap_register_cdev(axp20x->regmap, NULL);
+}
+
+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 axp20x_i2c_driver = {
+ .name = "axp20x-i2c",
+ .probe = axp20x_i2c_probe,
+ .of_compatible = DRV_OF_COMPAT(axp20x_i2c_of_match),
+};
+
+coredevice_i2c_driver(axp20x_i2c_driver);
+
+MODULE_DESCRIPTION("PMIC MFD I2C driver for AXP20X");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
new file mode 100644
index 0000000000..666b9ea98c
--- /dev/null
+++ b/drivers/mfd/axp20x.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core driver for the X-Powers' Power Management ICs
+ *
+ * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC
+ * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature
+ * as well as configurable GPIOs.
+ *
+ * This file contains the interface independent core functions.
+ *
+ * Copyright (C) 2014 Carlo Caione
+ *
+ * Author: Carlo Caione <carlo@caione.org>
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <clock.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/mfd/core.h>
+#include <module.h>
+#include <of.h>
+#include <of_device.h>
+#include <linux/regmap.h>
+#include <regulator.h>
+
+#define AXP20X_OFF BIT(7)
+
+#define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0
+#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
+
+static const char * const axp20x_model_names[] = {
+ "AXP152",
+ "AXP202",
+ "AXP209",
+ "AXP221",
+ "AXP223",
+ "AXP288",
+ "AXP313A",
+ "AXP803",
+ "AXP806",
+ "AXP809",
+ "AXP813",
+};
+
+static const struct regmap_config axp152_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AXP152_PWM1_DUTY_CYCLE,
+};
+
+static const struct regmap_config axp20x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AXP20X_OCV(AXP20X_OCV_MAX),
+};
+
+static const struct regmap_config axp22x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AXP22X_BATLOW_THRES1,
+};
+
+static const struct regmap_config axp288_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .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,
+ .max_register = AXP806_REG_ADDR_EXT,
+};
+
+static const struct mfd_cell axp20x_cells[] = {
+ {
+ .name = "axp20x-gpio",
+ /* .of_compatible = "x-powers,axp209-gpio", */
+ }, {
+ .name = "axp20x-pek",
+ }, {
+ .name = "axp20x-regulator",
+ }, {
+ .name = "axp20x-adc",
+ /* .of_compatible = "x-powers,axp209-adc", */
+ }, {
+ .name = "axp20x-battery-power-supply",
+ /* .of_compatible = "x-powers,axp209-battery-power-supply", */
+ }, {
+ .name = "axp20x-ac-power-supply",
+ /* .of_compatible = "x-powers,axp202-ac-power-supply", */
+ }, {
+ .name = "axp20x-usb-power-supply",
+ /* .of_compatible = "x-powers,axp202-usb-power-supply", */
+ },
+};
+
+static const struct mfd_cell axp221_cells[] = {
+ {
+ .name = "axp221-pek",
+ }, {
+ .name = "axp20x-regulator",
+ }, {
+ .name = "axp22x-adc",
+ /* .of_compatible = "x-powers,axp221-adc", */
+ }, {
+ .name = "axp20x-ac-power-supply",
+ /* .of_compatible = "x-powers,axp221-ac-power-supply", */
+ }, {
+ .name = "axp20x-battery-power-supply",
+ /* .of_compatible = "x-powers,axp221-battery-power-supply", */
+ }, {
+ .name = "axp20x-usb-power-supply",
+ /* .of_compatible = "x-powers,axp221-usb-power-supply", */
+ },
+};
+
+static const struct mfd_cell axp223_cells[] = {
+ {
+ .name = "axp221-pek",
+ }, {
+ .name = "axp22x-adc",
+ /* .of_compatible = "x-powers,axp221-adc", */
+ }, {
+ .name = "axp20x-battery-power-supply",
+ /* .of_compatible = "x-powers,axp221-battery-power-supply", */
+ }, {
+ .name = "axp20x-regulator",
+ }, {
+ .name = "axp20x-ac-power-supply",
+ /* .of_compatible = "x-powers,axp221-ac-power-supply", */
+ }, {
+ .name = "axp20x-usb-power-supply",
+ /* .of_compatible = "x-powers,axp223-usb-power-supply", */
+ },
+};
+
+static const struct mfd_cell axp152_cells[] = {
+ {
+ .name = "axp20x-pek",
+ },
+ {
+ .name = "axp20x-regulator",
+ },
+};
+
+static const struct mfd_cell axp288_cells[] = {
+ {
+ .name = "axp288_adc",
+ }, {
+ .name = "axp288_extcon",
+ }, {
+ .name = "axp288_charger",
+ }, {
+ .name = "axp221-pek",
+ }, {
+ .name = "axp288_pmic_acpi",
+ },
+};
+
+static const struct mfd_cell axp313a_cells[] = {
+ {
+ .name = "axp313a-regulator"
+ },
+};
+
+
+static const struct mfd_cell axp803_cells[] = {
+ {
+ .name = "axp221-pek",
+ }, {
+ .name = "axp20x-gpio",
+ /* .of_compatible = "x-powers,axp813-gpio", */
+ }, {
+ .name = "axp813-adc",
+ /* .of_compatible = "x-powers,axp813-adc", */
+ }, {
+ .name = "axp20x-battery-power-supply",
+ /* .of_compatible = "x-powers,axp813-battery-power-supply", */
+ }, {
+ .name = "axp20x-ac-power-supply",
+ /* .of_compatible = "x-powers,axp813-ac-power-supply", */
+ }, {
+ .name = "axp20x-usb-power-supply",
+ /* .of_compatible = "x-powers,axp813-usb-power-supply", */
+ },
+ { .name = "axp20x-regulator" },
+};
+
+static const struct mfd_cell axp806_cells[] = {
+ {
+ .name = "axp20x-regulator",
+ },
+};
+
+static const struct mfd_cell axp809_cells[] = {
+ {
+ .name = "axp221-pek",
+ }, {
+ .name = "axp20x-regulator",
+ },
+};
+
+static const struct mfd_cell axp813_cells[] = {
+ {
+ .name = "axp221-pek",
+ }, {
+ .name = "axp20x-regulator",
+ }, {
+ .name = "axp20x-gpio",
+ /* .of_compatible = "x-powers,axp813-gpio", */
+ }, {
+ .name = "axp813-adc",
+ /* .of_compatible = "x-powers,axp813-adc", */
+ }, {
+ .name = "axp20x-battery-power-supply",
+ /* .of_compatible = "x-powers,axp813-battery-power-supply", */
+ }, {
+ .name = "axp20x-ac-power-supply",
+ /* .of_compatible = "x-powers,axp813-ac-power-supply", */
+ }, {
+ .name = "axp20x-usb-power-supply",
+ /* .of_compatible = "x-powers,axp813-usb-power-supply", */
+ },
+};
+
+static void axp20x_power_off(struct poweroff_handler *handler)
+{
+ struct axp20x_dev *axp20x = container_of(handler, struct axp20x_dev, poweroff);
+
+ regmap_write(axp20x->regmap, AXP20X_OFF_CTRL, AXP20X_OFF);
+
+ shutdown_barebox();
+
+ /* Give capacitors etc. time to drain to avoid kernel panic msg. */
+ mdelay(500);
+ hang();
+}
+
+int axp20x_match_device(struct axp20x_dev *axp20x)
+{
+ struct device *dev = axp20x->dev;
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(dev->driver->of_compatible, dev);
+ if (!of_id) {
+ dev_err(dev, "Unable to match OF ID\n");
+ return -ENODEV;
+ }
+ axp20x->variant = (long)of_id->data;
+
+ switch (axp20x->variant) {
+ case AXP152_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp152_cells);
+ axp20x->cells = axp152_cells;
+ axp20x->regmap_cfg = &axp152_regmap_config;
+ break;
+ case AXP202_ID:
+ case AXP209_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp20x_cells);
+ axp20x->cells = axp20x_cells;
+ axp20x->regmap_cfg = &axp20x_regmap_config;
+ break;
+ case AXP221_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp221_cells);
+ axp20x->cells = axp221_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ break;
+ case AXP223_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp223_cells);
+ axp20x->cells = axp223_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ break;
+ case AXP288_ID:
+ axp20x->cells = axp288_cells;
+ 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;
+ axp20x->regmap_cfg = &axp288_regmap_config;
+ break;
+ case AXP806_ID:
+ /*
+ * Don't register the power key part if in slave mode or
+ * if there is no interrupt line.
+ */
+ axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
+ axp20x->cells = axp806_cells;
+ axp20x->regmap_cfg = &axp806_regmap_config;
+ break;
+ case AXP809_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp809_cells);
+ axp20x->cells = axp809_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ break;
+ case AXP813_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp813_cells);
+ axp20x->cells = axp813_cells;
+ axp20x->regmap_cfg = &axp288_regmap_config;
+ break;
+ default:
+ dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant);
+ return -EINVAL;
+ }
+ dev_info(dev, "AXP20x variant %s found\n",
+ axp20x_model_names[axp20x->variant]);
+
+ return 0;
+}
+EXPORT_SYMBOL(axp20x_match_device);
+
+int axp20x_device_probe(struct axp20x_dev *axp20x)
+{
+ int ret;
+
+ /*
+ * The AXP806 supports either master/standalone or slave mode.
+ * Slave mode allows sharing the serial bus, even with multiple
+ * AXP806 which all have the same hardware address.
+ *
+ * This is done with extra "serial interface address extension",
+ * or AXP806_BUS_ADDR_EXT, and "register address extension", or
+ * AXP806_REG_ADDR_EXT, registers. The former is read-only, with
+ * 1 bit customizable at the factory, and 1 bit depending on the
+ * state of an external pin. The latter is writable. The device
+ * will only respond to operations to its other registers when
+ * the these device addressing bits (in the upper 4 bits of the
+ * registers) match.
+ *
+ * By default we support an AXP806 chained to an AXP809 in slave
+ * mode. Boards which use an AXP806 in master mode can set the
+ * property "x-powers,master-mode" to override the default.
+ */
+ if (axp20x->variant == AXP806_ID) {
+ if (of_property_read_bool(axp20x->dev->of_node,
+ "x-powers,master-mode") ||
+ 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);
+ else
+ regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
+ 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");
+
+
+ axp20x->poweroff.name = "axp20x-poweroff";
+ axp20x->poweroff.poweroff = axp20x_power_off;
+ axp20x->poweroff.priority = 200;
+
+ if (!(axp20x->variant == AXP288_ID) || (axp20x->variant == AXP313A_ID))
+ poweroff_handler_register(&axp20x->poweroff);
+
+ return 0;
+}
+EXPORT_SYMBOL(axp20x_device_probe);
+
+MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
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 d98a6ed9a7..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,13 +174,13 @@ 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;
int ret, i, current, count = 0;
uint32_t crc[2];
- uint8_t flag[2];
+ uint8_t flag[2] = { FLAG_NONE, FLAG_NONE };
size_t size[2];
void *blob[2] = { NULL, NULL };
uint8_t *data[2];
@@ -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/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..593a7035e5 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -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;
@@ -1186,7 +1186,7 @@ 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/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2cd52f3820..19f4322f65 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -40,7 +40,7 @@ config NAND_IMX
"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.
+ 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
@@ -65,6 +65,7 @@ config NAND_MXS
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
@@ -94,25 +95,41 @@ config NAND_MRVL_NFC
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"
- depends on ARCH_AT91
+ 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 || COMPILE_TEST
+ depends on NAND_ATMEL_LEGACY
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"
default n
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1584e86694..a0207d328b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -16,10 +16,9 @@ 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_STM32) += stm32_fmc2_nand.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_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
diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/atmel/Makefile
new file mode 100644
index 0000000000..0f739c3f31
--- /dev/null
+++ b/drivers/mtd/nand/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/atmel/atmel_nand_ecc.h
index c7864d96dd..c7864d96dd 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/atmel/atmel_nand_ecc.h
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel/legacy.c
index 6a54add93b..cee9e49be0 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/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:
@@ -1195,7 +1195,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;
@@ -1253,7 +1253,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 +1281,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 {
@@ -1336,19 +1336,14 @@ 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.algo = NAND_ECC_ALGO_HAMMING;
}
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;
@@ -1433,7 +1428,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/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c
new file mode 100644
index 0000000000..5188a11cbe
--- /dev/null
+++ b/drivers/mtd/nand/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/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
new file mode 100644
index 0000000000..1b89607a33
--- /dev/null
+++ b/drivers/mtd/nand/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/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h
new file mode 100644
index 0000000000..6178a35e9d
--- /dev/null
+++ b/drivers/mtd/nand/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/denali.h
index 7a721ffbbc..ed489d010b 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/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/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4c90ad9757..2599e8c8c2 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -36,6 +36,7 @@
#include <asm/byteorder.h>
#include <io.h>
#include <malloc.h>
+#include <linux/gpio/consumer.h>
#include <module.h>
#include <of_mtd.h>
@@ -91,11 +92,16 @@ static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
return 0;
}
-const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+static 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);
+
+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)
@@ -127,11 +133,16 @@ static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
return 0;
}
-const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+static 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);
+
+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
@@ -785,6 +796,27 @@ 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 *gpio,
+ unsigned long timeout_ms)
+{
+ return gpiod_poll_timeout_us(gpio, true, timeout_ms * USEC_PER_MSEC);
+};
+EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
+
static bool nand_supports_get_features(struct nand_chip *chip, int addr)
{
return (chip->parameters.supports_set_get_features &&
@@ -4651,8 +4683,8 @@ 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);
+ chip->base.ecc.requirements.strength = NAND_ECC_STRENGTH(type);
+ chip->base.ecc.requirements.step_size = NAND_ECC_STEP(type);
chip->parameters.model = strdup(type->name);
if (!chip->parameters.model)
@@ -4914,9 +4946,9 @@ free_detect_allocation:
}
static const char * const nand_ecc_algos[] = {
- [NAND_ECC_HAMMING] = "hamming",
- [NAND_ECC_BCH] = "bch",
- [NAND_ECC_RS] = "rs",
+ [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)
@@ -4927,7 +4959,7 @@ static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
err = of_property_read_string(np, "nand-ecc-algo", &pm);
if (!err) {
- for (ecc_algo = NAND_ECC_HAMMING;
+ for (ecc_algo = NAND_ECC_ALGO_HAMMING;
ecc_algo < ARRAY_SIZE(nand_ecc_algos);
ecc_algo++) {
if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
@@ -4942,12 +4974,12 @@ static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
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 int nand_dt_init(struct nand_chip *chip)
@@ -4976,7 +5008,7 @@ static int nand_dt_init(struct nand_chip *chip)
if (ecc_mode >= 0)
chip->ecc.mode = ecc_mode;
- if (ecc_algo != NAND_ECC_UNKNOWN)
+ if (ecc_algo != NAND_ECC_ALGO_UNKNOWN)
chip->ecc.algo = ecc_algo;
if (ecc_strength >= 0)
@@ -5105,7 +5137,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
return -EINVAL;
switch (ecc->algo) {
- case NAND_ECC_HAMMING:
+ case NAND_ECC_ALGO_HAMMING:
ecc->calculate = nand_calculate_ecc;
ecc->correct = nand_correct_data;
ecc->read_page = nand_read_page_swecc;
@@ -5126,7 +5158,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
return 0;
- case NAND_ECC_BCH:
+ case NAND_ECC_ALGO_BCH:
if (!mtd_nand_has_bch()) {
WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
return -EINVAL;
@@ -5270,8 +5302,8 @@ nand_match_ecc_req(struct nand_chip *chip,
{
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 = chip->base.ecc.requirements.step_size;
+ int req_strength = chip->base.ecc.requirements.strength;
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
int best_step, best_strength, best_ecc_bytes;
int best_ecc_bytes_total = INT_MAX;
@@ -5464,7 +5496,7 @@ static bool nand_ecc_strength_good(struct nand_chip *chip)
struct nand_ecc_ctrl *ecc = &chip->ecc;
int corr, ds_corr;
- if (ecc->size == 0 || chip->base.eccreq.step_size == 0)
+ if (ecc->size == 0 || chip->base.ecc.requirements.step_size == 0)
/* Not enough information */
return true;
@@ -5473,10 +5505,10 @@ static bool nand_ecc_strength_good(struct nand_chip *chip)
* the correction density.
*/
corr = (mtd->writesize * ecc->strength) / ecc->size;
- ds_corr = (mtd->writesize * chip->base.eccreq.strength) /
- chip->base.eccreq.step_size;
+ ds_corr = (mtd->writesize * chip->base.ecc.requirements.strength) /
+ chip->base.ecc.requirements.step_size;
- return corr >= ds_corr && ecc->strength >= chip->base.eccreq.strength;
+ return corr >= ds_corr && ecc->strength >= chip->base.ecc.requirements.strength;
}
static int rawnand_erase(struct nand_device *nand, const struct nand_pos *pos)
@@ -5566,7 +5598,7 @@ 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->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_ALGO_BCH)) {
switch (mtd->oobsize) {
case 8:
case 16:
@@ -5662,7 +5694,7 @@ 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;
+ ecc->algo = NAND_ECC_ALGO_HAMMING;
case NAND_ECC_SOFT:
ret = nand_set_ecc_soft_ops(chip);
if (ret) {
@@ -5752,8 +5784,8 @@ int nand_scan_tail(struct nand_chip *chip)
if (!nand_ecc_strength_good(chip))
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);
+ chip->base.ecc.requirements.strength,
+ chip->base.ecc.requirements.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)) {
@@ -5913,7 +5945,7 @@ 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)
+ chip->ecc.algo == NAND_ECC_ALGO_BCH)
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
nanddev_cleanup(&chip->base);
diff --git a/drivers/mtd/nand/nand_denali_dt.c b/drivers/mtd/nand/nand_denali_dt.c
index 5cb62cd733..d21cdc9756 100644
--- a/drivers/mtd/nand/nand_denali_dt.c
+++ b/drivers/mtd/nand/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_esmt.c b/drivers/mtd/nand/nand_esmt.c
index 2331eb174e..cd635c27ef 100644
--- a/drivers/mtd/nand/nand_esmt.c
+++ b/drivers/mtd/nand/nand_esmt.c
@@ -14,20 +14,20 @@ static void esmt_nand_decode_id(struct nand_chip *chip)
/* Extract ECC requirements from 5th id byte. */
if (chip->id.len >= 5 && nand_is_slc(chip)) {
- chip->base.eccreq.step_size = 512;
+ chip->base.ecc.requirements.step_size = 512;
switch (chip->id.data[4] & 0x3) {
case 0x0:
- chip->base.eccreq.strength = 4;
+ chip->base.ecc.requirements.strength = 4;
break;
case 0x1:
- chip->base.eccreq.strength = 2;
+ chip->base.ecc.requirements.strength = 2;
break;
case 0x2:
- chip->base.eccreq.strength = 1;
+ chip->base.ecc.requirements.strength = 1;
break;
default:
WARN(1, "Could not get ECC info");
- chip->base.eccreq.step_size = 0;
+ chip->base.ecc.requirements.step_size = 0;
break;
}
}
diff --git a/drivers/mtd/nand/nand_fsl_ifc.c b/drivers/mtd/nand/nand_fsl_ifc.c
index 64dc9c225f..3b14b4ae15 100644
--- a/drivers/mtd/nand/nand_fsl_ifc.c
+++ b/drivers/mtd/nand/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 */
@@ -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 */
@@ -964,7 +964,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
} else {
nand->ecc.mode = NAND_ECC_SOFT;
- nand->ecc.algo = NAND_ECC_HAMMING;
+ 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/nand_hynix.c
index 0422ed53aa..fef9207495 100644
--- a/drivers/mtd/nand/nand_hynix.c
+++ b/drivers/mtd/nand/nand_hynix.c
@@ -498,30 +498,30 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
if (valid_jedecid) {
/* Reference: H27UCG8T2E datasheet */
- chip->base.eccreq.step_size = 1024;
+ chip->base.ecc.requirements.step_size = 1024;
switch (ecc_level) {
case 0:
- chip->base.eccreq.step_size = 0;
- chip->base.eccreq.strength = 0;
+ chip->base.ecc.requirements.step_size = 0;
+ chip->base.ecc.requirements.strength = 0;
break;
case 1:
- chip->base.eccreq.strength = 4;
+ chip->base.ecc.requirements.strength = 4;
break;
case 2:
- chip->base.eccreq.strength = 24;
+ chip->base.ecc.requirements.strength = 24;
break;
case 3:
- chip->base.eccreq.strength = 32;
+ chip->base.ecc.requirements.strength = 32;
break;
case 4:
- chip->base.eccreq.strength = 40;
+ chip->base.ecc.requirements.strength = 40;
break;
case 5:
- chip->base.eccreq.strength = 50;
+ chip->base.ecc.requirements.strength = 50;
break;
case 6:
- chip->base.eccreq.strength = 60;
+ chip->base.ecc.requirements.strength = 60;
break;
default:
/*
@@ -542,14 +542,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;
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1 << ecc_level;
} else if (ecc_level < 7) {
if (ecc_level == 5)
- chip->base.eccreq.step_size = 2048;
+ chip->base.ecc.requirements.step_size = 2048;
else
- chip->base.eccreq.step_size = 1024;
- chip->base.eccreq.strength = 24;
+ chip->base.ecc.requirements.step_size = 1024;
+ chip->base.ecc.requirements.strength = 24;
} else {
/*
* We should never reach this case, but if that
@@ -562,14 +562,14 @@ 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;
+ chip->base.ecc.requirements.step_size = 0;
+ chip->base.ecc.requirements.strength = 0;
} else if (ecc_level < 5) {
- chip->base.eccreq.step_size = 512;
- chip->base.eccreq.strength = 1 << (ecc_level - 1);
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1 << (ecc_level - 1);
} else {
- chip->base.eccreq.step_size = 1024;
- chip->base.eccreq.strength = 24 +
+ chip->base.ecc.requirements.step_size = 1024;
+ chip->base.ecc.requirements.strength = 24 +
(8 * (ecc_level - 5));
}
}
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index 66c53670d3..23b9c52e0f 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -18,8 +18,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/rawnand.h>
#include <linux/clk.h>
-#include <mach/generic.h>
-#include <mach/imx-nand.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx-nand.h>
#include <io.h>
#include <of_mtd.h>
#include <errno.h>
@@ -27,7 +27,7 @@
struct imx_nand_host {
struct nand_chip nand;
struct mtd_partition *parts;
- struct device_d *dev;
+ struct device *dev;
void *spare0;
void *main_area0;
@@ -1106,7 +1106,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {
static int __init mxcnd_probe_dt(struct imx_nand_host *host)
{
- struct device_node *np = host->dev->device_node;
+ struct device_node *np = host->dev->of_node;
int buswidth;
if (!IS_ENABLED(CONFIG_OFDEVICE))
@@ -1240,7 +1240,7 @@ out:
* @return The function always returns 0.
*/
-static int __init imxnd_probe(struct device_d *dev)
+static int __init imxnd_probe(struct device *dev)
{
struct resource *iores;
struct nand_chip *this;
@@ -1480,8 +1480,9 @@ static __maybe_unused struct of_device_id imx_nand_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_nand_compatible);
-static struct driver_d imx_nand_driver = {
+static struct driver imx_nand_driver = {
.name = "imx_nand",
.probe = imxnd_probe,
.of_compatible = DRV_OF_COMPAT(imx_nand_compatible),
diff --git a/drivers/mtd/nand/nand_jedec.c b/drivers/mtd/nand/nand_jedec.c
index 48997bd5f5..2b21e2d5b5 100644
--- a/drivers/mtd/nand/nand_jedec.c
+++ b/drivers/mtd/nand/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_micron.c b/drivers/mtd/nand/nand_micron.c
index d59be7ca7b..758316e681 100644
--- a/drivers/mtd/nand/nand_micron.c
+++ b/drivers/mtd/nand/nand_micron.c
@@ -426,7 +426,8 @@ 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 (chip->base.ecc.requirements.strength != 4 &&
+ chip->base.ecc.requirements.strength != 8)
return MICRON_ON_DIE_UNSUPPORTED;
/* 0x2 means on-die ECC is available. */
@@ -467,7 +468,8 @@ 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 (chip->base.ecc.requirements.strength != 4 &&
+ chip->base.ecc.requirements.strength != 8)
return MICRON_ON_DIE_UNSUPPORTED;
return MICRON_ON_DIE_SUPPORTED;
@@ -524,7 +526,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 (chip->base.ecc.requirements.strength == 4) {
micron->ecc.rawbuf = kmalloc(mtd->writesize +
mtd->oobsize,
GFP_KERNEL);
@@ -534,17 +536,17 @@ static int micron_nand_init(struct nand_chip *chip)
}
}
- if (chip->base.eccreq.strength == 4)
+ if (chip->base.ecc.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 = chip->base.ecc.requirements.strength * 2;
chip->ecc.size = 512;
- chip->ecc.strength = chip->base.eccreq.strength;
- chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.strength = chip->base.ecc.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/nand_mrvl_nfc.c
index 064853b344..27ca4456c8 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/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)
@@ -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/nand_mxs.c
index 6f707f9b17..c2a7d036d6 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/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;
@@ -2272,11 +2300,15 @@ static __maybe_unused struct of_device_id gpmi_dt_ids[] = {
.compatible = "fsl,imx6q-gpmi-nand",
.data = (void *)GPMI_IMX6,
}, {
+ .compatible = "fsl,imx7d-gpmi-nand",
+ .data = (void *)GPMI_IMX6,
+ }, {
/* 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_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 9c4f267196..ab36183005 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/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;
@@ -1184,7 +1184,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 +1343,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/nand_onfi.c
index c6b187be02..5dd29ba6ba 100644
--- a/drivers/mtd/nand/nand_onfi.c
+++ b/drivers/mtd/nand/nand_onfi.c
@@ -95,8 +95,8 @@ 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;
+ chip->base.ecc.requirements.strength = ecc->ecc_bits;
+ chip->base.ecc.requirements.step_size = 1 << ecc->codeword_size;
ret = 0;
ext_out:
@@ -266,8 +266,8 @@ 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;
+ chip->base.ecc.requirements.strength = p->ecc_bits;
+ chip->base.ecc.requirements.step_size = 512;
} else if (onfi_version >= 21 &&
(le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index bb41b006e2..ff3642939a 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/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;
@@ -147,8 +147,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_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/nand_samsung.c b/drivers/mtd/nand/nand_samsung.c
index 3a4a19e808..ee993af1e5 100644
--- a/drivers/mtd/nand/nand_samsung.c
+++ b/drivers/mtd/nand/nand_samsung.c
@@ -71,23 +71,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;
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1 << extid;
} else {
- chip->base.eccreq.step_size = 1024;
+ chip->base.ecc.requirements.step_size = 1024;
switch (extid) {
case 5:
- chip->base.eccreq.strength = 24;
+ chip->base.ecc.requirements.strength = 24;
break;
case 6:
- chip->base.eccreq.strength = 40;
+ chip->base.ecc.requirements.strength = 40;
break;
case 7:
- chip->base.eccreq.strength = 60;
+ chip->base.ecc.requirements.strength = 60;
break;
default:
WARN(1, "Could not decode ECC info");
- chip->base.eccreq.step_size = 0;
+ chip->base.ecc.requirements.step_size = 0;
}
}
} else {
@@ -97,8 +97,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;
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1;
break;
/* K9F1G08U0E 21nm chips do not support subpage write */
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c
index 21a5dbc7e0..3fe0347bfd 100644
--- a/drivers/mtd/nand/nand_toshiba.c
+++ b/drivers/mtd/nand/nand_toshiba.c
@@ -140,7 +140,7 @@ 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)
@@ -175,20 +175,20 @@ 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;
+ chip->base.ecc.requirements.step_size = 512;
switch (chip->id.data[5] & 0x7) {
case 0x4:
- chip->base.eccreq.strength = 1;
+ chip->base.ecc.requirements.strength = 1;
break;
case 0x5:
- chip->base.eccreq.strength = 4;
+ chip->base.ecc.requirements.strength = 4;
break;
case 0x6:
- chip->base.eccreq.strength = 8;
+ chip->base.ecc.requirements.strength = 8;
break;
default:
WARN(1, "Could not get ECC info");
- chip->base.eccreq.step_size = 0;
+ chip->base.ecc.requirements.step_size = 0;
break;
}
}
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 8f58f5997c..940ed9809e 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/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;
@@ -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/omap_elm.c
index 583235fc78..da731e44f3 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/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/stm32_fmc2_nand.c b/drivers/mtd/nand/stm32_fmc2_nand.c
new file mode 100644
index 0000000000..47b012cc9e
--- /dev/null
+++ b/drivers/mtd/nand/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.mode = NAND_ECC_HW;
+ 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..ac46575004 100644
--- a/drivers/mtd/nor/cfi_flash.c
+++ b/drivers/mtd/nor/cfi_flash.c
@@ -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/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..1773db09a1 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 {
@@ -680,7 +679,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 +867,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 +1170,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 +1380,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 341d02a1da..13e9ff6924 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -40,6 +40,12 @@ config DRIVER_NET_AT91_ETHER
depends on HAS_AT91_ETHER
select PHYLIB
+config DRIVER_NET_BCMGENET
+ bool "BCMGENET V5 support"
+ select PHYLIB
+ help
+ This driver supports the BCMGENET Ethernet MAC.
+
config DRIVER_NET_CS8900
bool "cs8900 ethernet driver"
depends on HAS_CS8900 || COMPILE_TEST
@@ -101,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)
@@ -173,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
@@ -226,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
@@ -237,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.
@@ -261,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"
@@ -277,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
@@ -310,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.
@@ -333,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 fa3d4583a0..207345cfa3 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_NET_USB) += usb/
obj-$(CONFIG_DRIVER_NET_AR231X) += ar231x.o
obj-$(CONFIG_DRIVER_NET_ARC_EMAC) += arc_emac.o
obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_DRIVER_NET_BCMGENET) += bcmgenet.o
obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o
obj-$(CONFIG_DRIVER_NET_CPSW) += cpsw.o
obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC) += davinci_emac.o
@@ -14,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
@@ -23,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
@@ -33,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
new file mode 100644
index 0000000000..9e0bacb31a
--- /dev/null
+++ b/drivers/net/bcmgenet.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Amit Singh Tomar <amittomer25@gmail.com>
+ *
+ * Driver for Broadcom GENETv5 Ethernet controller (as found on the RPi4)
+ * This driver is based on the Linux driver:
+ * drivers/net/ethernet/broadcom/genet/bcmgenet.c
+ * which is: Copyright (c) 2014-2017 Broadcom
+ *
+ * The hardware supports multiple queues (16 priority queues and one
+ * default queue), both for RX and TX. There are 256 DMA descriptors (both
+ * for TX and RX), and they live in MMIO registers. The hardware allows
+ * assigning descriptor ranges to queues, but we choose the most simple setup:
+ * All 256 descriptors are assigned to the default queue (#16).
+ * Also the Linux driver supports multiple generations of the MAC, whereas
+ * we only support v5, as used in the Raspberry Pi 4.
+ */
+
+#include <common.h>
+#include <dma.h>
+#include <malloc.h>
+#include <net.h>
+#include <init.h>
+#include <driver.h>
+#include <io.h>
+#include <clock.h>
+#include <xfuncs.h>
+#include <linux/phy.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <of_net.h>
+#include <linux/iopoll.h>
+
+/* Register definitions derived from Linux source */
+#define SYS_REV_CTRL 0x00
+
+#define SYS_PORT_CTRL 0x04
+#define PORT_MODE_EXT_GPHY 3
+
+#define GENET_SYS_OFF 0x0000
+#define SYS_RBUF_FLUSH_CTRL (GENET_SYS_OFF + 0x08)
+#define SYS_TBUF_FLUSH_CTRL (GENET_SYS_OFF + 0x0c)
+
+#define GENET_EXT_OFF 0x0080
+#define EXT_RGMII_OOB_CTRL (GENET_EXT_OFF + 0x0c)
+#define RGMII_LINK BIT(4)
+#define OOB_DISABLE BIT(5)
+#define RGMII_MODE_EN BIT(6)
+#define ID_MODE_DIS BIT(16)
+
+#define GENET_RBUF_OFF 0x0300
+#define RBUF_TBUF_SIZE_CTRL (GENET_RBUF_OFF + 0xb4)
+#define RBUF_CTRL (GENET_RBUF_OFF + 0x00)
+#define RBUF_ALIGN_2B BIT(1)
+
+#define GENET_UMAC_OFF 0x0800
+#define UMAC_MIB_CTRL (GENET_UMAC_OFF + 0x580)
+#define UMAC_MAX_FRAME_LEN (GENET_UMAC_OFF + 0x014)
+#define UMAC_MAC0 (GENET_UMAC_OFF + 0x00c)
+#define UMAC_MAC1 (GENET_UMAC_OFF + 0x010)
+#define UMAC_CMD (GENET_UMAC_OFF + 0x008)
+#define MDIO_CMD (GENET_UMAC_OFF + 0x614)
+#define UMAC_TX_FLUSH (GENET_UMAC_OFF + 0x334)
+#define MDIO_START_BUSY BIT(29)
+#define MDIO_READ_FAIL BIT(28)
+#define MDIO_RD (2 << 26)
+#define MDIO_WR BIT(26)
+#define MDIO_PMD_SHIFT 21
+#define MDIO_PMD_MASK 0x1f
+#define MDIO_REG_SHIFT 16
+#define MDIO_REG_MASK 0x1f
+
+#define CMD_TX_EN BIT(0)
+#define CMD_RX_EN BIT(1)
+#define UMAC_SPEED_10 0
+#define UMAC_SPEED_100 1
+#define UMAC_SPEED_1000 2
+#define UMAC_SPEED_2500 3
+#define CMD_SPEED_SHIFT 2
+#define CMD_SPEED_MASK 3
+#define CMD_SW_RESET BIT(13)
+#define CMD_LCL_LOOP_EN BIT(15)
+#define CMD_TX_EN BIT(0)
+#define CMD_RX_EN BIT(1)
+
+#define MIB_RESET_RX BIT(0)
+#define MIB_RESET_RUNT BIT(1)
+#define MIB_RESET_TX BIT(2)
+
+/* total number of Buffer Descriptors, same for Rx/Tx */
+#define TOTAL_DESCS 256
+#define RX_DESCS TOTAL_DESCS
+#define TX_DESCS TOTAL_DESCS
+
+#define DEFAULT_Q 0x10
+
+#define ENET_MAX_MTU_SIZE 1536
+
+/* Tx/Rx Dma Descriptor common bits */
+#define DMA_EN BIT(0)
+#define DMA_RING_BUF_EN_SHIFT 0x01
+#define DMA_RING_BUF_EN_MASK 0xffff
+#define DMA_BUFLENGTH_MASK 0x0fff
+#define DMA_BUFLENGTH_SHIFT 16
+#define DMA_RING_SIZE_SHIFT 16
+#define DMA_OWN 0x8000
+#define DMA_EOP 0x4000
+#define DMA_SOP 0x2000
+#define DMA_WRAP 0x1000
+#define DMA_MAX_BURST_LENGTH 0x8
+/* Tx specific DMA descriptor bits */
+#define DMA_TX_UNDERRUN 0x0200
+#define DMA_TX_APPEND_CRC 0x0040
+#define DMA_TX_OW_CRC 0x0020
+#define DMA_TX_DO_CSUM 0x0010
+#define DMA_TX_QTAG_SHIFT 7
+
+/* DMA rings size */
+#define DMA_RING_SIZE 0x40
+#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DEFAULT_Q + 1))
+
+/* DMA descriptor */
+#define DMA_DESC_LENGTH_STATUS 0x00
+#define DMA_DESC_ADDRESS_LO 0x04
+#define DMA_DESC_ADDRESS_HI 0x08
+#define DMA_DESC_SIZE 12
+
+#define GENET_RX_OFF 0x2000
+#define GENET_RDMA_REG_OFF 0x2c00
+#define GENET_TX_OFF 0x4000
+#define GENET_TDMA_REG_OFF 0x4c00
+
+#define DMA_FC_THRESH_HI (RX_DESCS >> 4)
+#define DMA_FC_THRESH_LO 5
+#define DMA_FC_THRESH_VALUE ((DMA_FC_THRESH_LO << 16) | \
+ DMA_FC_THRESH_HI)
+
+#define DMA_XOFF_THRESHOLD_SHIFT 16
+
+#define TDMA_RING_REG_BASE 0x5000
+#define TDMA_READ_PTR (TDMA_RING_REG_BASE + 0x00)
+#define TDMA_CONS_INDEX (TDMA_RING_REG_BASE + 0x08)
+#define TDMA_PROD_INDEX (TDMA_RING_REG_BASE + 0x0c)
+#define DMA_RING_BUF_SIZE 0x10
+#define DMA_START_ADDR 0x14
+#define DMA_END_ADDR 0x1c
+#define DMA_MBUF_DONE_THRESH 0x24
+#define TDMA_FLOW_PERIOD (TDMA_RING_REG_BASE + 0x28)
+#define TDMA_WRITE_PTR (TDMA_RING_REG_BASE + 0x2c)
+
+#define RDMA_RING_REG_BASE 0x3000
+#define RDMA_WRITE_PTR (RDMA_RING_REG_BASE + 0x00)
+#define RDMA_PROD_INDEX (RDMA_RING_REG_BASE + 0x08)
+#define RDMA_CONS_INDEX (RDMA_RING_REG_BASE + 0x0c)
+#define RDMA_XON_XOFF_THRESH (RDMA_RING_REG_BASE + 0x28)
+#define RDMA_READ_PTR (RDMA_RING_REG_BASE + 0x2c)
+
+#define TDMA_REG_BASE 0x5040
+#define RDMA_REG_BASE 0x3040
+#define DMA_RING_CFG 0x00
+#define DMA_CTRL 0x04
+#define DMA_SCB_BURST_SIZE 0x0c
+
+#define RX_BUF_LENGTH 2048
+#define RX_TOTAL_BUFSIZE (RX_BUF_LENGTH * RX_DESCS)
+#define RX_BUF_OFFSET 2
+
+struct bcmgenet_eth_priv {
+ char *rxbuffer;
+ void *mac_reg;
+ int tx_index;
+ int rx_index;
+ int c_index;
+ u32 interface;
+ struct mii_bus miibus;
+ struct eth_device edev;
+ struct device *dev;
+ unsigned char addr[6];
+};
+
+static void bcmgenet_umac_reset(struct bcmgenet_eth_priv *priv)
+{
+ u32 reg;
+
+ reg = readl(priv->mac_reg + SYS_RBUF_FLUSH_CTRL);
+ reg |= BIT(1);
+ writel(reg, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL));
+ udelay(10);
+
+ reg &= ~BIT(1);
+ writel(reg, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL));
+ udelay(10);
+
+ writel(0, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL));
+ udelay(10);
+
+ writel(0, priv->mac_reg + UMAC_CMD);
+
+ writel(CMD_SW_RESET | CMD_LCL_LOOP_EN, priv->mac_reg + UMAC_CMD);
+ udelay(2);
+ writel(0, priv->mac_reg + UMAC_CMD);
+
+ /* clear tx/rx counter */
+ writel(MIB_RESET_RX | MIB_RESET_TX | MIB_RESET_RUNT,
+ priv->mac_reg + UMAC_MIB_CTRL);
+ writel(0, priv->mac_reg + UMAC_MIB_CTRL);
+
+ writel(ENET_MAX_MTU_SIZE, priv->mac_reg + UMAC_MAX_FRAME_LEN);
+
+ /* init rx registers, enable ip header optimization */
+ reg = readl(priv->mac_reg + RBUF_CTRL);
+ reg |= RBUF_ALIGN_2B;
+ writel(reg, (priv->mac_reg + RBUF_CTRL));
+
+ writel(1, (priv->mac_reg + RBUF_TBUF_SIZE_CTRL));
+}
+
+static int __bcmgenet_set_hwaddr(struct bcmgenet_eth_priv *priv)
+{
+ const unsigned char *addr = priv->addr;
+ u32 reg;
+
+ reg = addr[0] << 24 | addr[1] << 16 | addr[2] << 8 | addr[3];
+ writel_relaxed(reg, priv->mac_reg + UMAC_MAC0);
+
+ reg = addr[4] << 8 | addr[5];
+ writel_relaxed(reg, priv->mac_reg + UMAC_MAC1);
+
+ 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;
+}
+
+static void bcmgenet_disable_dma(struct bcmgenet_eth_priv *priv)
+{
+ clrbits_le32(priv->mac_reg + TDMA_REG_BASE + DMA_CTRL, DMA_EN);
+ clrbits_le32(priv->mac_reg + RDMA_REG_BASE + DMA_CTRL, DMA_EN);
+
+ writel(1, priv->mac_reg + UMAC_TX_FLUSH);
+ udelay(10);
+ writel(0, priv->mac_reg + UMAC_TX_FLUSH);
+}
+
+static void bcmgenet_enable_dma(struct bcmgenet_eth_priv *priv)
+{
+ u32 dma_ctrl = (1 << (DEFAULT_Q + DMA_RING_BUF_EN_SHIFT)) | DMA_EN;
+
+ writel(dma_ctrl, priv->mac_reg + TDMA_REG_BASE + DMA_CTRL);
+ setbits_le32(priv->mac_reg + RDMA_REG_BASE + DMA_CTRL, dma_ctrl);
+}
+
+static int bcmgenet_gmac_eth_send(struct eth_device *edev, void *packet, int length)
+{
+ struct bcmgenet_eth_priv *priv = edev->priv;
+ void *desc_base = priv->mac_reg + GENET_TX_OFF + priv->tx_index * DMA_DESC_SIZE;
+ u32 len_stat = length << DMA_BUFLENGTH_SHIFT;
+ u32 prod_index, cons;
+ u32 tries = 100;
+ dma_addr_t dma;
+
+ prod_index = readl(priv->mac_reg + TDMA_PROD_INDEX);
+
+ dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->dev, dma))
+ return -EFAULT;
+
+ len_stat |= 0x3f << DMA_TX_QTAG_SHIFT;
+ len_stat |= DMA_TX_APPEND_CRC | DMA_SOP | DMA_EOP;
+
+ /* Set-up packet for transmission */
+ writel(lower_32_bits(dma), (desc_base + DMA_DESC_ADDRESS_LO));
+ writel(upper_32_bits(dma), (desc_base + DMA_DESC_ADDRESS_HI));
+ writel(len_stat, (desc_base + DMA_DESC_LENGTH_STATUS));
+
+ /* Increment index and start transmission */
+ if (++priv->tx_index >= TX_DESCS)
+ priv->tx_index = 0;
+
+ prod_index++;
+
+ /* Start Transmisson */
+ writel(prod_index, priv->mac_reg + TDMA_PROD_INDEX);
+
+ do {
+ cons = readl(priv->mac_reg + TDMA_CONS_INDEX);
+ } while ((cons & 0xffff) < prod_index && --tries);
+
+ dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE);
+
+ if (!tries) {
+ dev_err(priv->dev, "sending timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+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, addr_lo, addr_hi;
+ dma_addr_t addr;
+
+ if (prod_index == priv->c_index)
+ return -EAGAIN;
+
+ length = readl(desc_base + DMA_DESC_LENGTH_STATUS);
+ 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 = (u64)addr_hi << 32 | addr_lo;
+
+ 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
+ * RBUF_ALIGN_2B
+ */
+ net_receive(edev, (void *)addr + RX_BUF_OFFSET, length - RX_BUF_OFFSET);
+
+ 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;
+ writel(priv->c_index, priv->mac_reg + RDMA_CONS_INDEX);
+
+ /* Forward our descriptor pointer, wrapping around if needed. */
+ if (++priv->rx_index >= RX_DESCS)
+ priv->rx_index = 0;
+
+ return 0;
+}
+
+static void rx_descs_init(struct bcmgenet_eth_priv *priv)
+{
+ char *rxbuffs = priv->rxbuffer;
+ u32 len_stat, i;
+ void *desc_base = priv->mac_reg + GENET_RX_OFF;
+
+ 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);
+ }
+}
+
+static void rx_ring_init(struct bcmgenet_eth_priv *priv)
+{
+ writel(DMA_MAX_BURST_LENGTH,
+ priv->mac_reg + RDMA_REG_BASE + DMA_SCB_BURST_SIZE);
+
+ writel(0x0, priv->mac_reg + RDMA_RING_REG_BASE + DMA_START_ADDR);
+ writel(0x0, priv->mac_reg + RDMA_READ_PTR);
+ writel(0x0, priv->mac_reg + RDMA_WRITE_PTR);
+ writel(RX_DESCS * DMA_DESC_SIZE / 4 - 1,
+ priv->mac_reg + RDMA_RING_REG_BASE + DMA_END_ADDR);
+
+ /* cannot init RDMA_PROD_INDEX to 0, so align RDMA_CONS_INDEX on it instead */
+ priv->c_index = readl(priv->mac_reg + RDMA_PROD_INDEX);
+ writel(priv->c_index, priv->mac_reg + RDMA_CONS_INDEX);
+ priv->rx_index = priv->c_index;
+ priv->rx_index &= 0xff;
+ writel((RX_DESCS << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH,
+ priv->mac_reg + RDMA_RING_REG_BASE + DMA_RING_BUF_SIZE);
+ writel(DMA_FC_THRESH_VALUE, priv->mac_reg + RDMA_XON_XOFF_THRESH);
+ writel(1 << DEFAULT_Q, priv->mac_reg + RDMA_REG_BASE + DMA_RING_CFG);
+}
+
+static void tx_ring_init(struct bcmgenet_eth_priv *priv)
+{
+ writel(DMA_MAX_BURST_LENGTH,
+ priv->mac_reg + TDMA_REG_BASE + DMA_SCB_BURST_SIZE);
+
+ writel(0x0, priv->mac_reg + TDMA_RING_REG_BASE + DMA_START_ADDR);
+ writel(0x0, priv->mac_reg + TDMA_READ_PTR);
+ writel(0x0, priv->mac_reg + TDMA_WRITE_PTR);
+ writel(TX_DESCS * DMA_DESC_SIZE / 4 - 1,
+ priv->mac_reg + TDMA_RING_REG_BASE + DMA_END_ADDR);
+ /* cannot init TDMA_CONS_INDEX to 0, so align TDMA_PROD_INDEX on it instead */
+ priv->tx_index = readl(priv->mac_reg + TDMA_CONS_INDEX);
+ writel(priv->tx_index, priv->mac_reg + TDMA_PROD_INDEX);
+ priv->tx_index &= 0xFF;
+ writel(0x1, priv->mac_reg + TDMA_RING_REG_BASE + DMA_MBUF_DONE_THRESH);
+ writel(0x0, priv->mac_reg + TDMA_FLOW_PERIOD);
+ writel((TX_DESCS << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH,
+ priv->mac_reg + TDMA_RING_REG_BASE + DMA_RING_BUF_SIZE);
+
+ writel(1 << DEFAULT_Q, priv->mac_reg + TDMA_REG_BASE + DMA_RING_CFG);
+}
+
+static void bcmgenet_adjust_link(struct eth_device *edev)
+{
+ struct bcmgenet_eth_priv *priv = edev->priv;
+ struct phy_device *phy_dev = edev->phydev;
+ u32 speed;
+
+ switch (phy_dev->speed) {
+ case SPEED_1000:
+ speed = UMAC_SPEED_1000;
+ break;
+ case SPEED_100:
+ speed = UMAC_SPEED_100;
+ break;
+ case SPEED_10:
+ speed = UMAC_SPEED_10;
+ break;
+ default:
+ dev_err(priv->dev, "bcmgenet: Unsupported PHY speed: %d\n", phy_dev->speed);
+ return;
+ }
+
+ clrsetbits_le32(priv->mac_reg + EXT_RGMII_OOB_CTRL, OOB_DISABLE,
+ RGMII_LINK | RGMII_MODE_EN);
+
+ if (phy_dev->interface == PHY_INTERFACE_MODE_RGMII ||
+ phy_dev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ setbits_le32(priv->mac_reg + EXT_RGMII_OOB_CTRL, ID_MODE_DIS);
+ writel(PORT_MODE_EXT_GPHY, priv->mac_reg + SYS_PORT_CTRL);
+ }
+
+ clrsetbits_le32(priv->mac_reg + UMAC_CMD, CMD_SPEED_MASK << CMD_SPEED_SHIFT,
+ speed << CMD_SPEED_SHIFT);
+}
+
+static int bcmgenet_gmac_eth_start(struct eth_device *edev)
+{
+ struct bcmgenet_eth_priv *priv = edev->priv;
+ int ret;
+
+ bcmgenet_umac_reset(priv);
+
+ __bcmgenet_set_hwaddr(priv);
+
+ /* Disable RX/TX DMA and flush TX queues */
+ bcmgenet_disable_dma(priv);
+
+ rx_ring_init(priv);
+ rx_descs_init(priv);
+ tx_ring_init(priv);
+ bcmgenet_enable_dma(priv);
+
+ ret = phy_device_connect(edev, &priv->miibus, -1,
+ bcmgenet_adjust_link, 0,
+ priv->interface);
+ if (ret)
+ return ret;
+
+ /* Enable Rx/Tx */
+ setbits_le32(priv->mac_reg + UMAC_CMD, CMD_TX_EN | CMD_RX_EN);
+
+ return 0;
+}
+
+static void bcmgenet_mdio_start(struct bcmgenet_eth_priv *priv)
+{
+ setbits_le32(priv->mac_reg + MDIO_CMD, MDIO_START_BUSY);
+}
+
+static int bcmgenet_mdio_write(struct mii_bus *bus, int addr,
+ int reg, u16 value)
+{
+ struct bcmgenet_eth_priv *priv = bus->priv;
+ u32 val;
+
+ /* Prepare the read operation */
+ val = MDIO_WR | (addr << MDIO_PMD_SHIFT) |
+ (reg << MDIO_REG_SHIFT) | (0xffff & value);
+ writel_relaxed(val, priv->mac_reg + MDIO_CMD);
+
+ /* Start MDIO transaction */
+ bcmgenet_mdio_start(priv);
+
+ return readl_poll_timeout(priv->mac_reg + MDIO_CMD, reg,
+ !(reg & MDIO_START_BUSY), 20);
+}
+
+static int bcmgenet_mdio_read(struct mii_bus *bus, int addr, int reg)
+{
+ struct bcmgenet_eth_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ /* Prepare the read operation */
+ val = MDIO_RD | (addr << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
+ writel_relaxed(val, priv->mac_reg + MDIO_CMD);
+
+ /* Start MDIO transaction */
+ bcmgenet_mdio_start(priv);
+
+ ret = readl_poll_timeout(priv->mac_reg + MDIO_CMD, reg,
+ !(reg & MDIO_START_BUSY), 20);
+ if (ret)
+ return ret;
+
+ val = readl_relaxed(priv->mac_reg + MDIO_CMD);
+
+ return val & 0xffff;
+}
+
+static int bcmgenet_probe(struct device *dev)
+{
+ struct resource *iores;
+ struct bcmgenet_eth_priv *priv;
+ u32 reg;
+ int ret;
+ u8 major;
+ struct eth_device *edev;
+
+ priv = xzalloc(sizeof(*priv));
+ edev = &priv->edev;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ ret = PTR_ERR(iores);
+ return ret;
+ }
+ priv->mac_reg = IOMEM(iores->start);
+ priv->dev = dev;
+ priv->rxbuffer = dma_alloc(RX_TOTAL_BUFSIZE);
+
+ edev->open = bcmgenet_gmac_eth_start;
+ edev->send = bcmgenet_gmac_eth_send;
+ edev->recv = bcmgenet_gmac_eth_recv;
+ edev->get_ethaddr = bcmgenet_get_hwaddr;
+ edev->set_ethaddr = bcmgenet_set_hwaddr;
+ edev->parent = dev;
+ edev->priv = priv;
+ dev->priv = priv;
+
+ /* Read GENET HW version */
+ reg = readl_relaxed(priv->mac_reg + SYS_REV_CTRL);
+ major = (reg >> 24) & 0x0f;
+ if (major != 6) {
+ if (major == 5)
+ major = 4;
+ else if (major == 0)
+ major = 1;
+
+ dev_err(priv->dev, "Unsupported GENETv%d.%d\n", major, (reg >> 16) & 0x0f);
+ return -ENODEV;
+ }
+
+ ret = of_get_phy_mode(dev->of_node);
+ if (ret < 0)
+ priv->interface = PHY_INTERFACE_MODE_MII;
+ else
+ priv->interface = ret;
+
+ writel(0, priv->mac_reg + SYS_RBUF_FLUSH_CTRL);
+ udelay(10);
+ /* disable MAC while updating its registers */
+ writel(0, priv->mac_reg + UMAC_CMD);
+ /* issue soft reset with (rg)mii loopback to ensure a stable rxclk */
+ writel(CMD_SW_RESET | CMD_LCL_LOOP_EN, priv->mac_reg + UMAC_CMD);
+
+ priv->miibus.read = bcmgenet_mdio_read;
+ priv->miibus.write = bcmgenet_mdio_write;
+
+ 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)
+ return ret;
+
+ ret = eth_register(edev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void bcmgenet_gmac_eth_stop(struct bcmgenet_eth_priv *priv)
+{
+ clrbits_le32(priv->mac_reg + UMAC_CMD, CMD_TX_EN | CMD_RX_EN);
+
+ bcmgenet_disable_dma(priv);
+}
+
+static void bcmgenet_remove(struct device *dev)
+{
+ struct bcmgenet_eth_priv *priv = dev->priv;
+
+ bcmgenet_gmac_eth_stop(priv);
+}
+
+static struct of_device_id bcmgenet_ids[] = {
+ {
+ .compatible = "brcm,genet-v5",
+ }, {
+ .compatible = "brcm,bcm2711-genet-v5",
+ }, {
+ /* sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(of, bcmgenet_ids);
+
+static struct driver bcmgenet_driver = {
+ .name = "brcm-genet",
+ .probe = bcmgenet_probe,
+ .remove = bcmgenet_remove,
+ .of_compatible = DRV_OF_COMPAT(bcmgenet_ids),
+};
+device_platform_driver(bcmgenet_driver);
diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c
index 73671ae2d4..31ca61a230 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
@@ -175,7 +175,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 +195,7 @@ struct cpdma_chan {
};
struct cpsw_priv {
- struct device_d *dev;
+ struct device *dev;
u32 version;
struct cpsw_platform_data data;
@@ -215,6 +215,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 +225,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 +583,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 +622,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 +655,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 +804,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 +824,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 +854,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;
@@ -864,6 +868,8 @@ static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan,
if (len)
*len = status & 0x7ff;
+ if (dma)
+ *dma = readl(&desc->hw_buffer);
if (buffer)
*buffer = (void *)readl(&desc->sw_buffer);
@@ -915,7 +921,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 +985,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 +1024,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 +1047,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 +1075,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 +1242,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 +1250,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 +1278,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 +1311,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 +1339,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 +1355,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 +1427,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 +1450,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/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..5262928480 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,9 +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;
+ rx_buf_pool = dma_alloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE);
@@ -633,18 +630,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 +911,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 +1058,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 +1087,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 +1121,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 +1149,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 +1178,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 +1209,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 +1240,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 +1258,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 +1272,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 +1287,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 +1310,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 +1332,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 +1347,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..f5b2fa74dc 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 = xmalloc(PKTSIZE);
+
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);
+
+ free(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 99d23ffedf..eed7c779e7 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -19,16 +19,68 @@
#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>
+#include <pinctrl.h>
#define DEFAULT_GPIO_RESET_ASSERT 1000 /* us */
#define DEFAULT_GPIO_RESET_DEASSERT 1000 /* us */
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;
@@ -67,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;
@@ -83,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"
@@ -175,35 +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_mdiobus_reset_phy(mdio, child);
- of_mdiobus_register_phy(mdio, child, addr);
+ of_pinctrl_select_state_default(child);
+
+ 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;
@@ -248,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;
@@ -333,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;
@@ -349,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;
}
@@ -439,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))
@@ -470,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);
@@ -524,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 cf593ee6a6..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)
@@ -554,7 +734,7 @@ static int ksz8873mll_read_status(struct phy_device *phydev)
int regval;
/* dummy read */
- regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
+ (void)phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_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.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..c23947b7cb 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,
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..9d34beab0d 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>
@@ -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..c6108c488b 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;
}
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..b6f81cfab8 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -5,8 +5,8 @@
#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 <malloc.h>
#include <asm/byteorder.h>
#include <errno.h>
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 fed387c43a..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;
@@ -136,7 +141,7 @@ static struct nvmem_cell *nvmem_find_cell(const char *cell_id)
struct nvmem_cell *p;
list_for_each_entry(p, &nvmem_cells, node)
- if (p && !strcmp(p->name, cell_id))
+ if (!strcmp(p->name, cell_id))
return p;
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,7 +606,10 @@ 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);
+ rc = nvmem->reg_read(nvmem->priv, cell->offset, &v, 1);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
*b++ |= GENMASK(bit_offset - 1, 0) & v;
/* setup rest of the byte if any */
@@ -607,8 +626,11 @@ 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 (rc < 0)
+ return ERR_PTR(rc);
+
*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
}
@@ -640,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;
@@ -674,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;
@@ -704,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);
@@ -737,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;
@@ -771,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;
@@ -803,3 +825,42 @@ void *nvmem_cell_get_and_read(struct device_node *np, const char *cell_name,
return value;
}
EXPORT_SYMBOL_GPL(nvmem_cell_get_and_read);
+
+/**
+ * nvmem_cell_read_variable_le_u32() - Read up to 32-bits of data as a little endian number.
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
+ u32 *val)
+{
+ size_t len;
+ const u8 *buf;
+ int i;
+
+ len = sizeof(*val);
+
+ buf = nvmem_cell_get_and_read(dev->of_node, cell_id, len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* Copy w/ implicit endian conversion */
+ *val = 0;
+ for (i = 0; i < len; i++)
+ *val |= buf[i] << (8 * i);
+
+ kfree(buf);
+
+ 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 1f99a8012f..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,18 @@
#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>
/*
@@ -45,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
@@ -59,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
@@ -87,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);
@@ -108,6 +149,8 @@ struct imx_ocotp_data {
int (*fuse_blow)(struct ocotp_priv *priv, u32 addr, u32 value);
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 {
@@ -121,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;
@@ -173,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);
@@ -207,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;
@@ -232,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);
@@ -248,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);
@@ -258,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;
@@ -266,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);
@@ -279,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) {
@@ -342,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);
@@ -421,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);
@@ -445,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;
}
@@ -490,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);
@@ -517,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;
@@ -534,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;
}
@@ -591,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);
@@ -618,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
@@ -630,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 = {
@@ -638,19 +718,33 @@ static struct regmap_bus imx_ocotp_regmap_bus = {
.reg_read = imx_ocotp_reg_read,
};
-static void imx_ocotp_init_dt(struct ocotp_priv *priv)
+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;
- int len;
+ struct device_node *node = priv->dev.parent->of_node;
+ u32 tester3, tester4;
+ int ret, len = 0;
if (!node)
- return;
+ return 0;
prop = of_get_property(node, "barebox,provide-mac-address", &len);
- if (!prop)
- return;
for (; len >= MAC_ADDRESS_PROPLEN; len -= MAC_ADDRESS_PROPLEN) {
struct device_node *rnode;
@@ -668,20 +762,19 @@ static void imx_ocotp_init_dt(struct ocotp_priv *priv)
of_eth_register_ethaddr(rnode, mac);
}
-}
-static int imx_ocotp_write(void *ctx, unsigned offset, const void *val, size_t bytes)
-{
- struct ocotp_priv *priv = ctx;
+ if (!of_property_read_bool(node, "barebox,feature-controller"))
+ return 0;
- return regmap_bulk_write(priv->map, offset, val, bytes);
-}
+ ret = regmap_read(priv->map, OCOTP_OFFSET_TO_ADDR(0x440), &tester3);
+ if (ret != 0)
+ return ret;
-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)
@@ -697,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;
@@ -733,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);
@@ -785,10 +866,12 @@ static int imx_ocotp_probe(struct device_d *dev)
if (IS_ENABLED(CONFIG_MACHINE_ID))
imx_ocotp_set_unique_machine_id();
- imx_ocotp_init_dt(priv);
+ ret = imx_ocotp_init_dt(priv);
+ if (ret)
+ dev_warn(dev, "feature controller registration failed: %pe\n",
+ ERR_PTR(ret));
dev_add_param_bool(&(priv->dev), "sense_enable", NULL, NULL, &priv->sense_enable, priv);
-
return 0;
}
@@ -822,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 },
@@ -830,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 },
@@ -841,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 },
@@ -852,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 },
@@ -863,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 },
@@ -874,18 +961,52 @@ 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 },
+ .format_mac = imx_ocotp_format_mac,
+ .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 = {
+ .tester4.vpu_bitmask = 0x1c0000,
+ .tester4.cpu_bitmask = 0x3,
+};
+
+static struct imx_ocotp_data imx8mm_ocotp_data = {
+ .nregs = 256,
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { 0x90 },
@@ -893,10 +1014,30 @@ 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,
+ .feat = &imx8mm_featctrl_data,
+ .ctrl = &ocotp_ctrl_reg_default,
+};
+
+static struct imx8m_featctrl_data imx8mn_featctrl_data = {
+ .tester4.gpu_bitmask = 0x1000000,
+ .tester4.cpu_bitmask = 0x3,
+};
+
+static struct imx_ocotp_data imx8mn_ocotp_data = {
+ .nregs = 256,
+ .addr_to_offset = imx6sl_addr_to_offset,
+ .mac_offsets_num = 1,
+ .mac_offsets = { 0x90 },
+ .format_mac = imx_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
+ .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 },
@@ -904,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[] = {
@@ -933,10 +1075,10 @@ static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
.data = &imx8mq_ocotp_data,
}, {
.compatible = "fsl,imx8mm-ocotp",
- .data = &imx8mq_ocotp_data,
+ .data = &imx8mm_ocotp_data,
}, {
.compatible = "fsl,imx8mn-ocotp",
- .data = &imx8mq_ocotp_data,
+ .data = &imx8mn_ocotp_data,
}, {
.compatible = "fsl,vf610-ocotp",
.data = &vf610_ocotp_data,
@@ -944,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 f3d2cc00cc..2791100a2d 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -5,17 +5,33 @@ 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"
+config FEATURE_CONTROLLER_FIXUP
+ bool "Fix up DT nodes gated by feature controller"
+ depends on FEATURE_CONTROLLER
+ default y
+ help
+ When specified, barebox feature controller drivers are consulted
+ prior to probing nodes to detect whether the device may not
+ be available (e.g. because support is fused out).
+ This option additionally fixes up the kernel device tree,
+ so it doesn't attempt probing these devices either.
+ If unsure, say y.
+
config OF_ADDRESS_PCI
bool
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 b91ee92e1b..f0d3574148 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;
@@ -832,7 +853,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) {
@@ -1049,7 +1070,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 +1151,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 +1494,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 +1509,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 +1775,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 +2009,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 +2228,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("");
@@ -2301,6 +2320,41 @@ void of_delete_property(struct property *pp)
free(pp);
}
+struct property *of_rename_property(struct device_node *np,
+ const char *old_name, const char *new_name)
+{
+ struct property *pp;
+
+ pp = of_find_property(np, old_name, NULL);
+ if (!pp)
+ return NULL;
+
+ of_property_write_bool(np, new_name, false);
+
+ free(pp->name);
+ pp->name = xstrdup(new_name);
+ 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
@@ -2362,6 +2416,36 @@ int of_append_property(struct device_node *np, const char *name, const void *val
return 0;
}
+int of_prepend_property(struct device_node *np, const char *name, const void *val, int len)
+{
+ struct property *pp;
+ const void *oldval;
+ void *buf;
+ int oldlen;
+
+ pp = of_find_property(np, name, &oldlen);
+ if (!pp) {
+ of_new_property(np, name, val, len);
+ return 0;
+ }
+
+ oldval = of_property_get_value(pp);
+
+ buf = malloc(len + oldlen);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, val, len);
+ memcpy(buf + len, oldval, oldlen);
+
+ free(pp->value);
+ pp->value = buf;
+ pp->length = len + oldlen;
+ pp->value_const = NULL;
+
+ return 0;
+}
+
int of_property_sprintf(struct device_node *np,
const char *propname, const char *fmt, ...)
{
@@ -2440,6 +2524,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)
{
@@ -2468,7 +2553,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)
@@ -2476,7 +2561,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);
@@ -2488,6 +2573,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
@@ -2504,6 +2590,12 @@ int of_probe(void)
return -ENODEV;
/*
+ * We do this first thing, so board drivers can patch the device
+ * tree prior to device creation if needed.
+ */
+ of_platform_device_create_root(root_node);
+
+ /*
* Handle certain compatibles explicitly, since we don't want to create
* platform_devices for every node in /reserved-memory with a
* "compatible",
@@ -2515,8 +2607,6 @@ int of_probe(void)
if (node)
of_platform_populate(node, NULL, NULL);
- of_platform_device_create_root(root_node);
-
of_platform_populate(root_node, of_default_bus_match_table, NULL);
return 0;
@@ -2566,25 +2656,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);
}
@@ -2617,30 +2717,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);
- p = strchrnul(name, ':');
+ dn = of_find_node_by_path_or_alias(NULL, buf);
- q = xstrndup(name, p - name);
+ free(buf);
- dn = of_find_node_by_path_or_alias(NULL, q);
+ if (options && *p)
+ *options = p + 1;
- free(q);
+ return dn;
+}
- if (baudrate && *p) {
- unsigned rate = simple_strtoul(p + 1, NULL, 10);
+struct device_node *of_get_stdoutpath(unsigned int *baudrate)
+{
+ const char *opts = NULL;
+ struct device_node *dn;
+
+ 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 && opts) {
+ unsigned rate = simple_strtoul(opts, NULL, 10);
if (rate)
*baudrate = rate;
}
@@ -2648,11 +2769,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;
@@ -2740,6 +2861,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
*
@@ -2783,6 +2919,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
*
@@ -2865,6 +3032,19 @@ struct device_node *of_find_node_by_reproducible_name(struct device_node *from,
return NULL;
}
+struct device_node *of_get_node_by_reproducible_name(struct device_node *dstroot,
+ struct device_node *srcnp)
+{
+ struct device_node *dstnp;
+ char *name;
+
+ name = of_get_reproducible_name(srcnp);
+ dstnp = of_find_node_by_reproducible_name(dstroot, name);
+ free(name);
+
+ return dstnp;
+}
+
/**
* of_graph_parse_endpoint() - parse common endpoint node properties
* @node: pointer to endpoint device_node
@@ -2876,8 +3056,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));
@@ -2949,15 +3129,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 42f45bbd4f..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:
@@ -212,7 +255,7 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
nodep = fdt_prop->data;
name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff));
- if (!name) {
+ if (!name || !node) {
ret = -ESPIPE;
goto err;
}
@@ -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_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 20a43f5170..73c7a91db9 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -102,8 +102,10 @@ static char *of_overlay_fix_path(struct device_node *root,
if (of_get_child_by_name(fragment, "__overlay__"))
break;
}
- if (!fragment)
+ if (!fragment) {
+ pr_info("could not find __overlay__ node\n");
return NULL;
+ }
target = find_target(root, fragment);
if (!target)
@@ -112,11 +114,11 @@ 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 void of_overlay_apply_symbols(struct device_node *root,
- struct device_node *overlay)
+static int of_overlay_apply_symbols(struct device_node *root,
+ struct device_node *overlay)
{
const char *old_path;
char *new_path;
@@ -129,12 +131,12 @@ static void of_overlay_apply_symbols(struct device_node *root,
if (!overlay_symbols) {
pr_debug("overlay doesn't have a __symbols__ node\n");
- return;
+ return 0;
}
if (!root_symbols) {
pr_info("root doesn't have a __symbols__ node\n");
- return;
+ return 0;
}
list_for_each_entry(prop, &overlay_symbols->properties, list) {
@@ -143,11 +145,15 @@ static void of_overlay_apply_symbols(struct device_node *root,
old_path = of_property_get_value(prop);
new_path = of_overlay_fix_path(root, overlay, old_path);
+ if (!new_path)
+ return -EINVAL;
pr_debug("add symbol %s with new path %s\n",
prop->name, new_path);
of_property_write_string(root_symbols, prop->name, new_path);
}
+
+ return 0;
}
static int of_overlay_apply_fragment(struct device_node *root,
@@ -190,7 +196,9 @@ int of_overlay_apply_tree(struct device_node *root,
goto out_err;
/* Copy symbols from resolved overlay to base device tree */
- of_overlay_apply_symbols(root, resolved);
+ err = of_overlay_apply_symbols(root, resolved);
+ if (err)
+ goto out_err;
/* Copy nodes and properties from resolved overlay to root */
for_each_child_of_node(resolved, fragment) {
@@ -300,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;
@@ -418,7 +415,6 @@ void of_overlay_set_basedir(const char *path)
static int of_overlay_apply_dir(struct device_node *root, const char *dirname,
bool filter)
{
- char *p, *path;
int ret = 0;
DIR *dir;
@@ -431,8 +427,6 @@ static int of_overlay_apply_dir(struct device_node *root, const char *dirname,
if (!dir)
return -errno;
- p = path = strdup(of_overlay_filepattern);
-
while (1) {
struct dirent *ent;
char *filename;
diff --git a/drivers/of/partition.c b/drivers/of/partition.c
index 6c05e28ea9..df66751fe9 100644
--- a/drivers/of/partition.c
+++ b/drivers/of/partition.c
@@ -26,14 +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;
- const char *name;
int len;
- unsigned long flags = 0;
int na, ns;
if (!node)
@@ -47,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)
@@ -60,21 +58,23 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
if (!partname)
return NULL;
- name = (char *)partname;
-
- 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);
@@ -82,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;
@@ -94,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) {
@@ -110,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);
@@ -144,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++;
}
@@ -195,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)
@@ -242,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 7f377b8b37..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,17 +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;
}
@@ -43,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;
@@ -70,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;
@@ -84,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);
}
/**
@@ -94,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;
@@ -109,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))
@@ -132,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;
@@ -140,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",
@@ -161,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;
}
@@ -177,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);
@@ -198,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;
@@ -220,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;
@@ -233,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;
@@ -254,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);
@@ -283,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;
}
@@ -300,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;
}
@@ -323,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;
@@ -346,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;
@@ -366,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);
@@ -391,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);
}
/**
@@ -408,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
@@ -434,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;
}
@@ -449,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
*/
@@ -477,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)
@@ -510,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)
@@ -522,12 +604,15 @@ int of_devices_ensure_probed_by_property(const char *property_name)
return 0;
for_each_node_with_property(node, property_name) {
- ret = of_device_ensure_probed(node);
+ if (!of_device_is_available(node))
+ continue;
+
+ err = of_device_ensure_probed(node);
if (err)
ret = err;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_property);
@@ -540,12 +625,15 @@ int of_devices_ensure_probed_by_name(const char *name)
return 0;
for_each_node_by_name(node, name) {
- ret = of_device_ensure_probed(node);
+ if (!of_device_is_available(node))
+ continue;
+
+ err = of_device_ensure_probed(node);
if (err)
ret = err;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_name);
diff --git a/drivers/of/reserved-mem.c b/drivers/of/reserved-mem.c
index 34e61dfea3..599a7c968a 100644
--- a/drivers/of/reserved-mem.c
+++ b/drivers/of/reserved-mem.c
@@ -1,19 +1,38 @@
// 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>
+#include <memory.h>
+#include <linux/ioport.h>
#define MEMRESERVE_NCELLS 2
-#define MEMRESERVE_FLAGS (IORESOURCE_MEM | IORESOURCE_EXCLUSIVE)
-int of_reserved_mem_walk(int (*handler)(const struct resource *res))
+static void request_region(struct resource *r)
+{
+ struct memory_bank *bank;
+
+ for_each_memory_bank(bank) {
+ 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);
+ break;
+ }
+}
+
+static int of_reserved_mem_walk(void)
{
struct device_node *node, *child;
int ncells = 0;
const __be32 *reg;
- int ret;
node = of_find_node_by_path("/reserved-memory");
if (node) {
@@ -27,11 +46,9 @@ int of_reserved_mem_walk(int (*handler)(const struct resource *res))
of_address_to_resource(child, 0, &resource);
resource.name = child->name;
- resource.flags = MEMRESERVE_FLAGS;
+ resource.flags = IORESOURCE_MEM;
- ret = handler(&resource);
- if (ret)
- return ret;
+ request_region(&resource);
}
}
@@ -48,7 +65,7 @@ int of_reserved_mem_walk(int (*handler)(const struct resource *res))
snprintf(name, sizeof(name), "fdt-memreserve-%u", n++);
resource.name = name;
- resource.flags = MEMRESERVE_FLAGS;
+ resource.flags = IORESOURCE_MEM;
resource.start = of_read_number(reg + i, MEMRESERVE_NCELLS);
i += MEMRESERVE_NCELLS;
@@ -61,11 +78,10 @@ int of_reserved_mem_walk(int (*handler)(const struct resource *res))
resource.end = resource.start + size - 1;
- ret = handler(&resource);
- if (ret)
- return ret;
+ request_region(&resource);
}
}
return 0;
}
+postmem_initcall(of_reserved_mem_walk);
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..9249bffecb 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -13,3 +13,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..b6eab56d87 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -50,7 +50,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 +65,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 +73,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 +97,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 +111,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/pci-ecam-generic.c b/drivers/pci/pci-ecam-generic.c
index 58c81ecbb6..e8609bd4b0 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;
@@ -198,8 +198,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..67868d09b6 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;
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..12a0ec71a7 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,6 +551,9 @@ 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);
diff --git a/drivers/pci/pci-mvebu.c b/drivers/pci/pci-mvebu.c
index 41bccd2783..9e2c7dc648 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);
@@ -395,10 +395,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 +438,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..fba8b47ece 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;
@@ -1230,8 +1230,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;
@@ -1277,7 +1278,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..84678e40a9 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);
@@ -152,6 +153,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)
{
@@ -221,9 +405,11 @@ static void setup_device(struct pci_dev *dev, int max_bar)
}
*last_addr = ALIGN(*last_addr, size);
- pci_write_config_dword(dev, pci_base_address_0, *last_addr);
+ pci_write_config_dword(dev, pci_base_address_0,
+ lower_32_bits(*last_addr));
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(*last_addr));
start = *last_addr;
*last_addr += size;
} else {
@@ -249,6 +435,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 +529,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 +553,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;
@@ -399,10 +619,10 @@ static unsigned int pci_scan_bus(struct pci_bus *bus)
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,
+ dev->dev.of_node = pci_of_match_device(bus->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,12 +646,15 @@ 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();
@@ -539,6 +762,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 +860,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..b34fc482ed 100644
--- a/drivers/pci/pcie-designware-host.c
+++ b/drivers/pci/pcie-designware-host.c
@@ -71,8 +71,8 @@ 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 device *dev = pci->dev;
+ struct device_node *np = dev->of_node;
struct of_pci_range range;
struct of_pci_range_parser parser;
struct resource *cfg_res;
@@ -87,7 +87,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
ns = of_n_size_cells(np);
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;
diff --git a/drivers/pci/pcie-designware.c b/drivers/pci/pcie-designware.c
index ecfd6d5fa6..9c8bc772f9 100644
--- a/drivers/pci/pcie-designware.c
+++ b/drivers/pci/pcie-designware.c
@@ -281,8 +281,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..4bb0795d06 100644
--- a/drivers/pci/pcie-designware.h
+++ b/drivers/pci/pcie-designware.h
@@ -180,7 +180,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..cc771e2cae
--- /dev/null
+++ b/drivers/pci/pcie-dw-rockchip.c
@@ -0,0 +1,300 @@
+// 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);
+
+ 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 8a57bd1aa9..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;
@@ -169,8 +169,6 @@ int phy_power_on(struct phy *phy)
dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
goto out;
}
- } else {
- ret = 0; /* Override possible ret == -ENOTSUPP */
}
++phy->power_count;
@@ -205,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)
@@ -227,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;
}
@@ -297,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);
}
@@ -333,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);
@@ -343,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;
@@ -362,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);
@@ -379,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/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/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..c1e937ea2c 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_pin_bank *gc_to_rockchip_pinctrl(struct gpio_chip *gc)
+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 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;
- mask = 1 << (16 + (gpio % 16));
- out = 1 << (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_DRV_PMU_OFFSET;
+ } else {
+ *regmap = info->regmap_base;
+ *reg = PX30_DRV_GRF_OFFSET;
- if (gpio < 16) {
- writel(mask | vval, bank->reg_base + RK_GPIOV2_DR_L);
- writel(mask | out, bank->reg_base + RK_GPIOV2_DDR_L);
+ /* 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;
+
+ 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
+
+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,168 @@ 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");
+ /* rk3066b does support any pulls */
+ if (ctrl->type == RK3066B)
+ return pull ? -EINVAL : 0;
+
+ ret = ctrl->pull_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+ if (ret)
+ return ret;
+
+ 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;
+ }
+
+ /* 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 = regmap_update_bits(regmap, reg, rmask, data);
+ break;
+ default:
+ dev_err(dev, "unsupported pinctrl type\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;
+ return ret;
+}
- bank_num = be32_to_cpu(*list++);
- pin_num = be32_to_cpu(*list++);
- func = be32_to_cpu(*list++);
- phandle = list++;
+#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
- if (!phandle)
- return -EINVAL;
+static int rk3328_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;
- 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));
+ *regmap = info->regmap_base;
+ *reg = RK3328_SCHMITT_GRF_OFFSET;
- ret = of_property_read_u32(np_config, "drive-strength", &drive_strength);
- if (!ret)
- rockchip_set_drive_perpin(bank, pin_num, drive_strength);
- }
+ *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;
return 0;
}
-static struct pinctrl_ops rockchip_pinctrl_ops = {
- .set_state = rockchip_pinctrl_set_state,
-};
+#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
-static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
- struct device_d *dev)
+static int rk3568_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;
-
- if (of_address_to_resource(bank->of_node, 0, &node_res)) {
- dev_err(dev, "cannot find IO resource for bank\n");
- return -ENOENT;
- }
+ struct rockchip_pinctrl *info = bank->drvdata;
- 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);
+ 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;
}
- bank->reg_base = (void __iomem *)res->start;
-
- if (of_device_is_compatible(bank->of_node,
- "rockchip,rk3188-gpio-bank0"))
- bank->bank_type = RK3188_BANK0;
- else
- bank->bank_type = COMMON_BANK;
-
- bank->clk = of_clk_get(bank->of_node, 0);
- if (IS_ERR(bank->clk))
- return PTR_ERR(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 clk_enable(bank->clk);
+ 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 +2282,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 +2314,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 +2337,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 +2361,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 +2467,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 +2594,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 +2885,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 +2898,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/pmdomain/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c
new file mode 100644
index 0000000000..fc2fbdaf14
--- /dev/null
+++ b/drivers/pmdomain/imx/gpcv2.c
@@ -0,0 +1,1331 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2017 Impinj, Inc
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * Based on the code of analogus driver:
+ *
+ * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ */
+
+#include <of_device.h>
+#include <common.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <clock.h>
+#include <abort.h>
+#include <malloc.h>
+#include <io.h>
+#include <init.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+
+#include <pm_domain.h>
+#include <regulator.h>
+#include <dt-bindings/power/imx7-power.h>
+#include <dt-bindings/power/imx8mq-power.h>
+#include <dt-bindings/power/imx8mm-power.h>
+#include <dt-bindings/power/imx8mn-power.h>
+#include <dt-bindings/power/imx8mp-power.h>
+
+#define GPC_LPCR_A_CORE_BSC 0x000
+
+#define GPC_PGC_CPU_MAPPING 0x0ec
+#define IMX8MP_GPC_PGC_CPU_MAPPING 0x1cc
+
+#define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6)
+#define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5)
+#define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4)
+#define IMX7_PCIE_PHY_A_CORE_DOMAIN BIT(3)
+#define IMX7_MIPI_PHY_A_CORE_DOMAIN BIT(2)
+
+#define IMX8M_PCIE2_A53_DOMAIN BIT(15)
+#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14)
+#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13)
+#define IMX8M_DISP_A53_DOMAIN BIT(12)
+#define IMX8M_HDMI_A53_DOMAIN BIT(11)
+#define IMX8M_VPU_A53_DOMAIN BIT(10)
+#define IMX8M_GPU_A53_DOMAIN BIT(9)
+#define IMX8M_DDR2_A53_DOMAIN BIT(8)
+#define IMX8M_DDR1_A53_DOMAIN BIT(7)
+#define IMX8M_OTG2_A53_DOMAIN BIT(5)
+#define IMX8M_OTG1_A53_DOMAIN BIT(4)
+#define IMX8M_PCIE1_A53_DOMAIN BIT(3)
+#define IMX8M_MIPI_A53_DOMAIN BIT(2)
+
+#define IMX8MM_VPUH1_A53_DOMAIN BIT(15)
+#define IMX8MM_VPUG2_A53_DOMAIN BIT(14)
+#define IMX8MM_VPUG1_A53_DOMAIN BIT(13)
+#define IMX8MM_DISPMIX_A53_DOMAIN BIT(12)
+#define IMX8MM_VPUMIX_A53_DOMAIN BIT(10)
+#define IMX8MM_GPUMIX_A53_DOMAIN BIT(9)
+#define IMX8MM_GPU_A53_DOMAIN (BIT(8) | BIT(11))
+#define IMX8MM_DDR1_A53_DOMAIN BIT(7)
+#define IMX8MM_OTG2_A53_DOMAIN BIT(5)
+#define IMX8MM_OTG1_A53_DOMAIN BIT(4)
+#define IMX8MM_PCIE_A53_DOMAIN BIT(3)
+#define IMX8MM_MIPI_A53_DOMAIN BIT(2)
+
+#define IMX8MN_DISPMIX_A53_DOMAIN BIT(12)
+#define IMX8MN_GPUMIX_A53_DOMAIN BIT(9)
+#define IMX8MN_DDR1_A53_DOMAIN BIT(7)
+#define IMX8MN_OTG1_A53_DOMAIN BIT(4)
+#define IMX8MN_MIPI_A53_DOMAIN BIT(2)
+
+#define IMX8MP_MEDIA_ISPDWP_A53_DOMAIN BIT(20)
+#define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19)
+#define IMX8MP_MIPI_PHY2_A53_DOMAIN BIT(18)
+#define IMX8MP_HDMI_PHY_A53_DOMAIN BIT(17)
+#define IMX8MP_HDMIMIX_A53_DOMAIN BIT(16)
+#define IMX8MP_VPU_VC8000E_A53_DOMAIN BIT(15)
+#define IMX8MP_VPU_G2_A53_DOMAIN BIT(14)
+#define IMX8MP_VPU_G1_A53_DOMAIN BIT(13)
+#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12)
+#define IMX8MP_GPU3D_A53_DOMAIN BIT(11)
+#define IMX8MP_VPUMIX_A53_DOMAIN BIT(10)
+#define IMX8MP_GPUMIX_A53_DOMAIN BIT(9)
+#define IMX8MP_GPU2D_A53_DOMAIN BIT(8)
+#define IMX8MP_AUDIOMIX_A53_DOMAIN BIT(7)
+#define IMX8MP_MLMIX_A53_DOMAIN BIT(6)
+#define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5)
+#define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4)
+#define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3)
+#define IMX8MP_MIPI_PHY1_A53_DOMAIN BIT(2)
+
+#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ 0x0d8
+#define IMX8MP_GPC_PU_PGC_SW_PDN_REQ 0x0e4
+
+#define GPC_PU_PGC_SW_PUP_REQ 0x0f8
+#define GPC_PU_PGC_SW_PDN_REQ 0x104
+
+#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4)
+#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3)
+#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2)
+#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1)
+#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0)
+
+#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13)
+#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12)
+#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11)
+#define IMX8M_DISP_SW_Pxx_REQ BIT(10)
+#define IMX8M_HDMI_SW_Pxx_REQ BIT(9)
+#define IMX8M_VPU_SW_Pxx_REQ BIT(8)
+#define IMX8M_GPU_SW_Pxx_REQ BIT(7)
+#define IMX8M_DDR2_SW_Pxx_REQ BIT(6)
+#define IMX8M_DDR1_SW_Pxx_REQ BIT(5)
+#define IMX8M_OTG2_SW_Pxx_REQ BIT(3)
+#define IMX8M_OTG1_SW_Pxx_REQ BIT(2)
+#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1)
+#define IMX8M_MIPI_SW_Pxx_REQ BIT(0)
+
+#define IMX8MM_VPUH1_SW_Pxx_REQ BIT(13)
+#define IMX8MM_VPUG2_SW_Pxx_REQ BIT(12)
+#define IMX8MM_VPUG1_SW_Pxx_REQ BIT(11)
+#define IMX8MM_DISPMIX_SW_Pxx_REQ BIT(10)
+#define IMX8MM_VPUMIX_SW_Pxx_REQ BIT(8)
+#define IMX8MM_GPUMIX_SW_Pxx_REQ BIT(7)
+#define IMX8MM_GPU_SW_Pxx_REQ (BIT(6) | BIT(9))
+#define IMX8MM_DDR1_SW_Pxx_REQ BIT(5)
+#define IMX8MM_OTG2_SW_Pxx_REQ BIT(3)
+#define IMX8MM_OTG1_SW_Pxx_REQ BIT(2)
+#define IMX8MM_PCIE_SW_Pxx_REQ BIT(1)
+#define IMX8MM_MIPI_SW_Pxx_REQ BIT(0)
+
+#define IMX8MN_DISPMIX_SW_Pxx_REQ BIT(10)
+#define IMX8MN_GPUMIX_SW_Pxx_REQ BIT(7)
+#define IMX8MN_DDR1_SW_Pxx_REQ BIT(5)
+#define IMX8MN_OTG1_SW_Pxx_REQ BIT(2)
+#define IMX8MN_MIPI_SW_Pxx_REQ BIT(0)
+
+#define IMX8MP_DDRMIX_Pxx_REQ BIT(19)
+#define IMX8MP_MEDIA_ISP_DWP_Pxx_REQ BIT(18)
+#define IMX8MP_HSIOMIX_Pxx_REQ BIT(17)
+#define IMX8MP_MIPI_PHY2_Pxx_REQ BIT(16)
+#define IMX8MP_HDMI_PHY_Pxx_REQ BIT(15)
+#define IMX8MP_HDMIMIX_Pxx_REQ BIT(14)
+#define IMX8MP_VPU_VC8K_Pxx_REQ BIT(13)
+#define IMX8MP_VPU_G2_Pxx_REQ BIT(12)
+#define IMX8MP_VPU_G1_Pxx_REQ BIT(11)
+#define IMX8MP_MEDIMIX_Pxx_REQ BIT(10)
+#define IMX8MP_GPU_3D_Pxx_REQ BIT(9)
+#define IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ BIT(8)
+#define IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ BIT(7)
+#define IMX8MP_GPU_2D_Pxx_REQ BIT(6)
+#define IMX8MP_AUDIOMIX_Pxx_REQ BIT(5)
+#define IMX8MP_MLMIX_Pxx_REQ BIT(4)
+#define IMX8MP_USB2_PHY_Pxx_REQ BIT(3)
+#define IMX8MP_USB1_PHY_Pxx_REQ BIT(2)
+#define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1)
+#define IMX8MP_MIPI_PHY1_SW_Pxx_REQ BIT(0)
+
+#define GPC_M4_PU_PDN_FLG 0x1bc
+
+#define IMX8MP_GPC_PU_PWRHSK 0x190
+#define GPC_PU_PWRHSK 0x1fc
+
+#define IMX8M_GPU_HSK_PWRDNACKN BIT(26)
+#define IMX8M_VPU_HSK_PWRDNACKN BIT(25)
+#define IMX8M_DISP_HSK_PWRDNACKN BIT(24)
+#define IMX8M_GPU_HSK_PWRDNREQN BIT(6)
+#define IMX8M_VPU_HSK_PWRDNREQN BIT(5)
+#define IMX8M_DISP_HSK_PWRDNREQN BIT(4)
+
+
+#define IMX8MM_GPUMIX_HSK_PWRDNACKN BIT(29)
+#define IMX8MM_GPU_HSK_PWRDNACKN (BIT(27) | BIT(28))
+#define IMX8MM_VPUMIX_HSK_PWRDNACKN BIT(26)
+#define IMX8MM_DISPMIX_HSK_PWRDNACKN BIT(25)
+#define IMX8MM_HSIO_HSK_PWRDNACKN (BIT(23) | BIT(24))
+#define IMX8MM_GPUMIX_HSK_PWRDNREQN BIT(11)
+#define IMX8MM_GPU_HSK_PWRDNREQN (BIT(9) | BIT(10))
+#define IMX8MM_VPUMIX_HSK_PWRDNREQN BIT(8)
+#define IMX8MM_DISPMIX_HSK_PWRDNREQN BIT(7)
+#define IMX8MM_HSIO_HSK_PWRDNREQN (BIT(5) | BIT(6))
+
+#define IMX8MN_GPUMIX_HSK_PWRDNACKN (BIT(29) | BIT(27))
+#define IMX8MN_DISPMIX_HSK_PWRDNACKN BIT(25)
+#define IMX8MN_HSIO_HSK_PWRDNACKN BIT(23)
+#define IMX8MN_GPUMIX_HSK_PWRDNREQN (BIT(11) | BIT(9))
+#define IMX8MN_DISPMIX_HSK_PWRDNREQN BIT(7)
+#define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5)
+
+#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30)
+#define IMX8MP_HDMIMIX_PWRDNACKN BIT(29)
+#define IMX8MP_HSIOMIX_PWRDNACKN BIT(28)
+#define IMX8MP_VPUMIX_PWRDNACKN BIT(26)
+#define IMX8MP_GPUMIX_PWRDNACKN BIT(25)
+#define IMX8MP_MLMIX_PWRDNACKN (BIT(23) | BIT(24))
+#define IMX8MP_AUDIOMIX_PWRDNACKN (BIT(20) | BIT(31))
+#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14)
+#define IMX8MP_HDMIMIX_PWRDNREQN BIT(13)
+#define IMX8MP_HSIOMIX_PWRDNREQN BIT(12)
+#define IMX8MP_VPUMIX_PWRDNREQN BIT(10)
+#define IMX8MP_GPUMIX_PWRDNREQN BIT(9)
+#define IMX8MP_MLMIX_PWRDNREQN (BIT(7) | BIT(8))
+#define IMX8MP_AUDIOMIX_PWRDNREQN (BIT(4) | BIT(15))
+
+/*
+ * The PGC offset values in Reference Manual
+ * (Rev. 1, 01/2018 and the older ones) GPC chapter's
+ * GPC_PGC memory map are incorrect, below offset
+ * values are from design RTL.
+ */
+#define IMX7_PGC_MIPI 16
+#define IMX7_PGC_PCIE 17
+#define IMX7_PGC_USB_HSIC 20
+
+
+#define IMX8M_PGC_MIPI 16
+#define IMX8M_PGC_PCIE1 17
+#define IMX8M_PGC_OTG1 18
+#define IMX8M_PGC_OTG2 19
+#define IMX8M_PGC_DDR1 21
+#define IMX8M_PGC_GPU 23
+#define IMX8M_PGC_VPU 24
+#define IMX8M_PGC_DISP 26
+#define IMX8M_PGC_MIPI_CSI1 27
+#define IMX8M_PGC_MIPI_CSI2 28
+#define IMX8M_PGC_PCIE2 29
+
+#define IMX8MM_PGC_MIPI 16
+#define IMX8MM_PGC_PCIE 17
+#define IMX8MM_PGC_OTG1 18
+#define IMX8MM_PGC_OTG2 19
+#define IMX8MM_PGC_DDR1 21
+#define IMX8MM_PGC_GPU2D 22
+#define IMX8MM_PGC_GPUMIX 23
+#define IMX8MM_PGC_VPUMIX 24
+#define IMX8MM_PGC_GPU3D 25
+#define IMX8MM_PGC_DISPMIX 26
+#define IMX8MM_PGC_VPUG1 27
+#define IMX8MM_PGC_VPUG2 28
+#define IMX8MM_PGC_VPUH1 29
+
+#define IMX8MN_PGC_MIPI 16
+#define IMX8MN_PGC_OTG1 18
+#define IMX8MN_PGC_DDR1 21
+#define IMX8MN_PGC_GPUMIX 23
+#define IMX8MN_PGC_DISPMIX 26
+
+#define IMX8MP_PGC_NOC 9
+#define IMX8MP_PGC_MIPI1 12
+#define IMX8MP_PGC_PCIE 13
+#define IMX8MP_PGC_USB1 14
+#define IMX8MP_PGC_USB2 15
+#define IMX8MP_PGC_MLMIX 16
+#define IMX8MP_PGC_AUDIOMIX 17
+#define IMX8MP_PGC_GPU2D 18
+#define IMX8MP_PGC_GPUMIX 19
+#define IMX8MP_PGC_VPUMIX 20
+#define IMX8MP_PGC_GPU3D 21
+#define IMX8MP_PGC_MEDIAMIX 22
+#define IMX8MP_PGC_VPU_G1 23
+#define IMX8MP_PGC_VPU_G2 24
+#define IMX8MP_PGC_VPU_VC8000E 25
+#define IMX8MP_PGC_HDMIMIX 26
+#define IMX8MP_PGC_HDMI 27
+#define IMX8MP_PGC_MIPI2 28
+#define IMX8MP_PGC_HSIOMIX 29
+#define IMX8MP_PGC_MEDIA_ISP_DWP 30
+#define IMX8MP_PGC_DDRMIX 31
+
+#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40)
+#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc)
+
+#define GPC_PGC_CTRL_PCR BIT(0)
+
+struct imx_pgc_regs {
+ u16 map;
+ u16 pup;
+ u16 pdn;
+ u16 hsk;
+};
+
+struct imx_pgc_domain {
+ struct generic_pm_domain genpd;
+ struct regmap *regmap;
+ const struct imx_pgc_regs *regs;
+ struct regulator *regulator;
+ struct reset_control *reset;
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ unsigned long pgc;
+
+ const struct {
+ u32 pxx;
+ u32 map;
+ u32 hskreq;
+ u32 hskack;
+ } bits;
+
+ const int voltage;
+ const bool keep_clocks;
+ struct device *dev;
+
+ unsigned int pgc_sw_pup_reg;
+ unsigned int pgc_sw_pdn_reg;
+};
+
+struct imx_pgc_domain_data {
+ const struct imx_pgc_domain *domains;
+ size_t domains_num;
+ const struct imx_pgc_regs *pgc_regs;
+};
+
+static inline struct imx_pgc_domain *
+to_imx_pgc_domain(struct generic_pm_domain *genpd)
+{
+ return container_of(genpd, struct imx_pgc_domain, genpd);
+}
+
+static int imx_pgc_power_up(struct generic_pm_domain *genpd)
+{
+ struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
+ u32 reg_val, pgc;
+ int ret;
+
+ if (!IS_ERR(domain->regulator))
+ ret = regulator_enable(domain->regulator);
+ 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 */
+ ret = clk_bulk_enable(domain->num_clks, domain->clks);
+ if (ret) {
+ dev_err(domain->dev, "failed to enable reset clocks\n");
+ goto out_regulator_disable;
+ }
+
+ reset_control_assert(domain->reset);
+
+ if (domain->bits.pxx) {
+ /* request the domain to power up */
+ regmap_update_bits(domain->regmap, domain->regs->pup,
+ domain->bits.pxx, domain->bits.pxx);
+ /*
+ * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
+ * for PUP_REQ/PDN_REQ bit to be cleared
+ */
+ ret = regmap_read_poll_timeout(domain->regmap,
+ domain->regs->pup, reg_val,
+ !(reg_val & domain->bits.pxx),
+ USEC_PER_MSEC);
+ if (ret) {
+ dev_err(domain->dev, "failed to command PGC\n");
+ goto out_clk_disable;
+ }
+
+ /* disable power control */
+ for_each_set_bit(pgc, &domain->pgc, 32) {
+ regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc),
+ GPC_PGC_CTRL_PCR);
+ }
+ }
+
+ /* delay for reset to propagate */
+ udelay(5);
+
+ reset_control_deassert(domain->reset);
+
+ /* request the ADB400 to power up */
+ if (domain->bits.hskreq) {
+ regmap_update_bits(domain->regmap, domain->regs->hsk,
+ domain->bits.hskreq, domain->bits.hskreq);
+
+ /*
+ * ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, reg_val,
+ * (reg_val & domain->bits.hskack),
+ * USEC_PER_MSEC);
+ * Technically we need the commented code to wait handshake. But that needs
+ * the BLK-CTL module BUS clk-en bit being set.
+ *
+ * There is a separate BLK-CTL module and we will have such a driver for it,
+ * that driver will set the BUS clk-en bit and handshake will be triggered
+ * automatically there. Just add a delay and suppose the handshake finish
+ * after that.
+ */
+ }
+
+ /* Disable reset clocks for all devices in the domain */
+ if (!domain->keep_clocks)
+ clk_bulk_disable(domain->num_clks, domain->clks);
+
+ return 0;
+
+out_clk_disable:
+ clk_bulk_disable(domain->num_clks, domain->clks);
+out_regulator_disable:
+ if (!IS_ERR(domain->regulator))
+ regulator_disable(domain->regulator);
+
+ return ret;
+}
+
+static int imx_pgc_power_down(struct generic_pm_domain *genpd)
+{
+ struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
+ u32 reg_val, pgc;
+ int ret;
+
+ /* Enable reset clocks for all devices in the domain */
+ if (!domain->keep_clocks) {
+ ret = clk_bulk_enable(domain->num_clks, domain->clks);
+ if (ret) {
+ dev_err(domain->dev, "failed to enable reset clocks\n");
+ return ret;
+ }
+ }
+
+ /* request the ADB400 to power down */
+ if (domain->bits.hskreq) {
+ regmap_clear_bits(domain->regmap, domain->regs->hsk,
+ domain->bits.hskreq);
+
+ ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk,
+ reg_val,
+ !(reg_val & domain->bits.hskack),
+ USEC_PER_MSEC);
+ if (ret) {
+ dev_err(domain->dev, "failed to power down ADB400\n");
+ goto out_clk_disable;
+ }
+ }
+
+ if (domain->bits.pxx) {
+ /* enable power control */
+ for_each_set_bit(pgc, &domain->pgc, 32) {
+ regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc),
+ GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
+ }
+
+ /* request the domain to power down */
+ regmap_update_bits(domain->regmap, domain->regs->pdn,
+ domain->bits.pxx, domain->bits.pxx);
+ /*
+ * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
+ * for PUP_REQ/PDN_REQ bit to be cleared
+ */
+ ret = regmap_read_poll_timeout(domain->regmap,
+ domain->regs->pdn, reg_val,
+ !(reg_val & domain->bits.pxx),
+ USEC_PER_MSEC);
+ if (ret) {
+ dev_err(domain->dev, "failed to command PGC\n");
+ goto out_clk_disable;
+ }
+ }
+
+ /* Disable reset clocks for all devices in the domain */
+ clk_bulk_disable(domain->num_clks, domain->clks);
+
+ if (!IS_ERR(domain->regulator)) {
+ ret = regulator_disable(domain->regulator);
+ if (ret) {
+ dev_err(domain->dev, "failed to disable regulator\n");
+ return ret;
+ }
+ }
+
+ return 0;
+
+out_clk_disable:
+ if (!domain->keep_clocks)
+ clk_bulk_disable(domain->num_clks, domain->clks);
+
+ return ret;
+}
+
+static const struct imx_pgc_domain imx7_pgc_domains[] = {
+ [IMX7_POWER_DOMAIN_MIPI_PHY] = {
+ .genpd = {
+ .name = "mipi-phy",
+ },
+ .bits = {
+ .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ,
+ .map = IMX7_MIPI_PHY_A_CORE_DOMAIN,
+ },
+ .voltage = 1000000,
+ .pgc = BIT(IMX7_PGC_MIPI),
+ },
+
+ [IMX7_POWER_DOMAIN_PCIE_PHY] = {
+ .genpd = {
+ .name = "pcie-phy",
+ },
+ .bits = {
+ .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ,
+ .map = IMX7_PCIE_PHY_A_CORE_DOMAIN,
+ },
+ .voltage = 1000000,
+ .pgc = BIT(IMX7_PGC_PCIE),
+ },
+
+ [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
+ .genpd = {
+ .name = "usb-hsic-phy",
+ },
+ .bits = {
+ .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ,
+ .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN,
+ },
+ .voltage = 1200000,
+ .pgc = BIT(IMX7_PGC_USB_HSIC),
+ },
+};
+
+static const struct imx_pgc_regs imx7_pgc_regs = {
+ .map = GPC_PGC_CPU_MAPPING,
+ .pup = GPC_PU_PGC_SW_PUP_REQ,
+ .pdn = GPC_PU_PGC_SW_PDN_REQ,
+ .hsk = GPC_PU_PWRHSK,
+};
+
+static const struct imx_pgc_domain_data imx7_pgc_domain_data = {
+ .domains = imx7_pgc_domains,
+ .domains_num = ARRAY_SIZE(imx7_pgc_domains),
+ .pgc_regs = &imx7_pgc_regs,
+};
+
+static const struct imx_pgc_domain imx8m_pgc_domains[] = {
+ [IMX8M_POWER_DOMAIN_MIPI] = {
+ .genpd = {
+ .name = "mipi",
+ },
+ .bits = {
+ .pxx = IMX8M_MIPI_SW_Pxx_REQ,
+ .map = IMX8M_MIPI_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_MIPI),
+ },
+
+ [IMX8M_POWER_DOMAIN_PCIE1] = {
+ .genpd = {
+ .name = "pcie1",
+ },
+ .bits = {
+ .pxx = IMX8M_PCIE1_SW_Pxx_REQ,
+ .map = IMX8M_PCIE1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_PCIE1),
+ },
+
+ [IMX8M_POWER_DOMAIN_USB_OTG1] = {
+ .genpd = {
+ .name = "usb-otg1",
+ },
+ .bits = {
+ .pxx = IMX8M_OTG1_SW_Pxx_REQ,
+ .map = IMX8M_OTG1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_OTG1),
+ },
+
+ [IMX8M_POWER_DOMAIN_USB_OTG2] = {
+ .genpd = {
+ .name = "usb-otg2",
+ },
+ .bits = {
+ .pxx = IMX8M_OTG2_SW_Pxx_REQ,
+ .map = IMX8M_OTG2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_OTG2),
+ },
+
+ [IMX8M_POWER_DOMAIN_DDR1] = {
+ .genpd = {
+ .name = "ddr1",
+ },
+ .bits = {
+ .pxx = IMX8M_DDR1_SW_Pxx_REQ,
+ .map = IMX8M_DDR2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_DDR1),
+ },
+
+ [IMX8M_POWER_DOMAIN_GPU] = {
+ .genpd = {
+ .name = "gpu",
+ },
+ .bits = {
+ .pxx = IMX8M_GPU_SW_Pxx_REQ,
+ .map = IMX8M_GPU_A53_DOMAIN,
+ .hskreq = IMX8M_GPU_HSK_PWRDNREQN,
+ .hskack = IMX8M_GPU_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8M_PGC_GPU),
+ },
+
+ [IMX8M_POWER_DOMAIN_VPU] = {
+ .genpd = {
+ .name = "vpu",
+ },
+ .bits = {
+ .pxx = IMX8M_VPU_SW_Pxx_REQ,
+ .map = IMX8M_VPU_A53_DOMAIN,
+ .hskreq = IMX8M_VPU_HSK_PWRDNREQN,
+ .hskack = IMX8M_VPU_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8M_PGC_VPU),
+ .keep_clocks = true,
+ },
+
+ [IMX8M_POWER_DOMAIN_DISP] = {
+ .genpd = {
+ .name = "disp",
+ },
+ .bits = {
+ .pxx = IMX8M_DISP_SW_Pxx_REQ,
+ .map = IMX8M_DISP_A53_DOMAIN,
+ .hskreq = IMX8M_DISP_HSK_PWRDNREQN,
+ .hskack = IMX8M_DISP_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8M_PGC_DISP),
+ },
+
+ [IMX8M_POWER_DOMAIN_MIPI_CSI1] = {
+ .genpd = {
+ .name = "mipi-csi1",
+ },
+ .bits = {
+ .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ,
+ .map = IMX8M_MIPI_CSI1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_MIPI_CSI1),
+ },
+
+ [IMX8M_POWER_DOMAIN_MIPI_CSI2] = {
+ .genpd = {
+ .name = "mipi-csi2",
+ },
+ .bits = {
+ .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ,
+ .map = IMX8M_MIPI_CSI2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_MIPI_CSI2),
+ },
+
+ [IMX8M_POWER_DOMAIN_PCIE2] = {
+ .genpd = {
+ .name = "pcie2",
+ },
+ .bits = {
+ .pxx = IMX8M_PCIE2_SW_Pxx_REQ,
+ .map = IMX8M_PCIE2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8M_PGC_PCIE2),
+ },
+};
+
+static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
+ .domains = imx8m_pgc_domains,
+ .domains_num = ARRAY_SIZE(imx8m_pgc_domains),
+ .pgc_regs = &imx7_pgc_regs,
+};
+
+static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
+ [IMX8MM_POWER_DOMAIN_HSIOMIX] = {
+ .genpd = {
+ .name = "hsiomix",
+ },
+ .bits = {
+ .pxx = 0, /* no power sequence control */
+ .map = 0, /* no power sequence control */
+ .hskreq = IMX8MM_HSIO_HSK_PWRDNREQN,
+ .hskack = IMX8MM_HSIO_HSK_PWRDNACKN,
+ },
+ .keep_clocks = true,
+ },
+
+ [IMX8MM_POWER_DOMAIN_PCIE] = {
+ .genpd = {
+ .name = "pcie",
+ },
+ .bits = {
+ .pxx = IMX8MM_PCIE_SW_Pxx_REQ,
+ .map = IMX8MM_PCIE_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_PCIE),
+ },
+
+ [IMX8MM_POWER_DOMAIN_OTG1] = {
+ .genpd = {
+ .name = "usb-otg1",
+ },
+ .bits = {
+ .pxx = IMX8MM_OTG1_SW_Pxx_REQ,
+ .map = IMX8MM_OTG1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_OTG1),
+ },
+
+ [IMX8MM_POWER_DOMAIN_OTG2] = {
+ .genpd = {
+ .name = "usb-otg2",
+ },
+ .bits = {
+ .pxx = IMX8MM_OTG2_SW_Pxx_REQ,
+ .map = IMX8MM_OTG2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_OTG2),
+ },
+
+ [IMX8MM_POWER_DOMAIN_GPUMIX] = {
+ .genpd = {
+ .name = "gpumix",
+ },
+ .bits = {
+ .pxx = IMX8MM_GPUMIX_SW_Pxx_REQ,
+ .map = IMX8MM_GPUMIX_A53_DOMAIN,
+ .hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN,
+ .hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MM_PGC_GPUMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MM_POWER_DOMAIN_GPU] = {
+ .genpd = {
+ .name = "gpu",
+ },
+ .bits = {
+ .pxx = IMX8MM_GPU_SW_Pxx_REQ,
+ .map = IMX8MM_GPU_A53_DOMAIN,
+ .hskreq = IMX8MM_GPU_HSK_PWRDNREQN,
+ .hskack = IMX8MM_GPU_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D),
+ },
+
+ [IMX8MM_POWER_DOMAIN_VPUMIX] = {
+ .genpd = {
+ .name = "vpumix",
+ },
+ .bits = {
+ .pxx = IMX8MM_VPUMIX_SW_Pxx_REQ,
+ .map = IMX8MM_VPUMIX_A53_DOMAIN,
+ .hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN,
+ .hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MM_PGC_VPUMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MM_POWER_DOMAIN_VPUG1] = {
+ .genpd = {
+ .name = "vpu-g1",
+ },
+ .bits = {
+ .pxx = IMX8MM_VPUG1_SW_Pxx_REQ,
+ .map = IMX8MM_VPUG1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_VPUG1),
+ },
+
+ [IMX8MM_POWER_DOMAIN_VPUG2] = {
+ .genpd = {
+ .name = "vpu-g2",
+ },
+ .bits = {
+ .pxx = IMX8MM_VPUG2_SW_Pxx_REQ,
+ .map = IMX8MM_VPUG2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_VPUG2),
+ },
+
+ [IMX8MM_POWER_DOMAIN_VPUH1] = {
+ .genpd = {
+ .name = "vpu-h1",
+ },
+ .bits = {
+ .pxx = IMX8MM_VPUH1_SW_Pxx_REQ,
+ .map = IMX8MM_VPUH1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_VPUH1),
+ .keep_clocks = true,
+ },
+
+ [IMX8MM_POWER_DOMAIN_DISPMIX] = {
+ .genpd = {
+ .name = "dispmix",
+ },
+ .bits = {
+ .pxx = IMX8MM_DISPMIX_SW_Pxx_REQ,
+ .map = IMX8MM_DISPMIX_A53_DOMAIN,
+ .hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN,
+ .hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MM_PGC_DISPMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MM_POWER_DOMAIN_MIPI] = {
+ .genpd = {
+ .name = "mipi",
+ },
+ .bits = {
+ .pxx = IMX8MM_MIPI_SW_Pxx_REQ,
+ .map = IMX8MM_MIPI_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MM_PGC_MIPI),
+ },
+};
+
+static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = {
+ .domains = imx8mm_pgc_domains,
+ .domains_num = ARRAY_SIZE(imx8mm_pgc_domains),
+ .pgc_regs = &imx7_pgc_regs,
+};
+
+static const struct imx_pgc_domain imx8mp_pgc_domains[] = {
+ [IMX8MP_POWER_DOMAIN_MIPI_PHY1] = {
+ .genpd = {
+ .name = "mipi-phy1",
+ },
+ .bits = {
+ .pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ,
+ .map = IMX8MP_MIPI_PHY1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_MIPI1),
+ },
+
+ [IMX8MP_POWER_DOMAIN_PCIE_PHY] = {
+ .genpd = {
+ .name = "pcie-phy1",
+ },
+ .bits = {
+ .pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ,
+ .map = IMX8MP_PCIE_PHY_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_PCIE),
+ },
+
+ [IMX8MP_POWER_DOMAIN_USB1_PHY] = {
+ .genpd = {
+ .name = "usb-otg1",
+ },
+ .bits = {
+ .pxx = IMX8MP_USB1_PHY_Pxx_REQ,
+ .map = IMX8MP_USB1_PHY_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_USB1),
+ },
+
+ [IMX8MP_POWER_DOMAIN_USB2_PHY] = {
+ .genpd = {
+ .name = "usb-otg2",
+ },
+ .bits = {
+ .pxx = IMX8MP_USB2_PHY_Pxx_REQ,
+ .map = IMX8MP_USB2_PHY_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_USB2),
+ },
+
+ [IMX8MP_POWER_DOMAIN_MLMIX] = {
+ .genpd = {
+ .name = "mlmix",
+ },
+ .bits = {
+ .pxx = IMX8MP_MLMIX_Pxx_REQ,
+ .map = IMX8MP_MLMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_MLMIX_PWRDNREQN,
+ .hskack = IMX8MP_MLMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_MLMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_AUDIOMIX] = {
+ .genpd = {
+ .name = "audiomix",
+ },
+ .bits = {
+ .pxx = IMX8MP_AUDIOMIX_Pxx_REQ,
+ .map = IMX8MP_AUDIOMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_AUDIOMIX_PWRDNREQN,
+ .hskack = IMX8MP_AUDIOMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_AUDIOMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_GPU2D] = {
+ .genpd = {
+ .name = "gpu2d",
+ },
+ .bits = {
+ .pxx = IMX8MP_GPU_2D_Pxx_REQ,
+ .map = IMX8MP_GPU2D_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_GPU2D),
+ },
+
+ [IMX8MP_POWER_DOMAIN_GPUMIX] = {
+ .genpd = {
+ .name = "gpumix",
+ },
+ .bits = {
+ .pxx = IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ,
+ .map = IMX8MP_GPUMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_GPUMIX_PWRDNREQN,
+ .hskack = IMX8MP_GPUMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_GPUMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_VPUMIX] = {
+ .genpd = {
+ .name = "vpumix",
+ },
+ .bits = {
+ .pxx = IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ,
+ .map = IMX8MP_VPUMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_VPUMIX_PWRDNREQN,
+ .hskack = IMX8MP_VPUMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_VPUMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_GPU3D] = {
+ .genpd = {
+ .name = "gpu3d",
+ },
+ .bits = {
+ .pxx = IMX8MP_GPU_3D_Pxx_REQ,
+ .map = IMX8MP_GPU3D_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_GPU3D),
+ },
+
+ [IMX8MP_POWER_DOMAIN_MEDIAMIX] = {
+ .genpd = {
+ .name = "mediamix",
+ },
+ .bits = {
+ .pxx = IMX8MP_MEDIMIX_Pxx_REQ,
+ .map = IMX8MP_MEDIAMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN,
+ .hskack = IMX8MP_MEDIAMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_MEDIAMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_VPU_G1] = {
+ .genpd = {
+ .name = "vpu-g1",
+ },
+ .bits = {
+ .pxx = IMX8MP_VPU_G1_Pxx_REQ,
+ .map = IMX8MP_VPU_G1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_VPU_G1),
+ },
+
+ [IMX8MP_POWER_DOMAIN_VPU_G2] = {
+ .genpd = {
+ .name = "vpu-g2",
+ },
+ .bits = {
+ .pxx = IMX8MP_VPU_G2_Pxx_REQ,
+ .map = IMX8MP_VPU_G2_A53_DOMAIN
+ },
+ .pgc = BIT(IMX8MP_PGC_VPU_G2),
+ },
+
+ [IMX8MP_POWER_DOMAIN_VPU_VC8000E] = {
+ .genpd = {
+ .name = "vpu-h1",
+ },
+ .bits = {
+ .pxx = IMX8MP_VPU_VC8K_Pxx_REQ,
+ .map = IMX8MP_VPU_VC8000E_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_VPU_VC8000E),
+ },
+
+ [IMX8MP_POWER_DOMAIN_HDMIMIX] = {
+ .genpd = {
+ .name = "hdmimix",
+ },
+ .bits = {
+ .pxx = IMX8MP_HDMIMIX_Pxx_REQ,
+ .map = IMX8MP_HDMIMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_HDMIMIX_PWRDNREQN,
+ .hskack = IMX8MP_HDMIMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_HDMIMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_HDMI_PHY] = {
+ .genpd = {
+ .name = "hdmi-phy",
+ },
+ .bits = {
+ .pxx = IMX8MP_HDMI_PHY_Pxx_REQ,
+ .map = IMX8MP_HDMI_PHY_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_HDMI),
+ },
+
+ [IMX8MP_POWER_DOMAIN_MIPI_PHY2] = {
+ .genpd = {
+ .name = "mipi-phy2",
+ },
+ .bits = {
+ .pxx = IMX8MP_MIPI_PHY2_Pxx_REQ,
+ .map = IMX8MP_MIPI_PHY2_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_MIPI2),
+ },
+
+ [IMX8MP_POWER_DOMAIN_HSIOMIX] = {
+ .genpd = {
+ .name = "hsiomix",
+ },
+ .bits = {
+ .pxx = IMX8MP_HSIOMIX_Pxx_REQ,
+ .map = IMX8MP_HSIOMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_HSIOMIX_PWRDNREQN,
+ .hskack = IMX8MP_HSIOMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_HSIOMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MP_POWER_DOMAIN_MEDIAMIX_ISPDWP] = {
+ .genpd = {
+ .name = "mediamix-isp-dwp",
+ },
+ .bits = {
+ .pxx = IMX8MP_MEDIA_ISP_DWP_Pxx_REQ,
+ .map = IMX8MP_MEDIA_ISPDWP_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MP_PGC_MEDIA_ISP_DWP),
+ },
+};
+
+static const struct imx_pgc_regs imx8mp_pgc_regs = {
+ .map = IMX8MP_GPC_PGC_CPU_MAPPING,
+ .pup = IMX8MP_GPC_PU_PGC_SW_PUP_REQ,
+ .pdn = IMX8MP_GPC_PU_PGC_SW_PDN_REQ,
+ .hsk = IMX8MP_GPC_PU_PWRHSK,
+};
+static const struct imx_pgc_domain_data imx8mp_pgc_domain_data = {
+ .domains = imx8mp_pgc_domains,
+ .domains_num = ARRAY_SIZE(imx8mp_pgc_domains),
+ .pgc_regs = &imx8mp_pgc_regs,
+};
+
+static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
+ [IMX8MN_POWER_DOMAIN_HSIOMIX] = {
+ .genpd = {
+ .name = "hsiomix",
+ },
+ .bits = {
+ .pxx = 0, /* no power sequence control */
+ .map = 0, /* no power sequence control */
+ .hskreq = IMX8MN_HSIO_HSK_PWRDNREQN,
+ .hskack = IMX8MN_HSIO_HSK_PWRDNACKN,
+ },
+ .keep_clocks = true,
+ },
+
+ [IMX8MN_POWER_DOMAIN_OTG1] = {
+ .genpd = {
+ .name = "usb-otg1",
+ },
+ .bits = {
+ .pxx = IMX8MN_OTG1_SW_Pxx_REQ,
+ .map = IMX8MN_OTG1_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MN_PGC_OTG1),
+ },
+
+ [IMX8MN_POWER_DOMAIN_GPUMIX] = {
+ .genpd = {
+ .name = "gpumix",
+ },
+ .bits = {
+ .pxx = IMX8MN_GPUMIX_SW_Pxx_REQ,
+ .map = IMX8MN_GPUMIX_A53_DOMAIN,
+ .hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN,
+ .hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MN_PGC_GPUMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MN_POWER_DOMAIN_DISPMIX] = {
+ .genpd = {
+ .name = "dispmix",
+ },
+ .bits = {
+ .pxx = IMX8MN_DISPMIX_SW_Pxx_REQ,
+ .map = IMX8MN_DISPMIX_A53_DOMAIN,
+ .hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN,
+ .hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MN_PGC_DISPMIX),
+ .keep_clocks = true,
+ },
+
+ [IMX8MN_POWER_DOMAIN_MIPI] = {
+ .genpd = {
+ .name = "mipi",
+ },
+ .bits = {
+ .pxx = IMX8MN_MIPI_SW_Pxx_REQ,
+ .map = IMX8MN_MIPI_A53_DOMAIN,
+ },
+ .pgc = BIT(IMX8MN_PGC_MIPI),
+ },
+};
+
+static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = {
+ .domains = imx8mn_pgc_domains,
+ .domains_num = ARRAY_SIZE(imx8mn_pgc_domains),
+ .pgc_regs = &imx7_pgc_regs,
+};
+
+static int imx_pgc_domain_probe(struct device *dev)
+{
+ struct imx_pgc_domain *domain = dev->priv;
+ int ret;
+
+ domain->dev = dev;
+
+ domain->regulator = regulator_get(domain->dev, "power");
+ if (IS_ERR(domain->regulator)) {
+ /* 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);
+ }
+
+ domain->num_clks = clk_bulk_get_all(domain->dev, &domain->clks);
+ if (domain->num_clks < 0)
+ return dev_err_probe(domain->dev, domain->num_clks,
+ "Failed to get domain's clocks\n");
+
+ /* There are no power domains yet with multiple resets */
+ if (reset_control_get_count(domain->dev) > 1)
+ return dev_err_probe(domain->dev, -ENOSYS,
+ "driver can't handle multiple resets yet\n");
+
+ domain->reset = reset_control_get_optional(domain->dev, NULL);
+ if (IS_ERR(domain->reset))
+ return dev_err_probe(domain->dev, PTR_ERR(domain->reset),
+ "Failed to get domain's resets\n");
+
+ if (domain->bits.map)
+ regmap_update_bits(domain->regmap, domain->regs->map,
+ domain->bits.map, domain->bits.map);
+
+ ret = pm_genpd_init(&domain->genpd, NULL, true);
+ if (ret) {
+ dev_err(domain->dev, "Failed to init power domain\n");
+ goto out_domain_unmap;
+ }
+
+ ret = of_genpd_add_provider_simple(domain->dev->of_node,
+ &domain->genpd);
+ if (ret) {
+ dev_err(domain->dev, "Failed to add genpd provider\n");
+ goto out_domain_unmap;
+ }
+
+ return 0;
+
+out_domain_unmap:
+ if (domain->bits.map)
+ regmap_update_bits(domain->regmap, domain->regs->map,
+ domain->bits.map, 0);
+ return ret;
+}
+
+static const struct platform_device_id imx_pgc_domain_id[] = {
+ { "imx-pgc-domain", },
+ { },
+};
+
+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 *dev)
+{
+ const struct imx_pgc_domain_data *domain_data =
+ of_device_get_match_data(dev);
+
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SZ_4K,
+ };
+ struct device_node *pgc_np, *np;
+ struct resource *res;
+ struct regmap *regmap;
+ int ret, pass = 0;
+
+ 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;
+ }
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ regmap = regmap_init_mmio(dev, IOMEM(res->start), &regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "failed to init regmap\n");
+
+ /*
+ * Run two passes for the registration of the PGC domain platform
+ * devices: first all devices that are not part of a power-domain
+ * themselves, then all the others. This avoids -EPROBE_DEFER being
+ * returned for nested domains, that need their parent PGC domains
+ * to be present on probe.
+ */
+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 *pd_dev;
+ u32 domain_index;
+
+ if ((pass == 0 && child_domain) || (pass == 1 && !child_domain))
+ continue;
+
+ if (!of_device_is_available(np))
+ continue;
+
+ ret = of_property_read_u32(np, "reg", &domain_index);
+ if (ret) {
+ dev_err(dev, "Failed to read 'reg' property\n");
+ return ret;
+ }
+
+ if (domain_index >= domain_data->domains_num) {
+ dev_warn(dev,
+ "Domain index %d is out of bounds\n",
+ domain_index);
+ continue;
+ }
+
+ domain = xmemdup(&domain_data->domains[domain_index],
+ sizeof(domain_data->domains[domain_index]));
+ domain->regmap = regmap;
+ domain->genpd.power_on = imx_pgc_power_up;
+ domain->regs = domain_data->pgc_regs;
+ domain->genpd.power_off = imx_pgc_power_down;
+
+ pd_dev = xzalloc(sizeof(*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;
+ dev_set_name(pd_dev, imx_pgc_domain_id[0].name);
+
+ ret = platform_device_register(pd_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (pass == 0) {
+ pass++;
+ goto again;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id imx_gpcv2_dt_ids[] = {
+ { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, },
+ { .compatible = "fsl,imx8mm-gpc", .data = &imx8mm_pgc_domain_data, },
+ { .compatible = "fsl,imx8mn-gpc", .data = &imx8mn_pgc_domain_data, },
+ { .compatible = "fsl,imx8mp-gpc", .data = &imx8mp_pgc_domain_data, },
+ { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, imx_gpcv2_dt_ids);
+
+static struct driver imx_gpcv2_driver = {
+ .name = "imx-gpcv2",
+ .probe = imx_gpcv2_probe,
+ .of_compatible = DRV_OF_COMPAT(imx_gpcv2_dt_ids),
+};
+coredevice_platform_driver(imx_gpcv2_driver);
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 1316af4213..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)
{
@@ -48,25 +48,12 @@ static int reboot_mode_add_param(struct device_d *dev,
return PTR_ERR_OR_ZERO(param);
}
-static struct device_node *of_get_node_by_reproducible_name(struct device_node *dstroot,
- struct device_node *srcnp)
-{
- struct device_node *dstnp;
- char *name;
-
- name = of_get_reproducible_name(srcnp);
- dstnp = of_find_node_by_reproducible_name(dstroot, name);
- free(name);
-
- return dstnp;
-}
-
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) {
@@ -133,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;
@@ -152,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/core.c b/drivers/pwm/core.c
index aba6c2a709..a3f27708e6 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;
@@ -82,7 +91,7 @@ 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 device *dev)
{
struct pwm_device *pwm;
struct param_d *p;
@@ -194,7 +203,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;
}
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 9ec81e18b1..d5e70600ee 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;
/*
@@ -410,10 +410,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 +439,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++;
@@ -462,7 +463,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..c9db4aef34 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.p_enable)
+ 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,6 +186,9 @@ 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)
@@ -200,8 +239,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 +254,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);
@@ -224,8 +268,8 @@ static int imx_pwm_probe(struct device_d *dev)
imx->mmio_base = IOMEM(iores->start);
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);
@@ -239,7 +283,7 @@ static int imx_pwm_probe(struct device_d *dev)
return pwmchip_add(&imx->chip, dev);;
}
-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..6b89ac192a 100644
--- a/drivers/pwm/pwm-mxs.c
+++ b/drivers/pwm/pwm-mxs.c
@@ -93,10 +93,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;
@@ -146,8 +146,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-stm32.c b/drivers/pwm/pwm-stm32.c
index 4e1a65207e..5c2029ab6a 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>
@@ -335,9 +336,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 +363,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++;
@@ -391,8 +392,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..0ed69d999f 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>
@@ -128,7 +128,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;
@@ -146,7 +146,7 @@ static int pxa_pwm_probe(struct device_d *dev)
return pwmchip_add(&chip->chip, dev);
}
-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/Kconfig b/drivers/reset/Kconfig
index 913b309eac..16c05d50f0 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -41,6 +41,7 @@ config RESET_IMX7
config RESET_STARFIVE
bool "StarFive Controller Driver" if COMPILE_TEST
+ depends on COMMON_CLK
default SOC_STARFIVE
help
This enables the reset controller driver for the StarFive JH7100.
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 fde8f718dd..f82b5797ce 100644
--- a/drivers/serial/serial_ns16550_pci.c
+++ b/drivers/serial/serial_ns16550_pci.c
@@ -1203,7 +1203,7 @@ static int pci_quatech_init(struct pci_dev *dev)
outl(inl(base + 0x38) | 0x00002000, base + 0x38);
tmp = inl(base + 0x3c);
outl(tmp | 0x01000000, base + 0x3c);
- outl(tmp &= ~0x01000000, base + 0x3c);
+ outl(tmp & ~0x01000000, base + 0x3c);
}
}
return 0;
@@ -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 742d13c4f5..333a134d3f 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -1,10 +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_IMX8MQ
- select PM_GENERIC_DOMAINS
- default y if ARCH_IMX7 || ARCH_IMX8MQ
+config IMX8M_FEATCTRL
+ 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 3a8a8d0b00..65b2677f7a 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -1,2 +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/gpcv2.c b/drivers/soc/imx/gpcv2.c
deleted file mode 100644
index a0e78ce55e..0000000000
--- a/drivers/soc/imx/gpcv2.c
+++ /dev/null
@@ -1,516 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2017 Impinj, Inc
- * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
- *
- * Based on the code of analogus driver:
- *
- * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
- */
-
-#include <of_device.h>
-#include <common.h>
-#include <clock.h>
-#include <abort.h>
-#include <malloc.h>
-#include <io.h>
-#include <init.h>
-#include <linux/iopoll.h>
-
-#include <pm_domain.h>
-#include <regulator.h>
-#include <dt-bindings/power/imx7-power.h>
-
-#include <dt-bindings/power/imx8mq-power.h>
-
-#define GPC_LPCR_A_BSC 0x000
-
-#define GPC_PGC_CPU_MAPPING 0x0ec
-
-#define IMX7_USB_HSIC_PHY_A_DOMAIN BIT(6)
-#define IMX7_USB_OTG2_PHY_A_DOMAIN BIT(5)
-#define IMX7_USB_OTG1_PHY_A_DOMAIN BIT(4)
-#define IMX7_PCIE_PHY_A_DOMAIN BIT(3)
-#define IMX7_MIPI_PHY_A_DOMAIN BIT(2)
-
-#define IMX8M_PCIE2_A53_DOMAIN BIT(15)
-#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14)
-#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13)
-#define IMX8M_DISP_A53_DOMAIN BIT(12)
-#define IMX8M_HDMI_A53_DOMAIN BIT(11)
-#define IMX8M_VPU_A53_DOMAIN BIT(10)
-#define IMX8M_GPU_A53_DOMAIN BIT(9)
-#define IMX8M_DDR2_A53_DOMAIN BIT(8)
-#define IMX8M_DDR1_A53_DOMAIN BIT(7)
-#define IMX8M_OTG2_A53_DOMAIN BIT(5)
-#define IMX8M_OTG1_A53_DOMAIN BIT(4)
-#define IMX8M_PCIE1_A53_DOMAIN BIT(3)
-#define IMX8M_MIPI_A53_DOMAIN BIT(2)
-
-#define GPC_PU_PGC_SW_PUP_REQ 0x0f8
-#define GPC_PU_PGC_SW_PDN_REQ 0x104
-
-#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4)
-#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3)
-#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2)
-#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1)
-#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0)
-
-#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13)
-#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12)
-#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11)
-#define IMX8M_DISP_SW_Pxx_REQ BIT(10)
-#define IMX8M_HDMI_SW_Pxx_REQ BIT(9)
-#define IMX8M_VPU_SW_Pxx_REQ BIT(8)
-#define IMX8M_GPU_SW_Pxx_REQ BIT(7)
-#define IMX8M_DDR2_SW_Pxx_REQ BIT(6)
-#define IMX8M_DDR1_SW_Pxx_REQ BIT(5)
-#define IMX8M_OTG2_SW_Pxx_REQ BIT(3)
-#define IMX8M_OTG1_SW_Pxx_REQ BIT(2)
-#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1)
-#define IMX8M_MIPI_SW_Pxx_REQ BIT(0)
-
-#define GPC_M4_PU_PDN_FLG 0x1bc
-
-/*
- * The PGC offset values in Reference Manual
- * (Rev. 1, 01/2018 and the older ones) GPC chapter's
- * GPC_PGC memory map are incorrect, below offset
- * values are from design RTL.
- */
-#define IMX7_PGC_MIPI 16
-#define IMX7_PGC_PCIE 17
-#define IMX7_PGC_USB_HSIC 20
-
-
-#define IMX8M_PGC_MIPI 16
-#define IMX8M_PGC_PCIE1 17
-#define IMX8M_PGC_OTG1 18
-#define IMX8M_PGC_OTG2 19
-#define IMX8M_PGC_DDR1 21
-#define IMX8M_PGC_GPU 23
-#define IMX8M_PGC_VPU 24
-#define IMX8M_PGC_DISP 26
-#define IMX8M_PGC_MIPI_CSI1 27
-#define IMX8M_PGC_MIPI_CSI2 28
-#define IMX8M_PGC_PCIE2 29
-
-#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40)
-#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc)
-
-#define GPC_PGC_CTRL_PCR BIT(0)
-
-struct imx_pgc_domain {
- struct generic_pm_domain genpd;
- void __iomem *base;
- struct regulator *regulator;
-
- unsigned int pgc;
-
- const struct {
- u32 pxx;
- u32 map;
- } bits;
-
- const int voltage;
- struct device_d *dev;
-};
-
-struct imx_pgc_domain_data {
- const struct imx_pgc_domain *domains;
- size_t domains_num;
-};
-
-static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
- bool on)
-{
- struct imx_pgc_domain *domain = container_of(genpd,
- struct imx_pgc_domain,
- genpd);
- unsigned int offset = on ?
- GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ;
- const bool enable_power_control = !on;
- const bool has_regulator = !IS_ERR(domain->regulator);
- int ret = 0;
- unsigned int mapping, ctrl = 0, pxx;
-
- mapping = readl(domain->base + GPC_PGC_CPU_MAPPING);
- mapping |= domain->bits.map;
- writel(mapping, domain->base + GPC_PGC_CPU_MAPPING);
-
- if (has_regulator && on) {
- ret = regulator_enable(domain->regulator);
- if (ret) {
- dev_err(domain->dev, "failed to enable regulator\n");
- goto unmap;
- }
- }
-
- if (enable_power_control) {
- ctrl = readl(domain->base + GPC_PGC_CTRL(domain->pgc));
- ctrl |= GPC_PGC_CTRL_PCR;
- writel(ctrl, domain->base + GPC_PGC_CTRL(domain->pgc));
- }
-
- pxx = readl(domain->base + offset);
- pxx |= domain->bits.pxx;
- writel(pxx, domain->base + offset);
-
- /*
- * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
- * for PUP_REQ/PDN_REQ bit to be cleared
- */
- ret = readl_poll_timeout(domain->base + offset, pxx,
- !(pxx & domain->bits.pxx), MSECOND);
- if (ret < 0) {
- dev_err(domain->dev, "falied to command PGC\n");
- /*
- * If we were in a process of enabling a
- * domain and failed we might as well disable
- * the regulator we just enabled. And if it
- * was the opposite situation and we failed to
- * power down -- keep the regulator on
- */
- on = !on;
- }
-
- if (enable_power_control) {
- ctrl &= ~GPC_PGC_CTRL_PCR;
- writel(ctrl, domain->base + GPC_PGC_CTRL(domain->pgc));
- }
-
- if (has_regulator && !on) {
- int err;
-
- err = regulator_disable(domain->regulator);
- if (err)
- dev_err(domain->dev,
- "failed to disable regulator: %d\n", ret);
- /* Preserve earlier error code */
- ret = ret ?: err;
- }
-unmap:
- mapping &= ~domain->bits.map;
- writel(mapping, domain->base + GPC_PGC_CPU_MAPPING);
-
- return ret;
-}
-
-static int imx_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd)
-{
- return imx_gpc_pu_pgc_sw_pxx_req(genpd, true);
-}
-
-static int imx_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd)
-{
- return imx_gpc_pu_pgc_sw_pxx_req(genpd, false);
-}
-
-static const struct imx_pgc_domain imx7_pgc_domains[] = {
- [IMX7_POWER_DOMAIN_MIPI_PHY] = {
- .genpd = {
- .name = "mipi-phy",
- },
- .bits = {
- .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ,
- .map = IMX7_MIPI_PHY_A_DOMAIN,
- },
- .voltage = 1000000,
- .pgc = IMX7_PGC_MIPI,
- },
-
- [IMX7_POWER_DOMAIN_PCIE_PHY] = {
- .genpd = {
- .name = "pcie-phy",
- },
- .bits = {
- .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ,
- .map = IMX7_PCIE_PHY_A_DOMAIN,
- },
- .voltage = 1000000,
- .pgc = IMX7_PGC_PCIE,
- },
-
- [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
- .genpd = {
- .name = "usb-hsic-phy",
- },
- .bits = {
- .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ,
- .map = IMX7_USB_HSIC_PHY_A_DOMAIN,
- },
- .voltage = 1200000,
- .pgc = IMX7_PGC_USB_HSIC,
- },
-};
-
-static const struct imx_pgc_domain_data imx7_pgc_domain_data = {
- .domains = imx7_pgc_domains,
- .domains_num = ARRAY_SIZE(imx7_pgc_domains),
-};
-
-static const struct imx_pgc_domain imx8m_pgc_domains[] = {
- [IMX8M_POWER_DOMAIN_MIPI] = {
- .genpd = {
- .name = "mipi",
- },
- .bits = {
- .pxx = IMX8M_MIPI_SW_Pxx_REQ,
- .map = IMX8M_MIPI_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_MIPI,
- },
-
- [IMX8M_POWER_DOMAIN_PCIE1] = {
- .genpd = {
- .name = "pcie1",
- },
- .bits = {
- .pxx = IMX8M_PCIE1_SW_Pxx_REQ,
- .map = IMX8M_PCIE1_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_PCIE1,
- },
-
- [IMX8M_POWER_DOMAIN_USB_OTG1] = {
- .genpd = {
- .name = "usb-otg1",
- },
- .bits = {
- .pxx = IMX8M_OTG1_SW_Pxx_REQ,
- .map = IMX8M_OTG1_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_OTG1,
- },
-
- [IMX8M_POWER_DOMAIN_USB_OTG2] = {
- .genpd = {
- .name = "usb-otg2",
- },
- .bits = {
- .pxx = IMX8M_OTG2_SW_Pxx_REQ,
- .map = IMX8M_OTG2_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_OTG2,
- },
-
- [IMX8M_POWER_DOMAIN_DDR1] = {
- .genpd = {
- .name = "ddr1",
- },
- .bits = {
- .pxx = IMX8M_DDR1_SW_Pxx_REQ,
- .map = IMX8M_DDR2_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_DDR1,
- },
-
- [IMX8M_POWER_DOMAIN_GPU] = {
- .genpd = {
- .name = "gpu",
- },
- .bits = {
- .pxx = IMX8M_GPU_SW_Pxx_REQ,
- .map = IMX8M_GPU_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_GPU,
- },
-
- [IMX8M_POWER_DOMAIN_VPU] = {
- .genpd = {
- .name = "vpu",
- },
- .bits = {
- .pxx = IMX8M_VPU_SW_Pxx_REQ,
- .map = IMX8M_VPU_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_VPU,
- },
-
- [IMX8M_POWER_DOMAIN_DISP] = {
- .genpd = {
- .name = "disp",
- },
- .bits = {
- .pxx = IMX8M_DISP_SW_Pxx_REQ,
- .map = IMX8M_DISP_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_DISP,
- },
-
- [IMX8M_POWER_DOMAIN_MIPI_CSI1] = {
- .genpd = {
- .name = "mipi-csi1",
- },
- .bits = {
- .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ,
- .map = IMX8M_MIPI_CSI1_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_MIPI_CSI1,
- },
-
- [IMX8M_POWER_DOMAIN_MIPI_CSI2] = {
- .genpd = {
- .name = "mipi-csi2",
- },
- .bits = {
- .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ,
- .map = IMX8M_MIPI_CSI2_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_MIPI_CSI2,
- },
-
- [IMX8M_POWER_DOMAIN_PCIE2] = {
- .genpd = {
- .name = "pcie2",
- },
- .bits = {
- .pxx = IMX8M_PCIE2_SW_Pxx_REQ,
- .map = IMX8M_PCIE2_A53_DOMAIN,
- },
- .pgc = IMX8M_PGC_PCIE2,
- },
-};
-
-static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
- .domains = imx8m_pgc_domains,
- .domains_num = ARRAY_SIZE(imx8m_pgc_domains),
-};
-
-static int imx_pgc_domain_probe(struct device_d *dev)
-{
- struct imx_pgc_domain *domain = dev->priv;
- int ret;
-
- domain->dev = dev;
-
- domain->regulator = regulator_get(domain->dev, "power");
- if (IS_ERR(domain->regulator)) {
- if (PTR_ERR(domain->regulator) != -ENODEV) {
- if (PTR_ERR(domain->regulator) != -EPROBE_DEFER)
- dev_err(domain->dev, "Failed to get domain's regulator\n");
- return PTR_ERR(domain->regulator);
- }
- } else {
- regulator_set_voltage(domain->regulator,
- domain->voltage, domain->voltage);
- }
-
- ret = pm_genpd_init(&domain->genpd, NULL, true);
- if (ret) {
- dev_err(domain->dev, "Failed to init power domain\n");
- return ret;
- }
-
- ret = of_genpd_add_provider_simple(domain->dev->device_node,
- &domain->genpd);
- if (ret) {
- dev_err(domain->dev, "Failed to add genpd provider\n");
- }
-
- return ret;
-}
-
-static const struct platform_device_id imx_pgc_domain_id[] = {
- { "imx-pgc-domain", },
- { },
-};
-
-static struct driver_d 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 const struct imx_pgc_domain_data *domain_data;
- struct device_node *pgc_np, *np;
- struct resource *res;
- void __iomem *base;
- int ret, pass = 0;
-
- pgc_np = of_get_child_by_name(dev->device_node, "pgc");
- if (!pgc_np) {
- dev_err(dev, "No power domains specified in DT\n");
- return -EINVAL;
- }
-
- res = dev_request_mem_resource(dev, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
-
- base = IOMEM(res->start);
-
- domain_data = of_device_get_match_data(dev);
-
- /*
- * Run two passes for the registration of the PGC domain platform
- * devices: first all devices that are not part of a power-domain
- * themselves, then all the others. This avoids -EPROBE_DEFER being
- * returned for nested domains, that need their parent PGC domains
- * to be present on probe.
- */
-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;
- u32 domain_index;
-
- if ((pass == 0 && child_domain) || (pass == 1 && !child_domain))
- continue;
-
- ret = of_property_read_u32(np, "reg", &domain_index);
- if (ret) {
- dev_err(dev, "Failed to read 'reg' property\n");
- return ret;
- }
-
- if (domain_index >= domain_data->domains_num) {
- dev_warn(dev,
- "Domain index %d is out of bounds\n",
- domain_index);
- continue;
- }
-
- domain = xmemdup(&domain_data->domains[domain_index],
- sizeof(domain_data->domains[domain_index]));
- domain->base = base;
- domain->genpd.power_on = imx_gpc_pu_pgc_sw_pup_req;
- domain->genpd.power_off = imx_gpc_pu_pgc_sw_pdn_req;
-
- pd_dev = xzalloc(sizeof(*pd_dev));
- pd_dev->device_node = np;
- pd_dev->device_node->dev = pd_dev;
- pd_dev->id = domain_index;
- pd_dev->parent = dev;
- pd_dev->priv = domain;
- dev_set_name(pd_dev, imx_pgc_domain_id[0].name);
-
- ret = platform_device_register(pd_dev);
- if (ret)
- return ret;
- }
-
- if (pass == 0) {
- pass++;
- goto again;
- }
-
- return 0;
-}
-
-static const struct of_device_id imx_gpcv2_dt_ids[] = {
- { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data },
- { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, },
- { }
-};
-
-static struct driver_d imx_gpcv2_driver = {
- .name = "imx7d-gpc",
- .probe = imx_gpcv2_probe,
- .of_compatible = DRV_OF_COMPAT(imx_gpcv2_dt_ids),
-};
-coredevice_platform_driver(imx_gpcv2_driver);
diff --git a/drivers/soc/imx/imx8m-featctrl.c b/drivers/soc/imx/imx8m-featctrl.c
new file mode 100644
index 0000000000..31579aff7e
--- /dev/null
+++ b/drivers/soc/imx/imx8m-featctrl.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2022 Ahmad Fatoum, Pengutronix
+
+#include <common.h>
+#include <linux/bitmap.h>
+#include <featctrl.h>
+#include <soc/imx8m/featctrl.h>
+
+#include <dt-bindings/features/imx8m.h>
+
+struct imx_feat {
+ struct feature_controller feat;
+ unsigned long features[BITS_TO_LONGS(IMX8M_FEAT_END)];
+};
+
+static struct imx_feat *to_imx_feat(struct feature_controller *feat)
+{
+ return container_of(feat, struct imx_feat, feat);
+}
+
+static int imx8m_feat_check(struct feature_controller *feat, int idx)
+{
+ struct imx_feat *priv = to_imx_feat(feat);
+
+ if (idx > IMX8M_FEAT_END)
+ return -EINVAL;
+
+ return test_bit(idx, priv->features) ? FEATCTRL_OKAY : FEATCTRL_GATED;
+}
+
+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;
+ struct imx_feat *priv;
+
+ if (!dev || !data)
+ return -ENODEV;
+
+ 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 (is_fused(tester3, data->tester3.vpu_bitmask) ||
+ is_fused(tester4, data->tester4.vpu_bitmask))
+ clear_bit(IMX8M_FEAT_VPU, features);
+ if (is_fused(tester4, data->tester4.gpu_bitmask))
+ clear_bit(IMX8M_FEAT_GPU, 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;
+
+ return feature_controller_register(&priv->feat);
+}
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/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..21e57d4b07 100644
--- a/drivers/sound/pwm-beeper.c
+++ b/drivers/sound/pwm-beeper.c
@@ -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,14 +60,9 @@ 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);
@@ -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..650af0e884 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"
@@ -660,7 +660,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 8be5c91f98..b198ba6bf8 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -61,6 +61,9 @@ static void dwc2_set_param_phy_type(struct dwc2 *dwc2)
case GHWCFG2_HS_PHY_TYPE_ULPI:
val = DWC2_PHY_TYPE_PARAM_ULPI;
break;
+ default:
+ dwc2_warn(dwc2, "Unhandled HS PHY type\n");
+ fallthrough;
case GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED:
val = DWC2_PHY_TYPE_PARAM_FS;
break;
@@ -185,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;
@@ -642,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..344f058be2 100644
--- a/drivers/usb/dwc2/host.c
+++ b/drivers/usb/dwc2/host.c
@@ -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 30aaef90ac..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,39 +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->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)) {
@@ -1045,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);
@@ -1136,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);
@@ -1170,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 0fbe941b12..0000000000
--- a/drivers/usb/gadget/fsl_udc_pbl.c
+++ /dev/null
@@ -1,210 +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>
-
-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 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..4628b36b17 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>
@@ -427,7 +427,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 +485,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 +844,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..41450268fc 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;
@@ -183,6 +209,17 @@ static struct usb_request *fastboot_alloc_request(struct usb_ep *ep)
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 +285,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 +298,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 +319,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 +426,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 129b1cf5e1..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,21 +42,49 @@ static const char *otg_mode_names[] = {
[USB_DR_MODE_OTG] = "otg",
};
-static struct device_d otg_device = {
- .name = "otg",
- .id = DEVICE_ID_SINGLE,
+static int register_otg_device(struct device *dev, struct otg_mode *otg)
+{
+ struct param_d *param_mode;
+ int ret;
+
+ ret = register_device(dev);
+ if (ret)
+ return ret;
+
+ param_mode = dev_add_param_enum(dev, "mode",
+ otg_set_mode, NULL, &otg->var_mode,
+ otg_mode_names, ARRAY_SIZE(otg_mode_names), otg);
+
+ return PTR_ERR_OR_ZERO(param_mode);
+}
+
+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 param_d *param_mode;
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");
@@ -65,25 +93,19 @@ int usb_register_otg_device(struct device_d *parent,
otg->set_mode_callback = set_mode;
otg->ctx = ctx;
- /* register otg.mode as an alias of otg0.mode */
- if (otg_device.parent == NULL) {
- otg_device.parent = parent;
- ret = register_device(&otg_device);
- if (ret)
- return ret;
-
- param_mode = dev_add_param_enum(&otg_device, "mode",
- otg_set_mode, NULL, &otg->var_mode,
- otg_mode_names, ARRAY_SIZE(otg_mode_names), otg);
- }
-
- ret = register_device(&otg->dev);
+ ret = register_otg_device(&otg->dev, otg);
if (ret)
return ret;
- param_mode = dev_add_param_enum(&otg->dev, "mode",
- otg_set_mode, NULL, &otg->var_mode,
- otg_mode_names, ARRAY_SIZE(otg_mode_names), otg);
+ /* register otg.mode as an alias of otg0.mode */
+ if (first_otg)
+ dev_add_alias(&otg->dev, "otg");
- return PTR_ERR_OR_ZERO(param_mode);
+ 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..2f8d6ecc72 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,221 @@ 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_alloc(info->screen_size);
+ memset(info->screen_base, 0, 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 +421,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 +463,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 +671,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;